From 72222397740da0828c3fce4313cda2dc602a2553 Mon Sep 17 00:00:00 2001 From: Franz Rolfsvaag Date: Sun, 10 Aug 2025 20:36:13 +0200 Subject: [PATCH] . --- bot.py | 3 ++ modules/pirate_report/pirate_report.py | 19 +++++++-- modules/pirates_list/pirates_list.py | 59 +++++++------------------- modules/spicepay/spicepay.py | 2 +- modules/user_cards/user_cards.py | 2 +- 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/bot.py b/bot.py index 927b27d..ce2357d 100644 --- a/bot.py +++ b/bot.py @@ -136,6 +136,9 @@ modules_path = pathlib.Path(__file__).parent / 'modules' extensions = [] for folder in modules_path.iterdir(): if folder.is_dir(): + # skip non-cog helpers under modules/common + if folder.name == 'common': + continue for file in folder.glob('*.py'): if file.name == '__init__.py': continue diff --git a/modules/pirate_report/pirate_report.py b/modules/pirate_report/pirate_report.py index 560d93a..13cf025 100644 --- a/modules/pirate_report/pirate_report.py +++ b/modules/pirate_report/pirate_report.py @@ -572,14 +572,27 @@ async def setup(bot): cog = PirateReportCog(bot) await bot.add_cog(cog) - # Register commands either globally or to a specific guild if configured home_gid = cfg(bot).int('home_guild_id', 0) + guild_obj = discord.Object(id=home_gid) if home_gid else None + + def _rm(name: str): + try: + bot.tree.remove_command(name, guild=guild_obj) + except Exception: + try: + bot.tree.remove_command(name, guild=None) + except Exception: + pass + + # make re-loads idempotent + for name in ("report", "edit_pirate", "encounter"): + _rm(name) + if home_gid: - guild_obj = discord.Object(id=home_gid) bot.tree.add_command(cog.report, guild=guild_obj) bot.tree.add_command(cog.edit_pirate, guild=guild_obj) bot.tree.add_command(cog.encounter, guild=guild_obj) else: bot.tree.add_command(cog.report) bot.tree.add_command(cog.edit_pirate) - bot.tree.add_command(cog.encounter) + bot.tree.add_command(cog.encounter) \ No newline at end of file diff --git a/modules/pirates_list/pirates_list.py b/modules/pirates_list/pirates_list.py index cec95a6..a6feac0 100644 --- a/modules/pirates_list/pirates_list.py +++ b/modules/pirates_list/pirates_list.py @@ -2,7 +2,8 @@ import asyncio import discord from discord.ext import commands -from mod_perms import require_mod_ctx # ctx-aware mod gate +from mod_perms import require_mod_ctx +from modules.common.settings import cfg as _cfg class PiratesListCog(commands.Cog): @@ -16,29 +17,17 @@ class PiratesListCog(commands.Cog): Posts are chunked to stay <2000 chars and previous posts are deleted on refresh. """ - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot - cfg = bot.config["DEFAULT"] - self.list_channel_id = int(cfg["pirates_list_channel_id"]) - try: - self.group_threshold = int(cfg.get("threat_group_threshold", "3")) - except Exception: - self.group_threshold = 3 - try: - self.min_samples = int(cfg.get("threat_min_samples_for_stats", "3")) - except Exception: - self.min_samples = 3 + + c = _cfg(bot) + self.list_channel_id = c.int("pirates_list_channel_id") + self.group_threshold = c.int("threat_group_threshold", 3) + self.min_samples = c.int("threat_min_samples_for_stats", 3) # serialize refreshes per guild - self._locks = {} - self._no_mentions = discord.AllowedMentions.none() - - def _lock_for(self, guild_id: int): - import asyncio - self._locks.setdefault(guild_id, asyncio.Lock()) - return self._locks[guild_id] - - # send settings: never ping on posted content + self._locks: dict[int, asyncio.Lock] = {} + # never ping on posted content self._no_mentions = discord.AllowedMentions.none() # ----------------- utils ----------------- @@ -119,30 +108,19 @@ class PiratesListCog(commands.Cog): async def refresh_list(self, guild: discord.Guild): """Edit list messages in place; only send extra messages when we need more chunks (new pirates).""" - # ---- serialize per guild ---- - lock = getattr(self, "_locks", {}).get(guild.id) - if lock is None: - # tiny fallback if you didn't add _lock_for() - import asyncio as _asyncio - if not hasattr(self, "_locks"): - self._locks = {} - self._locks[guild.id] = _asyncio.Lock() - lock = self._locks[guild.id] - - async with lock: + async with self._lock_for(guild.id): channel = guild.get_channel(self.list_channel_id) if not channel: print("[pirates_list] list channel not found:", self.list_channel_id) return dm = self.bot.data_manager - allow = getattr(self, "_no_mentions", discord.AllowedMentions.none()) + allow = self._no_mentions # ---- load & prune existing posts for this guild/channel ---- records = [r for r in dm.get("pirates_list_posts") - if r.get("guild_id") == guild.id and r.get("channel_id") == self.list_channel_id] + if r.get("guild_id") == guild.id and r.get("channel_id") == self.list_channel_id] - # fetch messages (drop any that vanished) msgs, kept_records = [], [] for r in records: try: @@ -150,11 +128,8 @@ class PiratesListCog(commands.Cog): msgs.append(m) kept_records.append(r) except Exception: - # prune dead record dm.remove("pirates_list_posts", lambda x, mid=r["message_id"]: x.get("message_id") == mid) - records = kept_records # only live ones, in stored order - # ---- build fresh, sorted contents ---- pirates = sorted( dm.get("pirates"), @@ -168,13 +143,11 @@ class PiratesListCog(commands.Cog): if not pirates: placeholder = "_No verified pirates yet._" if msgs: - # edit first, delete the rest if msgs[0].content != placeholder: try: await msgs[0].edit(content=placeholder, allowed_mentions=allow) except Exception as e: print("[pirates_list] edit placeholder failed:", repr(e)) - # remove extra posts/records for extra in msgs[1:]: try: await extra.delete() @@ -214,7 +187,7 @@ class PiratesListCog(commands.Cog): except Exception as e: print("[pirates_list] edit block failed:", repr(e)) - # ---- if we need *more* messages (usually after adding a pirate), send them ---- + # ---- send additional messages if needed ---- if len(chunks) > len(msgs): for i in range(len(msgs), len(chunks)): try: @@ -227,7 +200,7 @@ class PiratesListCog(commands.Cog): except Exception as e: print("[pirates_list] send block failed:", repr(e)) - # ---- if we need fewer messages (e.g., pirate removed), delete extras ---- + # ---- delete extras if fewer chunks now ---- elif len(chunks) < len(msgs): extras = msgs[len(chunks):] for m in extras: @@ -252,5 +225,5 @@ class PiratesListCog(commands.Cog): await ctx.reply("Pirates list refreshed.", ephemeral=is_slash) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(PiratesListCog(bot)) diff --git a/modules/spicepay/spicepay.py b/modules/spicepay/spicepay.py index 1e84407..25bfc56 100644 --- a/modules/spicepay/spicepay.py +++ b/modules/spicepay/spicepay.py @@ -561,7 +561,7 @@ class _StartView(discord.ui.View): class SpicePayCog(commands.Cog): def __init__(self, bot): self.bot = bot - self-sessions: Dict[tuple, Dict] = {} + self.sessions: Dict[tuple, Dict] = {} r = cfg(bot) def _f(key, default): diff --git a/modules/user_cards/user_cards.py b/modules/user_cards/user_cards.py index 1e3d786..b6d009b 100644 --- a/modules/user_cards/user_cards.py +++ b/modules/user_cards/user_cards.py @@ -443,7 +443,7 @@ class UserCardsCog(commands.Cog): await self._log(member.guild, f"📝 User joined: {member.mention} (ID: {member.id})") await self.refresh_card(member) - @commands.Cog.listener()) + @commands.Cog.listener() async def on_member_update(self, before: discord.Member, after: discord.Member): if before.nick != after.nick or before.roles != after.roles: await self.refresh_card(after)