"""Reaction handler with percentage-based triggers, cooldowns, and mention reactions.""" import asyncio import logging from typing import Optional import discord from discord.ext import commands logger = logging.getLogger(__name__) # ─── Configuration ─────────────────────────────────────────────────────────── REACTION_CHANCE = 0.05 # 5% chance to react to a message REACTION_COOLDOWN = 60 # seconds per channel # ─── Reaction Handler ──────────────────────────────────────────────────────── class ReactionHandler: """Handles message reactions with cooldowns and mention triggers.""" def __init__(self, bot: commands.Bot) -> None: self.bot = bot self._cooldowns: dict[int, float] = {} # channel_id -> next_available self._reaction_emojis: list[discord.Emoji] = [] async def handle_reactions(self, message: discord.Message) -> None: """Check if a message should be reacted to and handle reactions.""" channel_id = message.channel.id # Enforce cooldown now = discord.utils.utcnow().timestamp() if channel_id in self._cooldowns: if now < self._cooldowns[channel_id]: return # Check if message mentions the bot if self._is_mention(message): await self._handle_mention(message) else: # Random chance to react if self._should_react(): await self._add_reaction(message) # Enforce cooldown self._cooldown(channel_id) def _is_mention(self, message: discord.Message) -> bool: """Check if the message mentions the bot.""" return f"<@{self.bot.user.id}>" in message.content or f"<@!{self.bot.user.id}>" in message.content def _should_react(self) -> bool: """Determine if we should react (random chance).""" import random return random.random() < REACTION_CHANCE async def _handle_mention(self, message: discord.Message) -> None: """Handle a mention — react with a random emoji.""" if self._reaction_emojis: emoji = self._reaction_emojis[0] try: await message.add_reaction(emoji) except discord.Forbidden: pass async def _add_reaction(self, message: discord.Message) -> None: """Add a reaction to a message.""" if self._reaction_emojis: emoji = self._reaction_emojis[0] try: await message.add_reaction(emoji) except discord.Forbidden: pass def _cooldown(self, channel_id: int) -> None: """Set cooldown for a channel.""" self._cooldowns[channel_id] = discord.utils.utcnow().timestamp() + REACTION_COOLDOWN def register_emoji(self, emoji: discord.Emoji) -> None: """Register an emoji for reactions.""" self._reaction_emojis.append(emoji) # ─── Bot Integration ───────────────────────────────────────────────────────── async def setup(bot: commands.Bot) -> None: """Register the reaction handler with the bot.""" handler = ReactionHandler(bot) @bot.event async def on_message(message: discord.Message) -> None: """Handle reactions on messages.""" if message.author == bot.user: return if message.author.bot: return await handler.handle_reactions(message) def register_emoji(bot: commands.Bot, emoji: discord.Emoji) -> None: """Register an emoji for reactions.""" handler = ReactionHandler.__new__(ReactionHandler) handler.bot = bot handler._reaction_emojis = [] bot._reaction_handler = handler