gotta archive this lmao
This commit is contained in:
0
.dockerignore
Normal file → Executable file
0
.dockerignore
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.vscode/launch.json
vendored
Normal file → Executable file
0
.vscode/launch.json
vendored
Normal file → Executable file
0
Dockerfile
Normal file → Executable file
0
Dockerfile
Normal file → Executable file
18
ai_logic.py
Normal file → Executable file
18
ai_logic.py
Normal file → Executable file
@@ -1,7 +1,8 @@
|
|||||||
|
# ----- Configuration & Setup -----
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord # For type hintings
|
import discord # For type hintings
|
||||||
from json import loads as load_json
|
from json import loads as load_json
|
||||||
|
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
@@ -14,7 +15,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
OPENAI_KEY = os.getenv('OPENAI_KEY')
|
OPENAI_KEY = os.getenv('OPENAI_KEY')
|
||||||
|
|
||||||
# OpenAI & Assistant configuration
|
|
||||||
client: OpenAI = OpenAI(
|
client: OpenAI = OpenAI(
|
||||||
api_key=OPENAI_KEY,
|
api_key=OPENAI_KEY,
|
||||||
project="proj_vUQ7duhKKc1jxIqllRhy6DVJ",
|
project="proj_vUQ7duhKKc1jxIqllRhy6DVJ",
|
||||||
@@ -25,6 +25,7 @@ base_instructions: str = mr_jacobs.instructions
|
|||||||
logger.info(f"Base instructions: {base_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"
|
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:
|
def get_run_status(run: Run) -> str:
|
||||||
"""
|
"""
|
||||||
Retrieves the status of a run.
|
Retrieves the status of a run.
|
||||||
@@ -71,6 +72,7 @@ def get_instructions(context: discord.abc.GuildChannel | discord.Message = None)
|
|||||||
|
|
||||||
return tuned_instructions
|
return tuned_instructions
|
||||||
|
|
||||||
|
# ----- Action Handlers -----
|
||||||
async def handle_action(run: Run) -> bool:
|
async def handle_action(run: Run) -> bool:
|
||||||
# Define the list to store tool outputs
|
# Define the list to store tool outputs
|
||||||
tool_outputs = []
|
tool_outputs = []
|
||||||
@@ -114,7 +116,16 @@ async def handle_action(run: Run) -> bool:
|
|||||||
logger.warning("No tool outputs to submit.")
|
logger.warning("No tool outputs to submit.")
|
||||||
|
|
||||||
|
|
||||||
|
# ----- Run Handlers -----
|
||||||
async def handle_run(run: Run) -> bool:
|
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:
|
while True:
|
||||||
run = client.beta.threads.runs.retrieve(
|
run = client.beta.threads.runs.retrieve(
|
||||||
run.id,
|
run.id,
|
||||||
@@ -129,6 +140,9 @@ async def handle_run(run: Run) -> bool:
|
|||||||
case "requires_action":
|
case "requires_action":
|
||||||
logger.info(f"Run {run.id} requires action.")
|
logger.info(f"Run {run.id} requires action.")
|
||||||
await handle_action(run)
|
await handle_action(run)
|
||||||
|
case "expired":
|
||||||
|
logger.error(f"Run {run.id} expired.")
|
||||||
|
return False
|
||||||
case _:
|
case _:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|||||||
2
app.py
Normal file → Executable file
2
app.py
Normal file → Executable file
@@ -30,7 +30,7 @@ async def on_ready() -> None:
|
|||||||
print(f"{bot.user.name} is ready.")
|
print(f"{bot.user.name} is ready.")
|
||||||
|
|
||||||
# After successful initialization, schedule tasks.
|
# 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, 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(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)
|
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
94
discord_logic.py
Normal file → Executable file
@@ -1,10 +1,13 @@
|
|||||||
|
# ----- Imports & Setup -----
|
||||||
import logging
|
import logging
|
||||||
import regex as re
|
import regex as re
|
||||||
from random import randint
|
from random import randint
|
||||||
from asyncio import run as asyncio_run, sleep
|
from asyncio import run as asyncio_run, sleep
|
||||||
|
from datetime import timedelta
|
||||||
import discord
|
import discord
|
||||||
from discord import Bot, Message
|
from discord import Bot, Message
|
||||||
from discord.abc import GuildChannel as Channel
|
from discord.abc import GuildChannel as Channel
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
from ai_logic import run, get_instructions
|
from ai_logic import run, get_instructions
|
||||||
from quotes import select_quote, select_student
|
from quotes import select_quote, select_student
|
||||||
@@ -17,7 +20,7 @@ GENERAL_ID: int = 1339601047294840876
|
|||||||
|
|
||||||
bot = Bot(intents=discord.Intents.all())
|
bot = Bot(intents=discord.Intents.all())
|
||||||
|
|
||||||
# ---------- Discord Helper Functions ----------
|
# ----- Discord Helper Functions -----
|
||||||
async def get_channel(channel_id: int) -> Channel:
|
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.
|
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:
|
else:
|
||||||
logger.error(f"Message couldn't be edited as it was not found.")
|
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]:
|
async def msg_is_reply(message: Message) -> tuple[bool, Message]:
|
||||||
"""
|
"""
|
||||||
Checks if a message is a reply to another 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
|
return messages
|
||||||
|
|
||||||
# ---------- AI Interaction Functions ----------
|
# ----- AI Interaction Functions -----
|
||||||
async def send_quote(quote: str = None) -> None:
|
async def send_quote(quote: str = None) -> None:
|
||||||
"""
|
"""
|
||||||
Sends a quote to the general channel.
|
Sends a quote to the general channel.
|
||||||
@@ -282,7 +285,7 @@ async def has_essay(message: Message) -> bool:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ---------- Discord Commands & Event Handlers ----------
|
# ----- Discord Commands & Event Handlers -----
|
||||||
def setup_discord_bot(bot: Bot) -> None:
|
def setup_discord_bot(bot: Bot) -> None:
|
||||||
"""
|
"""
|
||||||
Sets up the Discord bot with commands and event listeners.
|
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)
|
response = await run(thread_messages, instructions, ctx.author.id)
|
||||||
await ctx.respond(content=response)
|
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(
|
@bot.slash_command(
|
||||||
name="rps",
|
name="rps",
|
||||||
description="Play \"Rock, Paper, Scissors, Essay\" with Mr. Jacobs!!!",
|
description="Play \"Rock, Paper, Scissors, Essay\" with Mr. Jacobs!!!",
|
||||||
|
|||||||
0
docker-compose.yml
Normal file → Executable file
0
docker-compose.yml
Normal file → Executable file
0
requirements.txt
Normal file → Executable file
0
requirements.txt
Normal file → Executable file
26
students.py
Normal file → Executable file
26
students.py
Normal file → Executable file
@@ -81,6 +81,28 @@ def assign_essay(student_id: int, essay: str = get_essay_topic()) -> str:
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"Student ID {student_id} is not a valid student ID.")
|
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):
|
def clear_essay(student_id):
|
||||||
"""Clears an assigned essay from a student
|
"""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"))
|
pickle.dump(ASSIGNED_ESSAY, open("./data/assigned_essay.pkl", "wb"))
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Student ID {student_id} is not a valid student ID.")
|
raise ValueError(f"Student ID {student_id} is not a valid student ID.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(get_essay())
|
||||||
Reference in New Issue
Block a user