261 lines
9.0 KiB
Python
261 lines
9.0 KiB
Python
"""
|
|
This is a discord bot for generating images using OpenAI's DALL-E
|
|
|
|
Author: Stefan Rial
|
|
YouTube: https://youtube.com/@StefanRial
|
|
GitHub: https://https://github.com/StefanRial/ClaudeBot
|
|
E-Mail: mail.stefanrial@gmail.com
|
|
"""
|
|
|
|
import discord
|
|
import openai
|
|
import urllib.request
|
|
import os
|
|
from datetime import datetime
|
|
from configparser import ConfigParser
|
|
from queue import Queue
|
|
import agent
|
|
from agent import agent_actions
|
|
from localagi import LocalAGI
|
|
import asyncio
|
|
import threading
|
|
from discord import app_commands
|
|
import functools
|
|
import typing
|
|
|
|
config_file = "config.ini"
|
|
config = ConfigParser(interpolation=None)
|
|
config.read(config_file)
|
|
|
|
SERVER_ID = config["discord"]["server_id"]
|
|
DISCORD_API_KEY = config["discord"][str("api_key")]
|
|
OPENAI_ORG = config["openai"][str("organization")]
|
|
OPENAI_API_KEY = config["openai"][str("api_key")]
|
|
|
|
FILE_PATH = config["settings"][str("file_path")]
|
|
FILE_NAME_FORMAT = config["settings"][str("file_name_format")]
|
|
|
|
SIZE_LARGE = "1024x1024"
|
|
SIZE_MEDIUM = "512x512"
|
|
SIZE_SMALL = "256x256"
|
|
SIZE_DEFAULT = config["settings"][str("default_size")]
|
|
|
|
GUILD = discord.Object(id=SERVER_ID)
|
|
|
|
if not os.path.isdir(FILE_PATH):
|
|
os.mkdir(FILE_PATH)
|
|
|
|
|
|
class Client(discord.Client):
|
|
def __init__(self, *, intents: discord.Intents):
|
|
super().__init__(intents=intents)
|
|
self.tree = app_commands.CommandTree(self)
|
|
|
|
async def setup_hook(self):
|
|
self.tree.copy_global_to(guild=GUILD)
|
|
await self.tree.sync(guild=GUILD)
|
|
|
|
|
|
claude_intents = discord.Intents.default()
|
|
claude_intents.messages = True
|
|
claude_intents.message_content = True
|
|
client = Client(intents=claude_intents)
|
|
|
|
openai.organization = OPENAI_ORG
|
|
openai.api_key = OPENAI_API_KEY
|
|
openai.Model.list()
|
|
|
|
|
|
async def close_thread(thread: discord.Thread):
|
|
await thread.edit(name="closed")
|
|
await thread.send(
|
|
embed=discord.Embed(
|
|
description="**Thread closed** - Context limit reached, closing...",
|
|
color=discord.Color.blue(),
|
|
)
|
|
)
|
|
await thread.edit(archived=True, locked=True)
|
|
|
|
@client.event
|
|
async def on_ready():
|
|
print(f"We have logged in as {client.user}")
|
|
|
|
def run_localagi_thread_history(history, message, thread, loop):
|
|
def call(thing):
|
|
return asyncio.run_coroutine_threadsafe(thing,loop).result()
|
|
sent_message = call(thread.send(f"⚙️ LocalAGI starts"))
|
|
|
|
user = message.author
|
|
def action_callback(name, parameters):
|
|
call(sent_message.edit(content=f"⚙️ Calling function '{name}' with {parameters}"))
|
|
def reasoning_callback(name, reasoning):
|
|
call(sent_message.edit(content=f"🤔 I'm thinking... '{reasoning}' (calling '{name}'), please wait.."))
|
|
|
|
localagi = LocalAGI(
|
|
agent_actions=agent_actions,
|
|
llm_model=config["agent"]["llm_model"],
|
|
tts_model=config["agent"]["tts_model"],
|
|
action_callback=action_callback,
|
|
reasoning_callback=reasoning_callback,
|
|
tts_api_base=config["agent"]["tts_api_base"],
|
|
functions_model=config["agent"]["functions_model"],
|
|
api_base=config["agent"]["api_base"],
|
|
stablediffusion_api_base=config["agent"]["stablediffusion_api_base"],
|
|
stablediffusion_model=config["agent"]["stablediffusion_model"],
|
|
)
|
|
# remove bot ID from the message content
|
|
message.content = message.content.replace(f"<@{client.user.id}>", "")
|
|
|
|
conversation_history = localagi.evaluate(
|
|
message.content,
|
|
history,
|
|
subtaskContext=True,
|
|
)
|
|
call(sent_message.edit(content=f"<@{user.id}> {conversation_history[-1]['content']}"))
|
|
|
|
|
|
def run_localagi_message(message, loop):
|
|
def call(thing):
|
|
return asyncio.run_coroutine_threadsafe(thing,loop).result()
|
|
sent_message = call(message.channel.send(f"⚙️ LocalAGI starts"))
|
|
|
|
user = message.author
|
|
def action_callback(name, parameters):
|
|
call(sent_message.edit(content=f"⚙️ Calling function '{name}' with {parameters}"))
|
|
def reasoning_callback(name, reasoning):
|
|
call(sent_message.edit(content=f"🤔 I'm thinking... '{reasoning}' (calling '{name}'), please wait.."))
|
|
|
|
localagi = LocalAGI(
|
|
agent_actions=agent_actions,
|
|
llm_model=config["agent"]["llm_model"],
|
|
tts_model=config["agent"]["tts_model"],
|
|
action_callback=action_callback,
|
|
reasoning_callback=reasoning_callback,
|
|
tts_api_base=config["agent"]["tts_api_base"],
|
|
functions_model=config["agent"]["functions_model"],
|
|
api_base=config["agent"]["api_base"],
|
|
stablediffusion_api_base=config["agent"]["stablediffusion_api_base"],
|
|
stablediffusion_model=config["agent"]["stablediffusion_model"],
|
|
)
|
|
# remove bot ID from the message content
|
|
message.content = message.content.replace(f"<@{client.user.id}>", "")
|
|
|
|
conversation_history = localagi.evaluate(
|
|
message.content,
|
|
[],
|
|
subtaskContext=True,
|
|
)
|
|
call(sent_message.edit(content=f"<@{user.id}> {conversation_history[-1]['content']}"))
|
|
|
|
def run_localagi(interaction, prompt, loop):
|
|
|
|
def call(thing):
|
|
return asyncio.run_coroutine_threadsafe(thing,loop).result()
|
|
|
|
user = interaction.user
|
|
embed = discord.Embed(
|
|
description=f"<@{user.id}> wants to chat! 🤖💬",
|
|
color=discord.Color.green(),
|
|
)
|
|
embed.add_field(name=user.name, value=prompt)
|
|
|
|
call(interaction.response.send_message(embed=embed))
|
|
response = call(interaction.original_response())
|
|
|
|
# create the thread
|
|
thread = call(response.create_thread(
|
|
name=prompt,
|
|
slowmode_delay=1,
|
|
reason="gpt-bot",
|
|
auto_archive_duration=60,
|
|
))
|
|
thread.typing()
|
|
|
|
sent_message = call(thread.send(f"⚙️ LocalAGI starts"))
|
|
messages = []
|
|
def action_callback(name, parameters):
|
|
call(sent_message.edit(content=f"⚙️ Calling function '{name}' with {parameters}"))
|
|
def reasoning_callback(name, reasoning):
|
|
call(sent_message.edit(content=f"🤔 I'm thinking... '{reasoning}' (calling '{name}'), please wait.."))
|
|
|
|
localagi = LocalAGI(
|
|
agent_actions=agent_actions,
|
|
llm_model=config["agent"]["llm_model"],
|
|
tts_model=config["agent"]["tts_model"],
|
|
action_callback=action_callback,
|
|
reasoning_callback=reasoning_callback,
|
|
tts_api_base=config["agent"]["tts_api_base"],
|
|
functions_model=config["agent"]["functions_model"],
|
|
api_base=config["agent"]["api_base"],
|
|
stablediffusion_api_base=config["agent"]["stablediffusion_api_base"],
|
|
stablediffusion_model=config["agent"]["stablediffusion_model"],
|
|
)
|
|
# remove bot ID from the message content
|
|
prompt = prompt.replace(f"<@{client.user.id}>", "")
|
|
|
|
conversation_history = localagi.evaluate(
|
|
prompt,
|
|
messages,
|
|
subtaskContext=True,
|
|
)
|
|
call(sent_message.edit(content=f"<@{user.id}> {conversation_history[-1]['content']}"))
|
|
|
|
@client.tree.command()
|
|
@app_commands.describe(prompt="Ask me anything!")
|
|
async def localai(interaction: discord.Interaction, prompt: str):
|
|
loop = asyncio.get_running_loop()
|
|
agent.loop = loop
|
|
agent.channel = interaction.channel
|
|
threading.Thread(target=run_localagi, args=[interaction, prompt,loop]).start()
|
|
|
|
# https://github.com/openai/gpt-discord-bot/blob/1161634a59c6fb642e58edb4f4fa1a46d2883d3b/src/utils.py#L15
|
|
def discord_message_to_message(message):
|
|
if (
|
|
message.type == discord.MessageType.thread_starter_message
|
|
and message.reference.cached_message
|
|
and len(message.reference.cached_message.embeds) > 0
|
|
and len(message.reference.cached_message.embeds[0].fields) > 0
|
|
):
|
|
field = message.reference.cached_message.embeds[0].fields[0]
|
|
if field.value:
|
|
return { "role": "user", "content": field.value }
|
|
else:
|
|
if message.content:
|
|
return { "role": "user", "content": message.content }
|
|
return None
|
|
|
|
@client.event
|
|
async def on_message(message):
|
|
# ignore messages from the bot
|
|
if message.author == client.user:
|
|
return
|
|
loop = asyncio.get_running_loop()
|
|
agent.loop = loop
|
|
# ignore messages not in a thread
|
|
channel = message.channel
|
|
agent.channel = channel
|
|
if not isinstance(channel, discord.Thread) and client.user.mentioned_in(message):
|
|
threading.Thread(target=run_localagi_message, args=[message,loop]).start()
|
|
return
|
|
if not isinstance(channel, discord.Thread):
|
|
return
|
|
# ignore threads not created by the bot
|
|
thread = channel
|
|
if thread.owner_id != client.user.id:
|
|
return
|
|
|
|
if thread.message_count > 5:
|
|
# too many messages, no longer going to reply
|
|
await close_thread(thread=thread)
|
|
return
|
|
|
|
channel_messages = [
|
|
discord_message_to_message(message)
|
|
async for message in thread.history(limit=5)
|
|
]
|
|
channel_messages = [x for x in channel_messages if x is not None]
|
|
channel_messages.reverse()
|
|
threading.Thread(target=run_localagi_thread_history, args=[channel_messages[:-1],message,thread,loop]).start()
|
|
|
|
client.run(DISCORD_API_KEY)
|