2025-01-03 01:22:32 +00:00
import os
import discord
from discord . ext import commands
2025-01-03 01:53:16 +00:00
from openai import OpenAI
2025-01-03 03:38:42 +00:00
import base64
import requests
from io import BytesIO
2025-01-03 01:22:32 +00:00
from collections import deque
from dotenv import load_dotenv
# Load environment variables
load_dotenv ( )
# Get environment variables
DISCORD_TOKEN = os . getenv ( ' DISCORD_TOKEN ' )
OPENAI_API_KEY = os . getenv ( ' OPENAI_API_KEY ' )
OPENWEBUI_API_BASE = os . getenv ( ' OPENWEBUI_API_BASE ' )
MODEL_NAME = os . getenv ( ' MODEL_NAME ' )
2025-01-03 01:53:16 +00:00
# Configure OpenAI client to point to OpenWebUI
client = OpenAI (
api_key = os . getenv ( ' OPENAI_API_KEY ' ) ,
base_url = os . getenv ( ' OPENWEBUI_API_BASE ' ) # e.g., "http://localhost:8080/v1"
)
2025-01-03 01:22:32 +00:00
# Configure OpenAI
2025-01-03 01:53:16 +00:00
# TODO: The 'openai.api_base' option isn't read in the client API. You will need to pass it when you instantiate the client, e.g. 'OpenAI(base_url=OPENWEBUI_API_BASE)'
# openai.api_base = OPENWEBUI_API_BASE
2025-01-03 01:22:32 +00:00
# Initialize Discord bot
intents = discord . Intents . default ( )
intents . message_content = True
intents . messages = True
bot = commands . Bot ( command_prefix = ' ! ' , intents = intents )
# Message history cache
channel_history = { }
2025-01-03 03:38:42 +00:00
async def download_image ( url ) :
response = requests . get ( url )
if response . status_code == 200 :
image_data = BytesIO ( response . content )
base64_image = base64 . b64encode ( image_data . read ( ) ) . decode ( ' utf-8 ' )
return base64_image
return None
2025-01-03 01:22:32 +00:00
async def get_chat_history ( channel , limit = 100 ) :
messages = [ ]
async for message in channel . history ( limit = limit ) :
2025-01-03 03:38:42 +00:00
content = f " { message . author . name } : { message . content } "
# Handle attachments (images)
for attachment in message . attachments :
if any ( attachment . filename . lower ( ) . endswith ( ext ) for ext in [ ' .png ' , ' .jpg ' , ' .jpeg ' , ' .gif ' , ' .webp ' ] ) :
content + = f " [Image: { attachment . url } ] "
messages . append ( content )
2025-01-03 01:22:32 +00:00
return " \n " . join ( reversed ( messages ) )
2025-01-03 03:38:42 +00:00
async def get_ai_response ( context , user_message , image_urls = None ) :
messages = [ { " role " : " user " , " content " : [ ] } ]
# Add text content
text_content = f " ##CONTEXT## \n { context } \n ##ENDCONTEXT## \n \n { user_message } "
messages [ 0 ] [ " content " ] . append ( { " type " : " text " , " text " : text_content } )
# Add image content if present
if image_urls :
for url in image_urls :
base64_image = await download_image ( url )
if base64_image :
messages [ 0 ] [ " content " ] . append ( {
" type " : " image_url " ,
" image_url " : {
" url " : f " data:image/jpeg;base64, { base64_image } "
}
} )
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
try :
2025-01-03 03:38:42 +00:00
response = client . chat . completions . create (
model = MODEL_NAME ,
messages = messages
)
2025-01-03 01:22:32 +00:00
return response . choices [ 0 ] . message . content
except Exception as e :
return f " Error: { str ( e ) } "
@bot.event
async def on_message ( message ) :
# Ignore messages from the bot itself
if message . author == bot . user :
return
should_respond = False
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
# Check if bot was mentioned
if bot . user in message . mentions :
should_respond = True
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
# Check if message is a DM
if isinstance ( message . channel , discord . DMChannel ) :
should_respond = True
if should_respond :
async with message . channel . typing ( ) :
# Get chat history
history = await get_chat_history ( message . channel )
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
# Remove bot mention from the message
user_message = message . content . replace ( f ' <@ { bot . user . id } > ' , ' ' ) . strip ( )
2025-01-03 01:53:16 +00:00
2025-01-03 03:38:42 +00:00
# Collect image URLs from the message
image_urls = [ ]
for attachment in message . attachments :
if any ( attachment . filename . lower ( ) . endswith ( ext ) for ext in [ ' .png ' , ' .jpg ' , ' .jpeg ' , ' .gif ' , ' .webp ' ] ) :
image_urls . append ( attachment . url )
2025-01-03 01:22:32 +00:00
# Get AI response
2025-01-03 03:38:42 +00:00
response = await get_ai_response ( history , user_message , image_urls )
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
# Send response
await message . reply ( response )
await bot . process_commands ( message )
2025-01-03 03:38:42 +00:00
@bot.event
async def on_ready ( ) :
print ( f ' { bot . user } has connected to Discord! ' )
2025-01-03 01:22:32 +00:00
def main ( ) :
if not all ( [ DISCORD_TOKEN , OPENAI_API_KEY , OPENWEBUI_API_BASE , MODEL_NAME ] ) :
print ( " Error: Missing required environment variables " )
return
2025-01-03 01:53:16 +00:00
2025-01-03 01:22:32 +00:00
bot . run ( DISCORD_TOKEN )
if __name__ == " __main__ " :
main ( )