Initial commit: Breehavior Monitor Discord bot
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>
This commit is contained in:
118
cogs/chat.py
Normal file
118
cogs/chat.py
Normal file
@@ -0,0 +1,118 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user