0.5.1.2.a4

- Minor patch to prevent non-initiated members from claiming crew roles
This commit is contained in:
Franz Rolfsvaag 2025-08-26 00:07:51 +02:00
parent 9b94280e8b
commit ac9953fed6
2 changed files with 30 additions and 1 deletions

2
bot.py
View File

@ -9,7 +9,7 @@ from modules.common.boot_notice import post_boot_notice
# Version consists of: # Version consists of:
# Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesnt trigger auto update) # Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesnt trigger auto update)
VERSION = "0.5.1.2.a3" VERSION = "0.5.1.2.a4"
# ---------- Env loading ---------- # ---------- Env loading ----------
load_dotenv() load_dotenv()

View File

@ -86,6 +86,7 @@ class ReactionRoleCog(commands.Cog):
- Debounced nickname review to avoid duplicates when users add multiple accept emojis. - Debounced nickname review to avoid duplicates when users add multiple accept emojis.
- Fedaykin role is removed when the user unreacts the Fedaykin emoji. - Fedaykin role is removed when the user unreacts the Fedaykin emoji.
- Settings are reloaded dynamically on each event (hot-apply without restart). - Settings are reloaded dynamically on each event (hot-apply without restart).
- NEW: Only users with Full Access may claim/request crew roles.
""" """
def __init__(self, bot): def __init__(self, bot):
@ -262,6 +263,25 @@ class ReactionRoleCog(commands.Cog):
except Exception: except Exception:
return False return False
def _has_full_initiated(self, member: discord.Member) -> bool:
"""User must have Full Access role to claim/request crew roles."""
if not member or not isinstance(member.guild, discord.Guild):
return False
role = member.guild.get_role(self.full_access_role) if self.full_access_role else None
return bool(role and role in member.roles)
async def _remove_reaction_silent(self, guild: discord.Guild, channel_id: int, message_id: int,
emoji: discord.PartialEmoji | discord.Emoji | str, member: discord.Member):
"""Best-effort: remove a reaction without messaging the user."""
try:
ch = guild.get_channel(channel_id)
if not isinstance(ch, (discord.TextChannel, discord.Thread)):
return
msg = await ch.fetch_message(message_id)
await msg.remove_reaction(emoji, member)
except Exception:
pass
async def maybe_apply_full_access(self, member: discord.Member): async def maybe_apply_full_access(self, member: discord.Member):
"""Grant when Rules+RoE+Nickname *claimed*; revoke when any missing.""" """Grant when Rules+RoE+Nickname *claimed*; revoke when any missing."""
guild = member.guild guild = member.guild
@ -426,6 +446,10 @@ class ReactionRoleCog(commands.Cog):
async def _post_fedaykin_card(self, guild: discord.Guild, member: discord.Member, hub_id: int) -> bool: async def _post_fedaykin_card(self, guild: discord.Guild, member: discord.Member, hub_id: int) -> bool:
"""Post the Fedaykin approval card; return True if posted somewhere. While headless, queue pending only.""" """Post the Fedaykin approval card; return True if posted somewhere. While headless, queue pending only."""
# Require Full Access to even request Fedaykin
if not self._has_full_initiated(member):
return False
# If headless or effectively headless (no Head members): queue silently # If headless or effectively headless (no Head members): queue silently
if self._fedaykin_headless or not self._has_fedaykin_head(guild): if self._fedaykin_headless or not self._has_fedaykin_head(guild):
await self._queue_pending(guild, member, reason="headless_runtime") await self._queue_pending(guild, member, reason="headless_runtime")
@ -737,6 +761,11 @@ class ReactionRoleCog(commands.Cog):
# ----- Crew roles hub (custom emoji toggles + Fedaykin request) ----- # ----- Crew roles hub (custom emoji toggles + Fedaykin request) -----
try: try:
if self.crew_msg_id and payload.message_id == self.crew_msg_id and payload.emoji.id: if self.crew_msg_id and payload.message_id == self.crew_msg_id and payload.emoji.id:
# Gate: must have Full Access to claim/request crew roles
if not self._has_full_initiated(member):
await self._remove_reaction_silent(guild, payload.channel_id, payload.message_id, payload.emoji, member)
return
# Harvester / Escort # Harvester / Escort
if payload.emoji.id == self.emoji_harvest_id and self.role_harvest_id: if payload.emoji.id == self.emoji_harvest_id and self.role_harvest_id:
role = guild.get_role(self.role_harvest_id) role = guild.get_role(self.role_harvest_id)