# modules/usage/usage_stats.py from __future__ import annotations from discord.ext import commands import discord COUNTER_KEY_PREFIX = "cmd::" def _key_from_app(cmd: discord.app_commands.Command) -> str: # app command qualified_name is "group sub" or "name" name = getattr(cmd, "qualified_name", None) or getattr(cmd, "name", "unknown") return f"{COUNTER_KEY_PREFIX}{name}" def _key_from_ctx(ctx: commands.Context) -> str: # prefix/hybrid qualified_name is "group sub" or "name" c = getattr(ctx, "command", None) name = getattr(c, "qualified_name", None) or getattr(c, "name", "unknown") return f"{COUNTER_KEY_PREFIX}{name}" class UsageStatsCog(commands.Cog): """Lightweight command run/attempt counters persisted in DataManager.""" def __init__(self, bot: commands.Bot): self.bot = bot print("[usage] UsageStatsCog init") # ---- successful completions ---- @commands.Cog.listener() async def on_app_command_completion(self, interaction: discord.Interaction, command: discord.app_commands.Command): dm = getattr(self.bot, "data_manager", None) if not dm: print("[usage] skip app (no data_manager)") return try: key = _key_from_app(command) newv = dm.incr_counter(key, 1) print(f"[usage] app ++ {key} -> {newv}") except Exception as e: print("[usage] incr app failed:", repr(e)) @commands.Cog.listener() async def on_command_completion(self, ctx: commands.Context): dm = getattr(self.bot, "data_manager", None) if not dm: print("[usage] skip prefix (no data_manager)") return try: key = _key_from_ctx(ctx) newv = dm.incr_counter(key, 1) print(f"[usage] px ++ {key} -> {newv}") except Exception as e: print("[usage] incr prefix failed:", repr(e)) # ---- attempts that error (optional but useful while testing) ---- @commands.Cog.listener() async def on_app_command_error(self, interaction: discord.Interaction, error: Exception): # Count attempts (separate key) so you can confirm the listener fires at all. dm = getattr(self.bot, "data_manager", None) cmd = getattr(interaction, "command", None) if dm and isinstance(cmd, discord.app_commands.Command): try: key = _key_from_app(cmd).replace("cmd::", "cmd_attempt::") newv = dm.incr_counter(key, 1) print(f"[usage] app !! {key} -> {newv} ({type(error).__name__})") except Exception as e: print("[usage] app err incr failed:", repr(e)) @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error: Exception): dm = getattr(self.bot, "data_manager", None) if dm and getattr(ctx, "command", None): try: key = _key_from_ctx(ctx).replace("cmd::", "cmd_attempt::") newv = dm.incr_counter(key, 1) print(f"[usage] px !! {key} -> {newv} ({type(error).__name__})") except Exception as e: print("[usage] px err incr failed:", repr(e)) async def setup(bot: commands.Bot): await bot.add_cog(UsageStatsCog(bot)) print("[usage] UsageStatsCog loaded")