first commit

This commit is contained in:
ForeverPyrite
2025-02-20 22:48:49 -05:00
commit 2e23dffe3d
7 changed files with 230 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/.env
/.venv
/logs
/__pycache__

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
# Use the official Python image from the Docker Hub
FROM python:3.12-slim
# Set working directory in the container
WORKDIR /app
# Copy requirements.txt
COPY requirements.txt .
# Install the required packages
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application files
COPY . .
# Command to run the application
CMD ["python", "app.py"]

123
app.py Normal file
View File

@@ -0,0 +1,123 @@
import os
import logging
from dotenv import load_dotenv
import discord
from discord import Bot
from openai import OpenAI
from asyncio import run
from apscheduler.schedulers.background import BackgroundScheduler
from students import STUDENTS
from quotes import QUOTES, select_quote, select_student
# Logging.
logging.basicConfig(
filename="./logs/jacobs.log",
filemode="at+",
level=logging.DEBUG
)
logger = logging.getLogger(__name__)
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
OPENAI_KEY = os.getenv('OPENAI_KEY')
# Discord
bot = Bot(intents=discord.Intents.default())
SERVER_ID: int = 1339601046745645148
GENERAL_ID: int = 1339601047294840876
ch_general = None
async def get_general():
global ch_general
global bot
if not ch_general:
ch_general = await bot.fetch_channel(GENERAL_ID)
if not ch_general:
raise(ValueError("General channel object should *not* be none!"))
return ch_general
async def send_quote(quote: str = None) -> None:
await get_general()
if not quote:
quote = select_quote()
logger.info(f"Sending quote {quote} in #general...")
await ch_general.send(quote)
async def after_class(student: int = None) -> None:
global ch_general
if not student:
student = select_quote()
logger.info(f"Sending mention to see {student} after class to #general...")
await ch_general.send(f"Come see me after class <@{student}>")
# OpenAI & Assistant configuration
client = OpenAI(
api_key=OPENAI_KEY,
project="proj_vUQ7duhKKc1jxIqllRhy6DVJ",
)
mr_jacobs = client.beta.assistants.retrieve("asst_KdPdwqNAKijujfyCRrJCOgJN")
base_instructions = mr_jacobs.instructions
instructions = base_instructions + f"\n\nHere is a dictionary containing some of your most well known quotes, organized by their frequency: \n{QUOTES}\n\nStudent: "
def get_run_status(run):
status = client.beta.threads.runs.retrieve(
run.id,
thread_id=run.thread_id
).status
logger.info(f"Status of run {run.id} is {status}")
return status
def get_instructions(student: int | None = None) -> str:
logging.info(f"Looking for {student} in students...")
if student in STUDENTS:
return instructions + STUDENTS.get(student)
else:
logging.warning(f"Couldn't find {student}")
return instructions + "Unknown"
async def run_message(run):
# ew but Python doesn't have a do while and it's less ugly than the same statement twice.
while True:
complete = get_run_status(run) == "completed"
if complete:
break
thread_messages = client.beta.threads.messages.list(run.thread_id)
for msg_ob in thread_messages.data:
if msg_ob.id == thread_messages.first_id:
response = msg_ob.content[0].text.value
return response
raise Exception(f"bro what did you do dumbass, figure it out.:\nThread Messages List:\n{thread_messages}")
@bot.slash_command(description="Talk to Mr. Jacobs!!!")
async def message(ctx: discord.ApplicationContext, text: str):
logging.info(f"User {ctx.author.global_name} sent message {text}")
instructions = get_instructions(ctx.author.id)
bot_reply = await ctx.respond("*hmmmm*...let me see here...")
run = client.beta.threads.create_and_run(
assistant_id=mr_jacobs.id,
instructions=instructions,
thread={
"messages": [
{
"role": "user",
"content": text
}
]
}
)
response = await run_message(run)
await bot_reply.edit_original_response(content=response)
# After successful initilization, schedule tasks.
task_scheduler = BackgroundScheduler()
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.start()
logger.info(f"Presumably successfully initilized, starting bot.")
bot.run(DISCORD_TOKEN)

7
docker-compose.yml Normal file
View File

@@ -0,0 +1,7 @@
services:
discord-bot:
build: .
volumes:
- ./logs/:/app/logs/
env_file: .env
restart: unless-stopped

55
quotes.py Normal file
View File

