gotta archive this lmao

This commit is contained in:
ForeverPyrite
2025-08-12 11:50:18 -05:00
parent 72c2126cd1
commit 32ac4622f9
11 changed files with 964 additions and 838 deletions

0
.dockerignore Normal file → Executable file
View File

0
.gitignore vendored Normal file → Executable file
View File

0
.vscode/launch.json vendored Normal file → Executable file
View File

0
Dockerfile Normal file → Executable file
View File

16
ai_logic.py Normal file → Executable file
View File

@@ -1,3 +1,4 @@
# ----- Configuration & Setup -----
import logging
import os
import asyncio
@@ -14,7 +15,6 @@ logger = logging.getLogger(__name__)
OPENAI_KEY = os.getenv('OPENAI_KEY')
# OpenAI & Assistant configuration
client: OpenAI = OpenAI(
api_key=OPENAI_KEY,
project="proj_vUQ7duhKKc1jxIqllRhy6DVJ",
@@ -25,6 +25,7 @@ base_instructions: str = mr_jacobs.instructions
logger.info(f"Base instructions: {base_instructions}")
instructions: str = base_instructions + f"\n\nHere is a dictionary containing some of your most well known quotes, organized by their frequency. DO NOT USE THE QUOTES FREQUENTLY! JUST UTILIZE THEM TO KNOW THE TYPES OF THINGS YOU SAY! \n{QUOTES}\n\n"
# ----- Helper Functions -----
def get_run_status(run: Run) -> str:
"""
Retrieves the status of a run.
@@ -71,6 +72,7 @@ def get_instructions(context: discord.abc.GuildChannel | discord.Message = None)
return tuned_instructions
# ----- Action Handlers -----
async def handle_action(run: Run) -> bool:
# Define the list to store tool outputs
tool_outputs = []
@@ -114,7 +116,16 @@ async def handle_action(run: Run) -> bool:
logger.warning("No tool outputs to submit.")
# ----- Run Handlers -----
async def handle_run(run: Run) -> bool:
"""Manages a run and all it's different possible states.
Args:
run (Run): The run to maintain.
Returns:
bool: Whether or not the run completed successfully.
"""
while True:
run = client.beta.threads.runs.retrieve(
run.id,
@@ -129,6 +140,9 @@ async def handle_run(run: Run) -> bool:
case "requires_action":
logger.info(f"Run {run.id} requires action.")
await handle_action(run)
case "expired":
logger.error(f"Run {run.id} expired.")
return False
case _:
await asyncio.sleep(1)
await asyncio.sleep(1)

2
app.py Normal file → Executable file
View File

@@ -30,7 +30,7 @@ async def on_ready() -> None:
print(f"{bot.user.name} is ready.")
# After successful initialization, schedule tasks.
task_scheduler = BackgroundScheduler(timezone=pytz.timezone('EDT'))
task_scheduler = BackgroundScheduler(timezone=pytz.timezone('EST'))
task_scheduler.add_job(lambda: bot.loop.create_task(send_quote()), 'cron', day_of_week='mon-fri', hour=8, minute=50)
task_scheduler.add_job(lambda: bot.loop.create_task(send_quote()), 'cron', day_of_week='mon-fri', hour='8-14', minute="*/20", jitter=180)
task_scheduler.add_job(lambda: bot.loop.create_task(after_class()), 'cron', day_of_week='mon-fri', hour=10, minute=55)

94
discord_logic.py Normal file → Executable file
View File

@@ -1,10 +1,13 @@
# ----- Imports & Setup -----
import logging
import regex as re
from random import randint
from asyncio import run as asyncio_run, sleep
from datetime import timedelta
import discord
from discord import Bot, Message
from discord.abc import GuildChannel as Channel
from discord.ext import commands
from ai_logic import run, get_instructions
from quotes import select_quote, select_student
@@ -17,7 +20,7 @@ GENERAL_ID: int = 1339601047294840876
bot = Bot(intents=discord.Intents.all())
# ---------- Discord Helper Functions ----------
# ----- Discord Helper Functions -----
async def get_channel(channel_id: int) -> Channel:
"""
Tries to get a cached channel object, if it fails it will send an API request to retrieve a new one.
@@ -124,7 +127,7 @@ async def edit_message(message: Message | int, channel: int = None):
else:
logger.error(f"Message couldn't be edited as it was not found.")
# ---------- Message Utility Functions ----------
# ----- Message Utility Functions -----
async def msg_is_reply(message: Message) -> tuple[bool, Message]:
"""
Checks if a message is a reply to another message.
@@ -242,7 +245,7 @@ async def gen_message_list(discord_messages: list[Message]) -> list[dict]:
return messages
# ---------- AI Interaction Functions ----------
# ----- AI Interaction Functions -----
async def send_quote(quote: str = None) -> None:
"""
Sends a quote to the general channel.
@@ -282,7 +285,7 @@ async def has_essay(message: Message) -> bool:
else:
return False
# ---------- Discord Commands & Event Handlers ----------
# ----- Discord Commands & Event Handlers -----
def setup_discord_bot(bot: Bot) -> None:
"""
Sets up the Discord bot with commands and event listeners.
@@ -306,6 +309,89 @@ def setup_discord_bot(bot: Bot) -> None:
response = await run(thread_messages, instructions, ctx.author.id)
await ctx.respond(content=response)
@bot.slash_command(name="clear_essay", description="Clear the essay assigned to a student")
async def clear_essay_command(ctx: discord.ApplicationContext, student: str) -> None:
"""
Slash command to clear the essay assigned to a student.
Args:
ctx (discord.ApplicationContext): The context of the command.
student (str): The ID of the student whose essay is to be cleared.
"""
try:
student = int(student)
except Exception as e:
logging.error(f"Failed to convert student ID to int: {e}")
await ctx.respond("Invalid student ID format.", ephemeral=True)
return
if not ctx.author.id == 620319269233885226:
await ctx.respond("You don't have permission to use this command.")
return
if student not in STUDENT_IDS:
await ctx.respond("Invalid student ID.", ephemeral=True)
return
ASSIGNED_ESSAY.pop(student, None)
await ctx.respond(f"Cleared essay for <@{student}>")
@bot.slash_command(name="get_essay", description="Get the essay assigned to a student, or all students")
async def get_essay_command(ctx: discord.ApplicationContext, student: str = None) -> None:
if student:
try:
student = int(student)
except Exception as e:
logging.error(f"Failed to convert student ID to int: {e}")
await ctx.respond("Invalid student ID format.", ephemeral=True)
return
await ctx.respond(get_essay(student))
else:
await ctx.respond(get_essay(), ephemeral=True)
@bot.slash_command(name="assign_essay", description="Assign an essay to a student")
@commands.has_permissions(moderate_members=True)
async def assign_essay_command(
ctx: discord.ApplicationContext,
student: str,
timeout: int | None = 0,
topic: str | None = None
) -> None:
"""
Slash command to assign an essay to a student.
Args:
ctx (discord.ApplicationContext): The context of the command.
student (int): The ID of the student to assign the essay to.
timeout (int | None, optional): The timeout in seconds before the essay is cleared. Defaults to 0.
topic (str | None, optional): The topic of the essay. If not provided, a random topic is selected.
"""
try:
student = int(student)
except Exception as e:
logging.error(f"Failed to convert student ID to int: {e}")
await ctx.respond("Invalid student ID format.")
return
if not ctx.author.id == 620319269233885226:
await ctx.respond("You don't have permission to use this command.")
return
if timeout <= 0:
timeout = None
logging.info(f"Assigning essay to student {student} with timeout {timeout} and topic {topic}")
if student not in STUDENT_IDS:
await ctx.respond("Invalid student ID.", ephemeral=True)
return
assign_essay(student, topic)
await ctx.respond(f"Assigned essay to <@{student}>: {ASSIGNED_ESSAY[student]}")
if timeout and topic:
timeout_until = discord.utils.utcnow() + timedelta(seconds=timeout)
try:
member: discord.Member = await ctx.interaction.guild.fetch_member(student)
await member.timeout(until=timeout_until, reason=f"Assigned essay: {topic}")
await ctx.respond(f'{member.mention} has been timed out for {timeout}.', ephemeral=True)
except discord.Forbidden:
await ctx.respond(f'Failed to timeout {member.mention}. Missing permissions.', ephemeral=True)
except discord.HTTPException as e:
await ctx.respond(f'Failed to timeout {member.mention}. {e.text}', ephemeral=True)
return
@bot.slash_command(
name="rps",
description="Play \"Rock, Paper, Scissors, Essay\" with Mr. Jacobs!!!",

0
docker-compose.yml Normal file → Executable file
View File

0
quotes.py Normal file → Executable file
View File

0
requirements.txt Normal file → Executable file
View File

26
students.py Normal file → Executable file
View File

@@ -81,6 +81,28 @@ def assign_essay(student_id: int, essay: str = get_essay_topic()) -> str:
else:
raise ValueError(f"Student ID {student_id} is not a valid student ID.")
def get_essay(student_id: int = None) -> str:
"""Gets the assigned essay for a student
Args:
student_id (int): The Discord ID of the student to get the essay for.
Raises:
ValueError: If the student ID is not in STUDENT_IDS.
Returns:
str: The topic of the essay assigned to the student.
"""
if student_id:
if student_id in STUDENT_IDS:
return ASSIGNED_ESSAY.get(student_id, "No essay assigned.")
else:
essays: str = ""
for essay in ASSIGNED_ESSAY:
essays += f"<@{essay}>: {ASSIGNED_ESSAY[essay]}\n"
return essays if essays else "No essays assigned."
def clear_essay(student_id):
"""Clears an assigned essay from a student
@@ -95,3 +117,7 @@ def clear_essay(student_id):
pickle.dump(ASSIGNED_ESSAY, open("./data/assigned_essay.pkl", "wb"))
else:
raise ValueError(f"Student ID {student_id} is not a valid student ID.")
if __name__ == "__main__":
print(get_essay())