Files
discordgpt/leogpt.py
Leonard Excoffier 1022d196ae Base setup,
Imported the leogpt.py program,
Edited the readme.md to give info on setup,
Created .gitignore and added config.py to exclusion
2023-11-24 20:26:22 -05:00

174 lines
5.8 KiB
Python

import discord
from discord.ext import commands
from openai import AsyncOpenAI
import logging
import asyncio
import time
import config
import os
from collections import defaultdict
from datetime import datetime, timedelta
# Configuration
openai_api_key = os.getenv('OPENAI_API_KEY', config.openai_api_key)
discord_bot_token = os.getenv('DISCORD_BOT_TOKEN', config.discord_bot_token)
assistant_id = config.assistant_id
# Setting up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# OpenAI Client Setup
openai_client = AsyncOpenAI(api_key=openai_api_key)
# Discord Bot Setup
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
# Thread Management
thread_ids = defaultdict(lambda: {"thread_id": None, "last_used": datetime.now()})
# Function to create a new thread
async def create_new_thread(identifier):
global thread_ids
try:
thread = await openai_client.beta.threads.create()
thread_ids[identifier] = {"thread_id": thread.id, "last_used": datetime.now()}
logging.info(f"New thread created for {identifier} with ID: {thread.id}")
except Exception as e:
logging.error(f"Error during thread creation for {identifier}: {e}")
raise
# Function to send messages in chunks
async def send_in_chunks(channel, message):
logging.info("Sending message in chunks")
try:
chunk_size = 2000
while message:
split_index = (message.rfind(' ', 0, chunk_size) + 1) if len(message) > chunk_size else len(message)
chunk = message[:split_index].strip()
await channel.send(chunk)
message = message[split_index:]
logging.info("All chunks sent successfully")
except Exception as e:
logging.error(f"Error sending message in chunks: {e}")
raise
# Function to process incoming messages
async def process_message(message):
return discord.utils.remove_markdown(message.clean_content)
# Function to send message to OpenAI
async def send_message_to_openai(clean_message, thread_id):
try:
await openai_client.beta.threads.messages.create(
thread_id=thread_id,
role="user",
content=clean_message
)
except Exception as e:
logging.error(f"Error sending message to OpenAI: {e}")
raise
# Function to check OpenAI response
async def check_openai_response(thread_id, run_id):
try:
start_time = time.time()
while True:
updated_run = await openai_client.beta.threads.runs.retrieve(
thread_id=thread_id,
run_id=run_id
)
if updated_run.status == "completed":
break
elapsed_time = time.time() - start_time
sleep_time = min(1 + elapsed_time / 10, 5)
await asyncio.sleep(sleep_time)
except Exception as e:
logging.error(f"Error checking OpenAI response: {e}")
raise
# Function to retrieve the latest response from OpenAI
async def retrieve_latest_response(thread_id):
try:
messages = await openai_client.beta.threads.messages.list(thread_id=thread_id)
assistant_messages = [msg for msg in messages.data if msg.role == "assistant"]
if assistant_messages:
latest_message = max(assistant_messages, key=lambda x: x.created_at)
return latest_message.content[0].text.value
else:
return "No response from the assistant."
except Exception as e:
logging.error(f"Error retrieving response from OpenAI: {e}")
raise
# Function to interact with OpenAI
async def interact_with_openai(clean_message, identifier):
global thread_ids
thread_info = thread_ids[identifier]
thread_id = thread_info["thread_id"]
if thread_id is None:
await create_new_thread(identifier)
thread_id = thread_ids[identifier]["thread_id"]
try:
await send_message_to_openai(clean_message, thread_id)
run = await openai_client.beta.threads.runs.create(
thread_id=thread_id,
assistant_id=assistant_id
)
await check_openai_response(thread_id, run.id)
return await retrieve_latest_response(thread_id)
except Exception as e:
logging.error(f"Error during OpenAI interaction: {e}")
return "I'm having trouble processing your request right now."
# Function for cleaning up old threads
def cleanup_old_threads():
now = datetime.now()
for key, value in list(thread_ids.items()):
if now - value["last_used"] > timedelta(hours=1):
del thread_ids[key]
# Bot event: on_ready
@bot.event
async def on_ready():
try:
logging.info(f"Logged in as {bot.user.name}")
except Exception as e:
logging.error(f"Error in on_ready: {e}")
# Bot event: on_message
@bot.event
async def on_message(message):
try:
if message.author == bot.user:
return
# Check for @everyone or @here mentions and ignore such messages
if "@everyone" in message.content or "@here" in message.content:
return
identifier = message.channel.id
thread_ids[identifier]["last_used"] = datetime.now()
cleanup_old_threads()
if bot.user.mentioned_in(message):
clean_message = await process_message(message)
logging.info(f"Received message from {message.author.name}: {clean_message}")
async with message.channel.typing():
response = await interact_with_openai(clean_message, identifier)
logging.info(f"OpenAI response: {response}")
await send_in_chunks(message.channel, response)
except Exception as e:
logging.error(f"Error in on_message for {message.content}: {e}")
# Running the bot
bot.run(discord_bot_token)