first commit

This commit is contained in:
ForeverPyrite
2025-02-20 22:48:49 -05:00
commit f84605fb0d
6 changed files with 206 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.