first commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/.env
|
||||
/.venv
|
||||
/logs
|
||||
/__pycache__
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal 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
123
app.py
Normal 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
7
docker-compose.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
discord-bot:
|
||||
build: .
|
||||
volumes:
|
||||
- ./logs/:/app/logs/
|
||||
env_file: .env
|
||||
restart: unless-stopped
|
||||
55
quotes.py
Normal file
55
quotes.py
Normal 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
BIN
requirements.txt
Normal file
Binary file not shown.
24
students.py
Normal file
24
students.py
Normal 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?"
|
||||
}
|
||||
Reference in New Issue
Block a user