Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: discord bot #1401

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 101 additions & 31 deletions extensions/discord/bot.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,138 @@
import os
import re

import logging
import aiohttp
import discord
import requests
from discord.ext import commands
import dotenv

dotenv.load_dotenv()

# Replace 'YOUR_BOT_TOKEN' with your bot's token
# Enable logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Bot configuration
TOKEN = os.getenv("DISCORD_TOKEN")
PREFIX = '@DocsGPT'
BASE_API_URL = 'http://localhost:7091'
PREFIX = '!' # Command prefix
BASE_API_URL = os.getenv("API_BASE", "https://gptcloud.arc53.com")
API_URL = BASE_API_URL + "/api/answer"
API_KEY = os.getenv("API_KEY")

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(command_prefix=PREFIX, intents=intents)

# Store conversation history per user
conversation_histories = {}

def escape_markdown(text):
"""Escapes Discord markdown characters."""
escape_chars = r'\*_$$$$()~>#+-=|{}.!'
return re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', text)

def split_string(input_str):
"""Splits the input string to detect bot mentions."""
pattern = r'^<@!?{0}>\s*'.format(bot.user.id)
match = re.match(pattern, input_str)
if match:
content = input_str[match.end():].strip()
return str(bot.user.id), content
return None, input_str


@bot.event
async def on_ready():
print(f'{bot.user.name} has connected to Discord!')


async def fetch_answer(question):
data = {
'sender': 'discord',
'question': question,
'history': ''
async def generate_answer(question, messages, conversation_id):
"""Generates an answer using the external API."""
payload = {
"question": question,
"api_key": API_KEY,
"history": messages,
"conversation_id": conversation_id
}
headers = {"Content-Type": "application/json",
"Accept": "application/json"}
response = requests.post(BASE_API_URL + '/api/answer', json=data, headers=headers)
if response.status_code == 200:
return response.json()['answer']
return 'Sorry, I could not fetch the answer.'

headers = {
"Content-Type": "application/json; charset=utf-8"
}
timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(API_URL, json=payload, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
conversation_id = data.get("conversation_id")
answer = data.get("answer", "Sorry, I couldn't find an answer.")
return {"answer": answer, "conversation_id": conversation_id}
else:
return {"answer": "Sorry, I couldn't find an answer.", "conversation_id": None}

@bot.command(name="start")
async def start(ctx):
"""Handles the /start command."""
await ctx.send(f"Hi {ctx.author.mention}! How can I assist you today?")

@bot.command(name="custom_help")
async def custom_help_command(ctx):
"""Handles the /custom_help command."""
help_text = (
"Here are the available commands:\n"
"`!start` - Begin a new conversation with the bot\n"
"`!help` - Display this help message\n\n"
"You can also mention me or send a direct message to ask a question!"
)
await ctx.send(help_text)

@bot.event
async def on_message(message):
if message.author == bot.user:
return

content = message.content.strip()
prefix, content = split_string(content)
if prefix is None:
return

part_prefix = str(bot.user.id)
if part_prefix == prefix:
answer = await fetch_answer(content)
await message.channel.send(answer)

# Process commands first
await bot.process_commands(message)


bot.run(TOKEN)
# Check if the message is in a DM channel
if isinstance(message.channel, discord.DMChannel):
content = message.content.strip()
else:
# In guild channels, check if the message mentions the bot at the start
content = message.content.strip()
prefix, content = split_string(content)
if prefix is None:
return
part_prefix = str(bot.user.id)
if part_prefix != prefix:
return # Bot not mentioned at the start, so do not process

# Now process the message
user_id = message.author.id
if user_id not in conversation_histories:
conversation_histories[user_id] = {
"history": [],
"conversation_id": None
}

conversation = conversation_histories[user_id]
conversation["history"].append({"prompt": content})

# Generate the answer
response_doc = await generate_answer(
content,
conversation["history"],
conversation["conversation_id"]
)
answer = response_doc["answer"]
conversation_id = response_doc["conversation_id"]

# Escape markdown characters
answer = escape_markdown(answer)

await message.channel.send(answer)

conversation["history"][-1]["response"] = answer
conversation["conversation_id"] = conversation_id

# Keep conversation history to last 10 exchanges
conversation["history"] = conversation["history"][-10:]

bot.run(TOKEN)
Loading