Formatted source, as well as making a change to prevent the bot from spamming #general

This commit is contained in:
ForeverPyrite
2025-09-08 10:35:14 -04:00
parent 6feeaea628
commit 45b2bc12b5
5 changed files with 270 additions and 143 deletions

View File

@@ -13,7 +13,7 @@ from students import *
logger = logging.getLogger(__name__)
OPENAI_KEY = os.getenv('OPENAI_KEY')
OPENAI_KEY = os.getenv("OPENAI_KEY")
client: OpenAI = OpenAI(
api_key=OPENAI_KEY,
@@ -23,7 +23,11 @@ client: OpenAI = OpenAI(
mr_jacobs: Assistant = client.beta.assistants.retrieve("asst_KdPdwqNAKijujfyCRrJCOgJN")
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"
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:
@@ -37,12 +41,12 @@ def get_run_status(run: Run) -> str:
str: The status of the run.
"""
status: str = client.beta.threads.runs.retrieve(
run.id,
thread_id=run.thread_id
run.id, thread_id=run.thread_id
).status
logger.info(f"Status of run {run.id} is {status}")
return status
def get_instructions(context: discord.abc.GuildChannel | discord.Message = None) -> str:
"""
Retrieves the instructions for the AI based on the context.
@@ -62,16 +66,21 @@ def get_instructions(context: discord.abc.GuildChannel | discord.Message = None)
if isinstance(context, discord.Message):
context = context.channel
if not isinstance(context, discord.abc.Messageable):
logger.critical(f"Literally how are we going to send a message in a channel that is not messageable? (bro how did we get here, wtf was passed as context? oh: {context}")
logger.critical(
f"Literally how are we going to send a message in a channel that is not messageable? (bro how did we get here, wtf was passed as context? oh: {context}"
)
return tuned_instructions
if isinstance(context, discord.TextChannel):
tuned_instructions += f"\nChannel Category: {context.category.name}\nChannel Name:{context.name}\nChannel Description: {context.topic if isinstance(context, discord.TextChannel) else 'No Description'}"
elif isinstance(context, discord.DMChannel):
logger.warning(f"DM Channel detected, adding context to instructions.\nDM channel with {ID_TO_NAME.get(context.recipient.id, context.recipient.name)}")
logger.warning(
f"DM Channel detected, adding context to instructions.\nDM channel with {ID_TO_NAME.get(context.recipient.id, context.recipient.name)}"
)
tuned_instructions += f"\nThis is a direct message with {STUDENTS.get(context.recipient.id) if context.recipient.id in STUDENTS else context.recipient.name + 'No extra known info on user.'}"
return tuned_instructions
# ----- Action Handlers -----
async def handle_action(run: Run) -> bool:
# Define the list to store tool outputs
@@ -79,23 +88,39 @@ async def handle_action(run: Run) -> bool:
tool: RequiredActionFunctionToolCall
# Loop through each tool in the required action section
try:
logger.debug(f"Run.require_action is currently: {run.required_action} and tool calls are {run.required_action.submit_tool_outputs.tool_calls}")
logger.debug(
f"Run.require_action is currently: {run.required_action} and tool calls are {run.required_action.submit_tool_outputs.tool_calls}"
)
for tool in run.required_action.submit_tool_outputs.tool_calls:
logger.info(f"Handling action for tool {tool.id}, function {tool.function.name}")
logger.info(
f"Handling action for tool {tool.id}, function {tool.function.name}"
)
if tool.function.name == "assign_essay":
logger.debug(f"Handling action for assign_essay tool. Received arguments {tool.function.arguments}")
logger.debug(
f"Handling action for assign_essay tool. Received arguments {tool.function.arguments}"
)
args = load_json(tool.function.arguments)
tool_outputs.append({
tool_outputs.append(
{
"tool_call_id": tool.id,
"output": assign_essay(int(run.metadata.get("user_id"))) if not tool.function.arguments else assign_essay(int(run.metadata.get("user_id")), args["topic"])
})
"output": assign_essay(int(run.metadata.get("user_id")))
if not tool.function.arguments
else assign_essay(
int(run.metadata.get("user_id")), args["topic"]
),
}
)
elif tool.function.name == "clear_essay":
logger.debug(f"Clearing the essay for student {run.metadata.get('user_id')}")
logger.debug(
f"Clearing the essay for student {run.metadata.get('user_id')}"
)
clear_essay(int(run.metadata.get("user_id")))
tool_outputs.append({
tool_outputs.append(
{
"tool_call_id": tool.id,
"output": "Essay cleared sucessfully, feel free to still give the student feedback on their essay because it couldn't have been perfect."
})
"output": "Essay cleared sucessfully, feel free to still give the student feedback on their essay because it couldn't have been perfect.",
}
)
except AttributeError as e:
logger.error(f"Failed to handle action: {e}")
except Exception as e:
@@ -105,9 +130,7 @@ async def handle_action(run: Run) -> bool:
if tool_outputs:
try:
run = client.beta.threads.runs.submit_tool_outputs_and_poll(
thread_id=run.thread_id,
run_id=run.id,
tool_outputs=tool_outputs
thread_id=run.thread_id, run_id=run.id, tool_outputs=tool_outputs
)
logger.info("Tool outputs submitted successfully.")
except Exception as e:
@@ -127,10 +150,7 @@ async def handle_run(run: Run) -> bool:
bool: Whether or not the run completed successfully.
"""
while True:
run = client.beta.threads.runs.retrieve(
run.id,
thread_id=run.thread_id
)
run = client.beta.threads.runs.retrieve(run.id, thread_id=run.thread_id)
match run.status:
case "completed":
return True
@@ -148,7 +168,9 @@ async def handle_run(run: Run) -> bool:
await asyncio.sleep(1)
async def run(messages: list[dict], instructions: str = instructions, user_id : int = None) -> str:
async def run(
messages: list[dict], instructions: str = instructions, user_id: int = None
) -> str:
"""
Runs the AI with the given messages and instructions.
@@ -160,16 +182,14 @@ async def run(messages: list[dict], instructions: str = instructions, user_id :
Returns:
str: The response from the AI.
"""
logger.debug(f"Running AI assistant with the following parameters:\nmessages={messages}\ninstructions={instructions}\nmetadata={str(user_id if user_id else -1)}")
logger.debug(
f"Running AI assistant with the following parameters:\nmessages={messages}\ninstructions={instructions}\nmetadata={str(user_id if user_id else -1)}"
)
run = client.beta.threads.create_and_run(
assistant_id=mr_jacobs.id,
instructions=instructions,
thread={
"messages": messages
},
metadata={
"user_id": str(user_id if user_id else -1)
}
thread={"messages": messages},
metadata={"user_id": str(user_id if user_id else -1)},
)
response = await run_message(run)
return response
@@ -193,4 +213,6 @@ async def run_message(run) -> str:
if msg_ob.id == thread_messages.first_id:
response = msg_ob.content[0].text.value
return response
logger.critical(f"Couldn't find the msg that matched with the first message ID:\nThread Messages List:\n{thread_messages}")
logger.critical(
f"Couldn't find the msg that matched with the first message ID:\nThread Messages List:\n{thread_messages}"
)

37
app.py
View File

@@ -10,31 +10,54 @@ from discord_logic import bot, setup_discord_bot, send_quote, after_class
# Load environment variables
load_dotenv()
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
# Logging.
logging.basicConfig(
filename=f"./logs/jacobs.log",
filemode="at+",
level=logging.DEBUG if os.getenv('DEBUG') == "True" else logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
level=logging.DEBUG if os.getenv("DEBUG") == "True" else logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
setup_discord_bot(bot)
@bot.event
async def on_ready() -> None:
"""Event handler for when the bot is initialized."""
logger.info(f"{bot.user.name} has connected to Discord and is ready.")
print(f"{bot.user.name} is ready.")
# After successful initialization, schedule tasks.
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)
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,
)
# I'm commenting this out since it doesn't really make sense to have active senior year.
# task_scheduler.add_job(
# lambda: bot.loop.create_task(after_class()),
# "cron",
# day_of_week="mon-fri",
# hour=10,
# minute=55,
# )
task_scheduler.start()
logger.info("Presumably successfully initialized, starting bot.")
bot.run(DISCORD_TOKEN)

View File

@@ -20,8 +20,9 @@ GENERAL_ID: int = 1339601047294840876
bot = Bot(intents=discord.Intents.all())
# ----- Discord Helper Functions -----
async def get_channel(channel_id: int) -> Channel:
async def get_channel(channel_id: int = GENERAL_ID) -> Channel:
"""
Tries to get a cached channel object, if it fails it will send an API request to retrieve a new one.
@@ -37,15 +38,22 @@ async def get_channel(channel_id: int) -> Channel:
logger.debug(f"Attempting to get channel with ID {channel_id}")
channel = bot.get_channel(channel_id)
if not channel:
logger.debug(f"Channel with ID {channel_id} not found in cache, fetching from API.")
logger.debug(
f"Channel with ID {channel_id} not found in cache, fetching from API."
)
channel = await bot.fetch_channel(channel_id)
if not channel:
logger.critical(f"Could not fetch channel with channel_id {channel_id}, fetch_channel returned None.")
logger.critical(
f"Could not fetch channel with channel_id {channel_id}, fetch_channel returned None."
)
return None
logger.info(f"Successfully retrieved {channel_id}, #{channel.name}")
return channel
async def get_message(message: Message | int, channel_id: int = None, attempts: int = 3) -> Message:
async def get_message(
message: Message | int, channel_id: int = None, attempts: int = 3
) -> Message:
"""
Retrieves a message object either from cache or by fetching it from the channel.
@@ -57,23 +65,33 @@ async def get_message(message: Message | int, channel_id: int = None, attempts:
Returns:
Message: The retrieved message object.
"""
logger.debug(f"Attempting to get message with ID {message if isinstance(message, int) else message.id}")
logger.debug(
f"Attempting to get message with ID {message if isinstance(message, int) else message.id}"
)
for attempt in range(attempts):
if isinstance(message, int):
got_message: Message = bot.get_message(message)
if not got_message and channel_id:
logger.debug(f"Message with ID {message} not found in cache, fetching from channel ID {channel_id}.")
logger.debug(
f"Message with ID {message} not found in cache, fetching from channel ID {channel_id}."
)
channel: discord.abc.GuildChannel = await get_channel(channel_id)
got_message: Message = await channel.fetch_message(message)
elif not got_message:
logger.error(f"Message with ID {message} not found in cache and no channel ID provided.")
logger.error(
f"Message with ID {message} not found in cache and no channel ID provided."
)
elif isinstance(message, Message):
got_message: Message = bot.get_message(message.id)
if not got_message:
logger.debug(f"Message with ID {message.id} not found in cache, fetching from channel.")
logger.debug(
f"Message with ID {message.id} not found in cache, fetching from channel."
)
got_message: Message = await message.channel.fetch_message(message.id)
else:
logger.error(f"Couldn't retrieve message:\nMessage: {message}\nChannel ID: {channel_id}")
logger.error(
f"Couldn't retrieve message:\nMessage: {message}\nChannel ID: {channel_id}"
)
return None
if got_message:
@@ -86,6 +104,7 @@ async def get_message(message: Message | int, channel_id: int = None, attempts:
logger.error(f"Failed to retrieve message after {attempts} attempts.")
return None
async def send_message(message: str, channel: int | Channel = GENERAL_ID) -> Message:
"""
Has the bot send a message to a specified channel. If no channel is specified, the bot sends it to general.
@@ -103,13 +122,18 @@ async def send_message(message: str, channel: int | Channel = GENERAL_ID) -> Mes
if isinstance(channel, Channel):
sent_message: Message = await channel.send(message)
if isinstance(sent_message, Message):
logger.info(f"Message '{message}' successfully sent to {sent_message.channel}")
logger.info(
f"Message '{message}' successfully sent to {sent_message.channel}"
)
return sent_message
else:
logger.error(f"Message likely wasn't successfully sent, as channel.send did not return a Message.")
logger.error(
f"Message likely wasn't successfully sent, as channel.send did not return a Message."
)
elif isinstance(channel, None):
logger.error(f"Message couldn't be sent as a channel wasn't found/messagable.")
async def edit_message(message: Message | int, channel: int = None):
"""
Edits a message in a channel.
@@ -118,7 +142,9 @@ async def edit_message(message: Message | int, channel: int = None):
message (Message | int): The message to edit.
channel (int, optional): The channel ID of the message. Defaults to None.
"""
logger.debug(f"Attempting to edit message {message if isinstance(message, int) else message.id}")
logger.debug(
f"Attempting to edit message {message if isinstance(message, int) else message.id}"
)
if isinstance(message, int):
message = await get_message(message, channel)
if isinstance(message, Message):
@@ -127,6 +153,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 -----
async def msg_is_reply(message: Message) -> tuple[bool, Message]:
"""
@@ -143,7 +170,9 @@ async def msg_is_reply(message: Message) -> tuple[bool, Message]:
replied_msg = message.reference.cached_message
if replied_msg is None:
logger.debug(f"Replied message not found in cache, fetching from channel ID {message.reference.channel_id}.")
logger.debug(
f"Replied message not found in cache, fetching from channel ID {message.reference.channel_id}."
)
channel = await get_channel(message.reference.channel_id)
replied_msg = await channel.fetch_message(message.reference.message_id)
@@ -151,6 +180,7 @@ async def msg_is_reply(message: Message) -> tuple[bool, Message]:
return True, replied_msg
return False, None
async def get_reply_chain(msg: Message) -> list[Message]:
"""
Retrieves the chain of replies for a given message.
@@ -172,6 +202,7 @@ async def get_reply_chain(msg: Message) -> list[Message]:
i += 1
return reply_chain
async def gen_message_list(discord_messages: list[Message]) -> list[dict]:
"""
Generates a list of message dictionaries from a list of Discord messages.
@@ -196,25 +227,36 @@ async def gen_message_list(discord_messages: list[Message]) -> list[dict]:
"author": message.author,
"mentions": message.mentions,
"created_at": message.created_at,
"jump_url": message.jump_url
"jump_url": message.jump_url,
}
role = "assistant" if message_copy["author"].id == bot.user.id else "user"
students_mentioned = None
if message_copy["mentions"] and role == "user":
students_mentioned = [student.id for student in message_copy["mentions"] if student.id in STUDENT_IDS]
mentions = re.findall(r'<@\d{17,19}>', message_copy["content"])
students_mentioned = [
student.id
for student in message_copy["mentions"]
if student.id in STUDENT_IDS
]
mentions = re.findall(r"<@\d{17,19}>", message_copy["content"])
for mention in mentions:
mention_id = mention.replace("<@", "").replace(">", "")
student_info = ID_TO_NAME.get(int(mention_id))
if student_info:
message_copy["content"] = message_copy["content"].replace(mention, student_info.split("\n")[0])
message_copy["content"] += '\n{'
message_copy["content"] = message_copy["content"].replace(
mention, student_info.split("\n")[0]
)
message_copy["content"] += "\n{"
if students_mentioned:
message_copy["content"] += f"\n\nInfo on students mentioned (DO NOT REPEAT!): "
message_copy["content"] += (
f"\n\nInfo on students mentioned (DO NOT REPEAT!): "
)
for student in students_mentioned:
message_copy["content"] += f"\n{STUDENTS.get(student)}"
message_copy["content"] += f"\n\nSent by: {STUDENTS.get(message_copy['author'].id) if message_copy['author'].id in STUDENT_IDS else message_copy['author'].name + ' (Author\'s details not found)'}" + '}'
message_copy["content"] += (
f"\n\nSent by: {STUDENTS.get(message_copy['author'].id) if message_copy['author'].id in STUDENT_IDS else message_copy['author'].name + " (Author's details not found)"}"
+ "}"
)
if not message_copy["content"]:
debug = True
@@ -222,10 +264,7 @@ async def gen_message_list(discord_messages: list[Message]) -> list[dict]:
fetched_message = await get_message(message_copy["id"])
message_copy["content"] = fetched_message.content
thread_message = {
"role": role,
"content": message_copy["content"]
}
thread_message = {"role": role, "content": message_copy["content"]}
debug = True # Delete after fix
if debug:
debug_dump = "Messages Dump:\n"
@@ -241,10 +280,12 @@ async def gen_message_list(discord_messages: list[Message]) -> list[dict]:
logger.debug(debug_dump)
messages.append(thread_message)
else:
logger.warning(f"Argument {message} is not of type Message and will be skipped.")
logger.warning(
f"Argument {message} is not of type Message and will be skipped."
)
return messages
# ----- AI Interaction Functions -----
async def send_quote(quote: str = None) -> None:
"""
@@ -254,10 +295,18 @@ async def send_quote(quote: str = None) -> None:
quote (str, optional): The quote to send. If not provided, a random quote is selected.
"""
if not quote:
# If the quote is not defined, it's likely this is a scheduled messages
# Therefore, we are going to return early if the bot is the last person
# who sent a message to prevent a lot of uneccassary messages in general
# when there is no conversation
if get_channel().last_message.message.author.id == bot.user.id:
return
# Since no quote is defined, we are getting a random one.
quote = select_quote()
logger.info(f"Sending quote '{quote}' in #general...")
await send_message(quote)
async def after_class(student: int = None) -> None:
"""
Sends a message to a student to see the bot after class.
@@ -270,6 +319,7 @@ async def after_class(student: int = None) -> None:
logger.info(f"Sending mention to see {student} after class to #general...")
await send_message(f"Come see me after class <@{student}>")
async def has_essay(message: Message) -> bool:
if message.author.id in ASSIGNED_ESSAY:
async with message.channel.typing():
@@ -277,14 +327,15 @@ async def has_essay(message: Message) -> bool:
message_list = await gen_message_list(message)
response = await run(
message_list,
f"{get_instructions(message)}\n\nTHIS USER HAS AN ESSAY!!! Yell at them and tell them to finish their essay on {ASSIGNED_ESSAY.get(message.author.id)}!!! Unless...they sent you the essay, in that case, you can decide whether or not it is satisfactory (it needs to be in MLA format and 300-2000 characters long, and to be relevant of course). If it is satisfactory, you can tell them they are free to go, after you run the function \"clear_essay\".",
message.author.id
f'{get_instructions(message)}\n\nTHIS USER HAS AN ESSAY!!! Yell at them and tell them to finish their essay on {ASSIGNED_ESSAY.get(message.author.id)}!!! Unless...they sent you the essay, in that case, you can decide whether or not it is satisfactory (it needs to be in MLA format and 300-2000 characters long, and to be relevant of course). If it is satisfactory, you can tell them they are free to go, after you run the function "clear_essay".',
message.author.id,
)
await message.reply(response)
return True
else:
return False
# ----- Discord Commands & Event Handlers -----
def setup_discord_bot(bot: Bot) -> None:
"""
@@ -293,6 +344,7 @@ def setup_discord_bot(bot: Bot) -> None:
Args:
bot (Bot): The Discord bot instance.
"""
@bot.slash_command(description="Talk to Mr. Jacobs!!!")
async def message(ctx: discord.ApplicationContext, text: str) -> None:
"""
@@ -309,8 +361,12 @@ 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:
@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.
@@ -333,8 +389,13 @@ def setup_discord_bot(bot: Bot) -> None:
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:
@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)
@@ -352,7 +413,7 @@ def setup_discord_bot(bot: Bot) -> None:
ctx: discord.ApplicationContext,
student: str,
timeout: int | None = 0,
topic: str | None = None
topic: str | None = None,
) -> None:
"""
Slash command to assign an essay to a student.
@@ -374,7 +435,9 @@ def setup_discord_bot(bot: Bot) -> None:
return
if timeout <= 0:
timeout = None
logging.info(f"Assigning essay to student {student} with timeout {timeout} and topic {topic}")
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
@@ -383,18 +446,30 @@ def setup_discord_bot(bot: Bot) -> None:
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)
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)
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)
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!!!",
description='Play "Rock, Paper, Scissors, Essay" with Mr. Jacobs!!!',
)
async def rps_essay(
ctx: discord.ApplicationContext,
@@ -403,9 +478,9 @@ def setup_discord_bot(bot: Bot) -> None:
choices=[
discord.OptionChoice("Rock"),
discord.OptionChoice("Paper"),
discord.OptionChoice("Scissors")
]
)
discord.OptionChoice("Scissors"),
],
),
) -> None:
"""
Play Rock Paper Scissors with Mr. Jacobs.
@@ -417,7 +492,7 @@ def setup_discord_bot(bot: Bot) -> None:
logging.info(f"{ctx.author} chose {choice}")
outcomes = {"Rock": "Paper", "Paper": "Scissors", "Scissors": "Rock"}
await ctx.respond(
f"I choose {outcomes[choice]}, you owe me a {randint(1,15)} page essay "
f"I choose {outcomes[choice]}, you owe me a {randint(1, 15)} page essay "
f"on {assign_essay(ctx.author.id)}. MLA format, double spaced, "
"12pt Times New Roman!"
)

View File

@@ -4,7 +4,7 @@ import datetime
from students import STUDENT_IDS
QUOTES = {
"NORMAL" : (
"NORMAL": (
"Oh boy, we are about to wake up!",
"All right 👏 🫰",
"How we doing, Paul?",
@@ -27,24 +27,23 @@ QUOTES = {
"*Spins lanyard*",
"Come on guys, you should know this already!",
"Let me just say this...",
"Not trying to be mean or anything...but..."
"Not trying to be mean or anything...but...",
),
"RARE" : (
"RARE": (
"Play stupid games, win big prizes! 🤑🤑",
"Oooooo raahahah!",
"It's cherry-pickin' time, y'all!",
"What does the fox say?"
"What does the fox say?",
),
"MYTHIC" : (
"MYTHIC": (
"I'm proud of you.",
"You can take a 5-minute break.",
"I have somewhere to be at 9:30, so you guys will have a sub."
"I have somewhere to be at 9:30, so you guys will have a sub.",
),
"LEGENDARY": (
)
"LEGENDARY": (),
}
def select_quote() -> str:
rarity = random.randint(0, 99)
if rarity < 1:
@@ -55,11 +54,16 @@ def select_quote() -> str:
quote = random.choice(QUOTES.get("NORMAL"))
# Append log to a file (quotes.log)
with open("./logs/quotes.log", "at+") as log_file:
log_file.write(f"At {datetime.datetime.now()}, rarity was rolled as {rarity} and selected: {quote}\n")
log_file.write(
f"At {datetime.datetime.now()}, rarity was rolled as {rarity} and selected: {quote}\n"
)
return quote
def select_student() -> int:
student = random.choice(STUDENT_IDS)
with open("./logs/quotes.log", "at+") as log_file:
log_file.write(f"At {datetime.datetime.now()}, wanted to see {student} after class.\n")
log_file.write(
f"At {datetime.datetime.now()}, wanted to see {student} after class.\n"
)
return student

View File

@@ -5,9 +5,7 @@ import pickle
logger = logging.getLogger(__name__)
# Redacted
ID_TO_NAME: dict[int, str] = {
}
ID_TO_NAME: dict[int, str] = {}
# Redacted
STUDENT_IDS: set = ()
@@ -28,7 +26,8 @@ finally:
# Redacted
STUDENTS: dict[int, str] = {}
ESSAY_TOPICS = ("why to not throw rocks during a fire drill",
ESSAY_TOPICS = (
"why to not throw rocks during a fire drill",
"how to sit in a chair properly",
"how to keep your hands to yourself",
"how to be on time",
@@ -41,11 +40,13 @@ ESSAY_TOPICS = ("why to not throw rocks during a fire drill",
"why having your professionalism packet is essential for your future career",
"why playing rock-paper-scissors over text is very productive",
"why steak is the best food for my lunch break",
)
)
def get_essay_topic() -> str:
return random_choice(ESSAY_TOPICS)
def assign_essay(student_id: int, essay: str = get_essay_topic()) -> str:
"""Assigns a student an essay
@@ -66,6 +67,7 @@ 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
@@ -106,3 +108,4 @@ def clear_essay(student_id):
if __name__ == "__main__":
print(get_essay())