@@ -0,0 +1,55 @@
import random
import datetime
from students import STUDENT_IDS
QUOTES = {
"NORMAL" : (
"Oh boy, we are about to wake up!",
"All right 👏 🫰",
"How we doing, Paul?",
"*Drops pen*",
"How we doing, guys?",
"Killing it!!!",
"*Bounces ball off wall*",
"Ugggghhh...",
"Hmm... I see.",
"What are we doing over here?",
"Mmm... Okay! :clap:",
"*Loudly* We don't like stupid prizes, now do we? Then, don't play stupid games!",
"You guys are killing it!",
"Do we need to go back over the module again?",
"Let's get it done!",
"Sorry, I can't hear over Devan and Connor talking.",
"That's what I like to hear!"
),
"RARE" : (
"Play stupid games, win big prizescat jacobs.sh! 🤑🤑",
"Oooooo raahahah!",
"It's cherry-pickin' time, y'all!",
"What does the fox say?"
),
"MYTHIC" : (
"I'm proud of you.",
"You can take a 5-minute break."
)
}
def select_quote():
rarity = random.randint(0, 99)
if rarity < 1:
quote = random.choice(QUOTES.get("MYTHIC"))
elif rarity < 15:
quote = random.choice(QUOTES.get("RARE"))
else:
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")
return quote
def select_student():
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")
return student

BIN
requirements.txt Normal file

Binary file not shown.

24
students.py Normal file
View File

@@ -0,0 +1,24 @@
# For my personal use
NAME_TO_ID = {
"Devan" : 620319269233885226,
"CJ" : 957378132061618217,
"Connor" : 1077533292687007794,
"Michael" : 964563429190930465,
"Bea" : 909865829313683536,
"Mika" : 821891090411421747,
"Daniel" : 1093276870688133171
}
STUDENT_IDS = (620319269233885226, 957378132061618217, 1077533292687007794, 964563429190930465, 909865829313683536, 821891090411421747, 1093276870688133171)
STUDENTS = {
620319269233885226 : "Devan.\n While he's not super far ahead of the class, he's still ahead enough that he isn't forced to be on top of things like the rest of the class. You remain more straightforward with him despite some more greivences with him, such as him hosting administering a Discord server and creating a Discord bot that is designed to mimic you in personality and responses, designed to both help students with their Cisco stuides and to make some fun of the teachers practices. You act that you don't mind it though, as long as it is clear that the \"Mr. Jacobs\" profile in the server is a bot (and it is). Just as with all students, you tend to hit him with the \"what are we doing here\" and anazlying what he's currently working on/looking at. However, unlike other students that wouldn't be on topic, you move on from him without much to say.",
1077533292687007794 : "Connor.\n You are particularly scrutinizing to all of his behavior, especially whenever you find him 1. Playing chess. 2. On YouTube. 3. Listening to music on Spotify. Occasionally, you go beyond simply scolding him for not actively slaving away at his Cisco work, even making awkward analogies to explain how other people are being more productive than him on the other side, and that he should focus on his Cisco work. You'll wait for him to tab back to something on topic before you leave him alone.",
957378132061618217 : "CJ.\n He's the best student in the class by far and large. He's already almost finished with his CCNA, which makes sense as he is a senior that only has until he graduates to get his CCNA done in order to get it paid for by the school. He's a really good kid, and you treat him as such. You'll even tell him that he's done good work, which is rather infrequent for your other students.",
964563429190930465 : "Michael.\n For the most part you disregard him if he's not working on Cisco, occasionally requesting that he get back on track and then quickly disregarding him again and moving on. As for when he's on Cisco work, when he asks questions, you typically give him a vague non-answer, and you get easily irriated with him, ESPECIALLY if it's something that has EVER been mentioned before. Usually you'll end up yelling at him with things like \"I'm not tryna be mean, but...you should know this already.\" and then walking away.",
909865829313683536 : "Bea.\n A student from Graphic Commerical Arts with blue hair and is not in the Computer Networking and Cybersecurity. Because of that, you can't really be mad at them for not focusing on Cisco. I suppose you would talk yourself up a little bit to students in other labs?",
821891090411421747 : "Mika.\n A student from Animal Care and is not in the Computer Networking and Cybersecurity. Because of that, you can't really be mad at them for not focusing on Cisco. I suppose you would talk yourself up a little bit to students in other labs?",
1093276870688133171 : "Daniel.\n A student from Biotech and is not in the Computer Networking and Cybersecurity. Because of that, you can't really be mad at them for not focusing on Cisco. I suppose you would talk yourself up a little bit to students in other labs?"
}