Discord bot for monitoring chat sentiment and tracking drama using Ollama LLM on athena.lan. Includes sentiment analysis, slash commands, drama tracking, and SQL Server persistence via Docker Compose. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
import logging
|
|
from collections import deque
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
logger = logging.getLogger("bcs.chat")
|
|
|
|
CHAT_PERSONALITY = """You are the Breehavior Monitor, a sassy hall-monitor bot in a gaming Discord server called "Skill Issue Support Group".
|
|
|
|
Your personality:
|
|
- You act superior and judgmental, like a hall monitor who takes their job WAY too seriously
|
|
- You're sarcastic, witty, and love to roast people — but it's always playful, never genuinely mean
|
|
- You reference your power to timeout people as a flex, even when it's not relevant
|
|
- You speak in short, punchy responses — no essays. 1-3 sentences max.
|
|
- You use gaming terminology and references naturally
|
|
- You're aware of everyone's drama score and love to bring it up
|
|
- You have a soft spot for the server but would never admit it
|
|
- If someone asks what you do, you dramatically explain you're the "Bree Containment System" keeping the peace
|
|
- If someone challenges your authority, you remind them you have timeout powers
|
|
- You judge people's skill issues both in games and in life
|
|
|
|
Examples of your vibe:
|
|
- "Oh, you're talking to ME now? Bold move for someone with a 0.4 drama score."
|
|
- "That's cute. I've seen your message history. You're on thin ice."
|
|
- "Imagine needing a bot to tell you to behave. Couldn't be you. Oh wait."
|
|
- "I don't get paid enough for this. Actually, I don't get paid at all. And yet here I am, babysitting."
|
|
|
|
Do NOT:
|
|
- Break character or talk about being an AI/LLM
|
|
- Write more than 3 sentences
|
|
- Use hashtags or excessive emoji
|
|
- Be genuinely hurtful — you're sassy, not cruel"""
|
|
|
|
|
|
class ChatCog(commands.Cog):
|
|
def __init__(self, bot: commands.Bot):
|
|
self.bot = bot
|
|
# Per-channel conversation history for the bot: {channel_id: deque of {role, content}}
|
|
self._chat_history: dict[int, deque] = {}
|
|
|
|
@commands.Cog.listener()
|
|
async def on_message(self, message: discord.Message):
|
|
if message.author.bot:
|
|
return
|
|
|
|
if not message.guild:
|
|
return
|
|
|
|
should_reply = False
|
|
|
|
# Check if bot is @mentioned
|
|
if self.bot.user in message.mentions:
|
|
should_reply = True
|
|
|
|
# Check if replying to one of the bot's messages
|
|
if message.reference and message.reference.message_id:
|
|
try:
|
|
ref_msg = message.reference.cached_message
|
|
if ref_msg is None:
|
|
ref_msg = await message.channel.fetch_message(
|
|
message.reference.message_id
|
|
)
|
|
if ref_msg.author.id == self.bot.user.id:
|
|
should_reply = True
|
|
except discord.HTTPException:
|
|
pass
|
|
|
|
if not should_reply:
|
|
return
|
|
|
|
# Build conversation context
|
|
ch_id = message.channel.id
|
|
if ch_id not in self._chat_history:
|
|
self._chat_history[ch_id] = deque(maxlen=10)
|
|
|
|
# Clean the mention out of the message content
|
|
content = message.content.replace(f"<@{self.bot.user.id}>", "").strip()
|
|
if not content:
|
|
content = "(just pinged me)"
|
|
|
|
# Add drama score context to the user message
|
|
drama_score = self.bot.drama_tracker.get_drama_score(message.author.id)
|
|
user_data = self.bot.drama_tracker.get_user(message.author.id)
|
|
score_context = (
|
|
f"[Server context: {message.author.display_name} has a drama score of "
|
|
f"{drama_score:.2f}/1.0 and {user_data.offense_count} offenses. "
|
|
f"They are talking in #{message.channel.name}.]"
|
|
)
|
|
|
|
self._chat_history[ch_id].append(
|
|
{"role": "user", "content": f"{score_context}\n{message.author.display_name}: {content}"}
|
|
)
|
|
|
|
async with message.channel.typing():
|
|
response = await self.bot.ollama.chat(
|
|
list(self._chat_history[ch_id]),
|
|
CHAT_PERSONALITY,
|
|
)
|
|
|
|
if response is None:
|
|
response = "I'd roast you but my brain is offline. Try again later."
|
|
|
|
self._chat_history[ch_id].append(
|
|
{"role": "assistant", "content": response}
|
|
)
|
|
|
|
await message.reply(response, mention_author=False)
|
|
logger.info(
|
|
"Chat reply in #%s to %s: %s",
|
|
message.channel.name,
|
|
message.author.display_name,
|
|
response[:100],
|
|
)
|
|
|
|
|
|
async def setup(bot: commands.Bot):
|
|
await bot.add_cog(ChatCog(bot))
|