0.3.9.7.a4
- Fixed permissions module not affecting certain features - Added clear `[MOD]` labels to moderator-only commands for clarity
This commit is contained in:
parent
4f0e000c93
commit
21f6150842
2
bot.py
2
bot.py
@ -9,7 +9,7 @@ from modules.common.boot_notice import post_boot_notice
|
||||
|
||||
# Version consists of:
|
||||
# Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesn’t trigger auto update)
|
||||
VERSION = "0.3.9.7.a3"
|
||||
VERSION = "0.3.9.7.a4"
|
||||
|
||||
# ---------- Env loading ----------
|
||||
|
||||
|
@ -4,12 +4,11 @@ import time
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from modules.common.settings import cfg # ENV-first config helper
|
||||
|
||||
from mod_perms import require_mod_ctx # <- use project mod perms
|
||||
|
||||
def now() -> float:
|
||||
return time.time()
|
||||
|
||||
|
||||
class AutoVCCog(commands.Cog):
|
||||
"""
|
||||
Auto-VC:
|
||||
@ -20,8 +19,8 @@ class AutoVCCog(commands.Cog):
|
||||
|
||||
Admin commands:
|
||||
/avc_status -> show current state
|
||||
/avc_cleanup_now -> run a cleanup/renumber pass now
|
||||
/avc_renumber -> renumber without deleting
|
||||
/avc_cleanup_now -> [MOD] run a cleanup/renumber pass now
|
||||
/avc_renumber -> [MOD] renumber without deleting
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
@ -39,6 +38,7 @@ class AutoVCCog(commands.Cog):
|
||||
self.empty_since: dict[int, float] = {} # channel_id -> ts when became empty
|
||||
self._vc_cooldowns: dict[int, float] = {} # user_id -> ts last created (anti-spam)
|
||||
self._create_lock = asyncio.Lock()
|
||||
self._ops_lock = asyncio.Lock() # serialize admin ops vs sweeper
|
||||
|
||||
# Background sweeper
|
||||
self._task = asyncio.create_task(self._sweeper())
|
||||
@ -143,8 +143,10 @@ class AutoVCCog(commands.Cog):
|
||||
await self.bot.wait_until_ready()
|
||||
while not self.bot.is_closed():
|
||||
try:
|
||||
for guild in self.bot.guilds:
|
||||
await self._cleanup_pass(guild)
|
||||
# Serialize with admin ops
|
||||
async with self._ops_lock:
|
||||
for guild in self.bot.guilds:
|
||||
await self._cleanup_pass(guild)
|
||||
except Exception as e:
|
||||
print("[auto_vc] sweeper loop error:", repr(e))
|
||||
await asyncio.sleep(30)
|
||||
@ -224,7 +226,7 @@ class AutoVCCog(commands.Cog):
|
||||
# ------------- admin commands -------------
|
||||
|
||||
@commands.hybrid_command(name="avc_status", description="Show Auto-VC status for this guild")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
@commands.guild_only()
|
||||
async def avc_status(self, ctx: commands.Context):
|
||||
g = ctx.guild
|
||||
recs = sorted(self._vc_records(g.id), key=lambda r: r.get('created_ts', 0))
|
||||
@ -245,18 +247,26 @@ class AutoVCCog(commands.Cog):
|
||||
msg = "Auto-VC status:\n" + "\n".join(lines) if lines else "No Auto-VC rooms tracked."
|
||||
await ctx.reply(msg)
|
||||
|
||||
@commands.hybrid_command(name="avc_cleanup_now", description="Run an immediate cleanup pass (delete idle rooms & renumber)")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
@commands.hybrid_command(name="avc_cleanup_now", description="[MOD] Run an immediate cleanup pass (delete idle rooms & renumber)")
|
||||
@commands.guild_only()
|
||||
async def avc_cleanup_now(self, ctx: commands.Context):
|
||||
await self._cleanup_pass(ctx.guild)
|
||||
await ctx.reply("Cleanup pass complete.")
|
||||
if not await require_mod_ctx(ctx, "This command is restricted to moderators."):
|
||||
return
|
||||
# serialize vs sweeper and other admin ops
|
||||
async with self._ops_lock:
|
||||
await self._cleanup_pass(ctx.guild)
|
||||
await self._log(ctx.guild, f"🧹 Cleanup pass invoked by {ctx.author.mention}")
|
||||
await ctx.reply("Cleanup pass complete.", ephemeral=hasattr(ctx, "interaction") and ctx.interaction is not None)
|
||||
|
||||
@commands.hybrid_command(name="avc_renumber", description="Force a renumber of tracked rooms")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
@commands.hybrid_command(name="avc_renumber", description="[MOD] Force a renumber of tracked rooms")
|
||||
@commands.guild_only()
|
||||
async def avc_renumber(self, ctx: commands.Context):
|
||||
await self._renumber(ctx.guild)
|
||||
await ctx.reply("Renumbered.")
|
||||
|
||||
if not await require_mod_ctx(ctx, "This command is restricted to moderators."):
|
||||
return
|
||||
async with self._ops_lock:
|
||||
await self._renumber(ctx.guild)
|
||||
await self._log(ctx.guild, f"🔢 Renumber invoked by {ctx.author.mention}")
|
||||
await ctx.reply("Renumbered.", ephemeral=hasattr(ctx, "interaction") and ctx.interaction is not None)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(AutoVCCog(bot))
|
||||
|
@ -396,7 +396,7 @@ class NickNudgeCog(commands.Cog):
|
||||
|
||||
# ---------- Mod commands to manipulate nickname reviews ----------
|
||||
|
||||
@app_commands.command(name="clear_nick_reviews", description="Delete all PENDING nickname review records for this server.")
|
||||
@app_commands.command(name="clear_nick_reviews", description="[MOD] Delete all PENDING nickname review records for this server.")
|
||||
async def clear_nick_reviews(self, interaction: discord.Interaction):
|
||||
"""Moderator-only. Clears all 'pending' entries in data_manager['nick_reviews'] for this guild."""
|
||||
# Must be used in a guild
|
||||
@ -437,7 +437,7 @@ class NickNudgeCog(commands.Cog):
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
@app_commands.command(name="recreate_nick_reviews", description="Scan and recreate any missing pending nickname reviews for this server.")
|
||||
@app_commands.command(name="recreate_nick_reviews", description="[MOD] Scan and recreate any missing pending nickname reviews for this server.")
|
||||
async def recreate_nick_reviews(self, interaction: discord.Interaction):
|
||||
"""Moderator-only bulk fixer for 'grey checkmark' users (claimed but no pending review)."""
|
||||
if not interaction.guild:
|
||||
@ -486,7 +486,7 @@ class NickNudgeCog(commands.Cog):
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
@app_commands.command(name="recreate", description="Recreate a missing pending nickname review for one user.")
|
||||
@app_commands.command(name="recreate", description="[MOD] Recreate a missing pending nickname review for one user.")
|
||||
@app_commands.describe(user="Member to recreate review for")
|
||||
async def recreate_nick_review(self, interaction: discord.Interaction, user: discord.Member):
|
||||
"""Moderator-only single-user fixer."""
|
||||
|
@ -196,7 +196,7 @@ class PirateCardsCog(commands.Cog):
|
||||
await self.refresh_card_for_account(guild, new_account)
|
||||
|
||||
# -------- command (mod-gated via require_mod_ctx) --------
|
||||
@commands.hybrid_command(name="pirate_cards_rebuild", description="Rebuild pirate cards for all known pirates")
|
||||
@commands.hybrid_command(name="pirate_cards_rebuild", description="[MOD] Rebuild pirate cards for all known pirates")
|
||||
async def pirate_cards_rebuild(self, ctx: commands.Context):
|
||||
if not await require_mod_ctx(ctx, "This command is restricted to moderators."):
|
||||
return
|
||||
|
@ -538,7 +538,7 @@ class PirateReportCog(commands.Cog):
|
||||
return await interaction.response.send_message("Use this in a server.", ephemeral=True)
|
||||
await interaction.response.send_modal(ReportModal(self))
|
||||
|
||||
@app_commands.command(name="edit_pirate", description="Edit a pirate entry (opens a form)")
|
||||
@app_commands.command(name="edit_pirate", description="[MOD] Edit a pirate entry (opens a form)")
|
||||
async def edit_pirate(self, interaction: discord.Interaction):
|
||||
if not await require_mod_interaction(interaction):
|
||||
return
|
||||
|
@ -211,7 +211,7 @@ class PiratesListCog(commands.Cog):
|
||||
dm.remove("pirates_list_posts", lambda r, mid=m.id: r.get("message_id") == mid)
|
||||
|
||||
# Manual refresh command (hybrid: works as /pirates_list_refresh and !pirates_list_refresh)
|
||||
@commands.hybrid_command(name="pirates_list_refresh", description="Rebuild the compact pirates list")
|
||||
@commands.hybrid_command(name="pirates_list_refresh", description="[MOD] Rebuild the compact pirates list")
|
||||
@commands.cooldown(1, 10, commands.BucketType.guild) # tiny anti-spam
|
||||
async def pirates_list_refresh(self, ctx: commands.Context):
|
||||
if not await require_mod_ctx(ctx, "This command is restricted to moderators."):
|
||||
|
@ -91,7 +91,7 @@ class PowerActionsCog(commands.Cog):
|
||||
|
||||
power = app_commands.Group(name="power", description="Administrative power actions (mod-only)")
|
||||
|
||||
@power.command(name="restart", description="Restart the bot (mod-only). Provide a descriptive reason.")
|
||||
@power.command(name="restart", description="[MOD] Restart the bot. Provide a descriptive reason.")
|
||||
@app_commands.describe(reason="Explain why a restart is necessary (be specific).")
|
||||
async def restart(self, interaction: discord.Interaction, reason: str):
|
||||
# Mods only
|
||||
|
@ -309,7 +309,7 @@ class StatusRotatorCog(commands.Cog):
|
||||
if not ver:
|
||||
return None
|
||||
# Occasionally include version (kept as its own generator for randomness)
|
||||
return f"Running {ver}"
|
||||
return f"Running v{ver}"
|
||||
|
||||
# ============== setup() ==============
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user