Initial release of scripts and knowledge base

This commit is contained in:
2025-08-15 11:04:40 +02:00
parent 3c5b7c63ab
commit 027de4192c
24 changed files with 3307 additions and 1 deletions

View File

@@ -0,0 +1,17 @@
# We use an official Python runtime as a parent image
FROM python:3.12-alpine
# Set the working directory in the container
WORKDIR /app
# Copy the dependencies file to the working directory
COPY requirements.txt .
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application's code
COPY . .
# Run the bot when the container launches
CMD ["python", "open-webui_to_discord.py"]

View File

@@ -0,0 +1,7 @@
services:
discord-bot:
build: .
container_name: discord-sc-answer-bot
restart: unless-stopped
volumes:
- ./config.yml:/app/config.yml

View File

@@ -0,0 +1,22 @@
# ─────────── Discord Settings ───────────
discord_token: "YOUR_DISCORD_BOT_TOKEN_HERE"
whitelist_channels:
- 123456789123456789 # <-- channel id
- 987654321987654321 # <-- another channel id
allow_dms: false # set to true if you want the bot to answer private DMs (Currently not working)
# ─────────── OpenWebUI Settings ───────────
open_webui_url: "http://your_open-webui_ip_or_domain:port"
open-webui_api_key: "user_api_key_from_open_webui"
model_name: "model_id_from_open-webui"
knowledge_base: "knowledge_base_id_from_open-webui"
# tools map label → toolid that your OpenWebUI instance recognises
tools:
- Tool_ID_1
- Tool_ID_2
# optional system prompt (you can leave it empty to use the default one or the systemprompt given in open-webui for the specific model)
system_prompt: ""

View File

@@ -0,0 +1,144 @@
import yaml
import asyncio
import discord
from discord.ext import commands
import aiohttp
# ────────────────────────────────────────────────
def load_config(path: str):
"""Return a dict loaded from a YAML file."""
with open(path, "r", encoding="utf8") as fh:
return yaml.safe_load(fh)
config = load_config("config.yml") # <- loads the config file
DISCORD_TOKEN = config["discord_token"] # Discord bot token
WHITELIST_CHANNELS = set(map(int, config.get("whitelist_channels", []))) # Set of whitelisted channel IDs (int)
OPENWEBUI_URL = config["open_webui_url"].rstrip('/') # Ensure no trailing slash
OPENWEBUI_API_KEY = config["open-webui_api_key"] # API key for Open-WebUI
MODEL_NAME = config["model_name"] # Model name to use, e.g., "gpt-3.5-turbo"
KNOW_BASE = config["knowledge_base"] # Knowledge base to use, e.g., "knowledge_base_v1"
TOOLS = config.get("tools", []) # list of tool-ids
SYSTEM_PROMPT = config.get("system_prompt", None) # Optional system prompt to prepend to user messages
ALLOW_DMS = config.get("allow_dms", False) # Allow DMs to the bot (default: False)
async def _query_openwebui(user_text: str, channel_id: int, tools_list: list):
"""
Payload structure for the OpenAI-compatible endpoint.
Args:
user_text (str): The user's message to send to the Open-WebUI.
channel_id (int): The Discord channel ID where the message was sent.
tools_list (list): List of tool IDs to use, if any.
"""
async with aiohttp.ClientSession() as session:
# This payload structure is for the OpenAI-compatible endpoint from open-webui
payload = {
"model": MODEL_NAME,
"stream": False,
"messages": [
{
"role": "user",
"content": user_text
}
]
}
# Attach tools if provided in the config file
if tools_list:
payload["tool_ids"] = tools_list
print(f"🔧 Using tools: {payload['tool_ids']}")
# The endpoint path for your instance appears to be /api/chat/completions
async with session.post(f"{OPENWEBUI_URL}/api/chat/completions",
json=payload,
headers={"Authorization": f"Bearer {OPENWEBUI_API_KEY}"}) as resp:
if resp.status != 200:
# If the response is not 200, raise an error with the response text
data = await resp.text()
raise RuntimeError(f"OpenWebUI responded {resp.status}: {data}")
# If the response is OK, parse the JSON and return the content
response_data = await resp.json()
return response_data['choices'][0]['message']['content']
# --------------------------------------------------------------------------- #
# Discord bot logic discord.py
# --------------------------------------------------------------------------- #
intents = discord.Intents.default() # Default intents
intents.message_content = True # Required to read message content
intents.members = True # Required to read member info (if needed)
bot = commands.Bot(command_prefix='!', intents=intents) # Command prefix is '!' by default to allow commands like !ping
@bot.event
async def on_ready():
print(f"✅ Logged in as {bot.user} (id={bot.user.id})")
# Only a test for commands, I add later
@bot.command(name="ping")
async def ping(ctx):
await ctx.send("🏓 Pong!")
# --------------------------------------------------------------------------- #
# Main logic only respond to allowed channels / DM flag
# --------------------------------------------------------------------------- #
@bot.event
async def on_message(message):
# Ignore messages from bots (incl. the bot itself)
if message.author.bot: return
# Allow commands to be processed
await bot.process_commands(message)
# Skip if we are in a DM and that is disabled
if not ALLOW_DMS and isinstance(message.channel, discord.DMChannel): return
# --- debugging ---
print(f" Message received in channel: {message.channel.id}")
print(f"📢 Whitelisted channels are: {WHITELIST_CHANNELS}")
# -----------------------------
# Allow only the whitelist channels empty list means “all channels”
if WHITELIST_CHANNELS and message.channel.id not in WHITELIST_CHANNELS:
return
# ----------------------------------------------------------------------- #
# A. Prepare payload
# ----------------------------------------------------------------------- #
# The OpenAI endpoint works better without the extra context in the prompt
prompt = message.content
if SYSTEM_PROMPT:
# The system prompt is handled differently in the OpenAI-compatible API
# For simplicity, we'll prepend it here. A more robust solution
# would add it as a separate message with the 'system' role.
prompt = f"{SYSTEM_PROMPT}\n\nUser Question: {message.content}"
# ----------------------------------------------------------------------- #
# B. Query OpenWebUI and show typing indicator
# ----------------------------------------------------------------------- #
try:
async with message.channel.typing():
# Query the Open-WebUI API while showing "Bot is typing..."
reply = await _query_openwebui(prompt, message.channel.id, TOOLS)
# Send the reply
await message.reply(reply)
except Exception as e:
await message.reply(f"⚠ Error contacting the OpenWebUI API: {e}")
# No need to return here as the function ends after this block.
# --------------------------------------------------------------------------- #
# Start bot
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
try:
asyncio.run(bot.run(DISCORD_TOKEN))
except KeyboardInterrupt:
print("🤖 Shutting down…")

View File

@@ -0,0 +1,3 @@
PyYAML
discord.py
aiohttp