Files
Breehavior-Monitor/cogs/reactions.py
AJ Isaacs 97e5738a2f fix: address review feedback for ReactionCog
- Use time.monotonic() at reaction time instead of stale message-receive timestamp
- Add excluded_channels config and filtering
- Truncate message content to 500 chars in pick_reaction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:28:20 -05:00

77 lines
2.3 KiB
Python

import asyncio
import logging
import random
import time
import discord
from discord.ext import commands
logger = logging.getLogger("bcs.reactions")
class ReactionCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
# Per-channel timestamp of last reaction
self._last_reaction: dict[int, float] = {}
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author.bot or not message.guild:
return
cfg = self.bot.config.get("reactions", {})
if not cfg.get("enabled", False):
return
# Skip empty messages
if not message.content or not message.content.strip():
return
# Channel exclusion
excluded = cfg.get("excluded_channels", [])
if excluded:
ch_name = getattr(message.channel, "name", "")
if message.channel.id in excluded or ch_name in excluded:
return
# RNG gate
chance = cfg.get("chance", 0.15)
if random.random() > chance:
return
# Per-channel cooldown
ch_id = message.channel.id
cooldown = cfg.get("cooldown_seconds", 45)
now = time.monotonic()
if now - self._last_reaction.get(ch_id, 0) < cooldown:
return
# Fire and forget so we don't block anything
asyncio.create_task(self._try_react(message, ch_id))
async def _try_react(self, message: discord.Message, ch_id: int):
try:
emoji = await self.bot.llm.pick_reaction(
message.content, message.channel.name,
)
if not emoji:
return
await message.add_reaction(emoji)
self._last_reaction[ch_id] = time.monotonic()
logger.info(
"Reacted %s to %s in #%s: %s",
emoji, message.author.display_name,
message.channel.name, message.content[:60],
)
except discord.HTTPException as e:
# Invalid emoji or missing permissions — silently skip
logger.debug("Reaction failed: %s", e)
except Exception:
logger.exception("Unexpected reaction error")
async def setup(bot: commands.Bot):
await bot.add_cog(ReactionCog(bot))