Added experimental features related to self-startup, updates fetching, and simple status messages
This commit is contained in:
		
							parent
							
								
									40f4e6e499
								
							
						
					
					
						commit
						25b4e88a4b
					
				
							
								
								
									
										92
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								bot.py
									
									
									
									
									
								
							@ -6,6 +6,8 @@ from dotenv import load_dotenv
 | 
				
			|||||||
from configparser import ConfigParser
 | 
					from configparser import ConfigParser
 | 
				
			||||||
from data_manager import DataManager
 | 
					from data_manager import DataManager
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
 | 
					import os, asyncio, xml.etree.ElementTree as ET
 | 
				
			||||||
 | 
					import aiohttp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Env & config loading ----------
 | 
					# ---------- Env & config loading ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,12 +37,10 @@ def _overlay_env_into_config(cfg: ConfigParser):
 | 
				
			|||||||
        if not k.startswith('SHAI_'):
 | 
					        if not k.startswith('SHAI_'):
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
        key = k[5:].lower()  # drop 'SHAI_' prefix
 | 
					        key = k[5:].lower()  # drop 'SHAI_' prefix
 | 
				
			||||||
        # normalize common aliases
 | 
					 | 
				
			||||||
        if key == 'data':
 | 
					        if key == 'data':
 | 
				
			||||||
            key = 'data_file'
 | 
					            key = 'data_file'
 | 
				
			||||||
        d[key] = str(v)
 | 
					        d[key] = str(v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # If neither env nor file provided data_file, set a safe default
 | 
					 | 
				
			||||||
    if not d.get('data_file', '').strip():
 | 
					    if not d.get('data_file', '').strip():
 | 
				
			||||||
        d['data_file'] = '/data/data.json'
 | 
					        d['data_file'] = '/data/data.json'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,25 +92,100 @@ async def _guild_selfcheck(g: discord.Guild, cfg):
 | 
				
			|||||||
            if not getattr(p, perm, False):
 | 
					            if not getattr(p, perm, False):
 | 
				
			||||||
                problems.append(f"Missing permission on #{ch.name}: {perm}")
 | 
					                problems.append(f"Missing permission on #{ch.name}: {perm}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Mod channel (for report moderation)
 | 
					 | 
				
			||||||
    _need_channel('mod_channel_id', 'read_messages', 'send_messages', 'add_reactions', 'read_message_history')
 | 
					    _need_channel('mod_channel_id', 'read_messages', 'send_messages', 'add_reactions', 'read_message_history')
 | 
				
			||||||
    # Modlog channel
 | 
					 | 
				
			||||||
    _need_channel('modlog_channel_id', 'read_messages', 'send_messages')
 | 
					    _need_channel('modlog_channel_id', 'read_messages', 'send_messages')
 | 
				
			||||||
    # Pirates list channel
 | 
					 | 
				
			||||||
    _need_channel('pirates_list_channel_id', 'read_messages', 'send_messages')
 | 
					    _need_channel('pirates_list_channel_id', 'read_messages', 'send_messages')
 | 
				
			||||||
    # Auto-VC category/trigger are handled inside the cog
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if problems:
 | 
					    if problems:
 | 
				
			||||||
        print(f"[SelfCheck:{g.name}]")
 | 
					        print(f"[SelfCheck:{g.name}]")
 | 
				
			||||||
        for p in problems:
 | 
					        for p in problems:
 | 
				
			||||||
            print(" -", p)
 | 
					            print(" -", p)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def _fetch_latest_from_rss(url: str):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        timeout = aiohttp.ClientTimeout(total=8)
 | 
				
			||||||
 | 
					        async with aiohttp.ClientSession(timeout=timeout) as sess:
 | 
				
			||||||
 | 
					            async with sess.get(url) as resp:
 | 
				
			||||||
 | 
					                if resp.status != 200:
 | 
				
			||||||
 | 
					                    return None, None
 | 
				
			||||||
 | 
					                text = await resp.text()
 | 
				
			||||||
 | 
					        # Gitea RSS structure: <rss><channel><item>…</item></channel></rss>
 | 
				
			||||||
 | 
					        root = ET.fromstring(text)
 | 
				
			||||||
 | 
					        item = root.find('./channel/item')
 | 
				
			||||||
 | 
					        if item is None:
 | 
				
			||||||
 | 
					            return None, None
 | 
				
			||||||
 | 
					        title = (item.findtext('title') or '').strip()
 | 
				
			||||||
 | 
					        link = (item.findtext('link') or '').strip()
 | 
				
			||||||
 | 
					        # Try to extract short sha from link tail if it's a commit URL
 | 
				
			||||||
 | 
					        sha = None
 | 
				
			||||||
 | 
					        if '/commit/' in link:
 | 
				
			||||||
 | 
					            sha = link.rsplit('/commit/', 1)[-1][:7]
 | 
				
			||||||
 | 
					        # Many Gitea feeds put the commit subject in <title>
 | 
				
			||||||
 | 
					        subject = title if title else None
 | 
				
			||||||
 | 
					        return subject, sha
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        return None, None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ---------- boot notice ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def _post_boot_notice():
 | 
				
			||||||
 | 
					    # 1) try build files
 | 
				
			||||||
 | 
					    commit = None
 | 
				
			||||||
 | 
					    subject = None
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        with open("/app/.build_commit", "r") as f:
 | 
				
			||||||
 | 
					            commit = f.read().strip()
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        with open("/app/.build_subject", "r") as f:
 | 
				
			||||||
 | 
					            subject = f.read().strip()
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 2) env fallback
 | 
				
			||||||
 | 
					    if not commit:
 | 
				
			||||||
 | 
					        commit = os.getenv("SHAI_BUILD_COMMIT", "").strip() or None
 | 
				
			||||||
 | 
					    if not subject:
 | 
				
			||||||
 | 
					        subject = os.getenv("SHAI_BUILD_SUBJECT", "").strip() or None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 3) RSS fallback (optional, set SHAI_GIT_RSS to enable; default to your repo)
 | 
				
			||||||
 | 
					    if (not commit or not subject):
 | 
				
			||||||
 | 
					        rss_url = os.getenv("SHAI_GIT_RSS", "https://git.rolfsvaag.no/frarol96/shaiwatcher.rss").strip()
 | 
				
			||||||
 | 
					        if rss_url:
 | 
				
			||||||
 | 
					            sub2, sha2 = await _fetch_latest_from_rss(rss_url)
 | 
				
			||||||
 | 
					            subject = subject or sub2
 | 
				
			||||||
 | 
					            commit = commit or sha2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg = "Self-update and reboot successful!"
 | 
				
			||||||
 | 
					    if subject and len(subject) > 5:
 | 
				
			||||||
 | 
					        msg += f" — {subject}"
 | 
				
			||||||
 | 
					    if commit:
 | 
				
			||||||
 | 
					        msg += f" (`{commit}`)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ch_id_raw = bot.config['DEFAULT'].get('modlog_channel_id', '')
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        ch_id = int(ch_id_raw) if ch_id_raw else 0
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        ch_id = 0
 | 
				
			||||||
 | 
					    if not ch_id:
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    for g in bot.guilds:
 | 
				
			||||||
 | 
					        ch = g.get_channel(ch_id)
 | 
				
			||||||
 | 
					        if ch:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                await ch.send(msg)
 | 
				
			||||||
 | 
					            except Exception:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ---------- events ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@bot.event
 | 
					@bot.event
 | 
				
			||||||
async def on_ready():
 | 
					async def on_ready():
 | 
				
			||||||
    print(f"Logged in as {bot.user} (ID: {bot.user.id})")
 | 
					    print(f"Logged in as {bot.user} (ID: {bot.user.id})")
 | 
				
			||||||
    print("[Intents] members:", bot.intents.members, "/ message_content:", bot.intents.message_content, "/ voice_states:", bot.intents.voice_states)
 | 
					    print("[Intents] members:", bot.intents.members, "/ message_content:", bot.intents.message_content, "/ voice_states:", bot.intents.voice_states)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Per-guild permission sanity checks (console log)
 | 
					 | 
				
			||||||
    await asyncio.gather(*[_guild_selfcheck(g, bot.config['DEFAULT']) for g in bot.guilds])
 | 
					    await asyncio.gather(*[_guild_selfcheck(g, bot.config['DEFAULT']) for g in bot.guilds])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Slash command sync
 | 
					    # Slash command sync
 | 
				
			||||||
@ -130,6 +205,9 @@ async def on_ready():
 | 
				
			|||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        print("[Slash] Sync failed:", repr(e))
 | 
					        print("[Slash] Sync failed:", repr(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Boot notice in modlog
 | 
				
			||||||
 | 
					    await _post_boot_notice()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Auto-discover extensions ----------
 | 
					# ---------- Auto-discover extensions ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
modules_path = pathlib.Path(__file__).parent / 'modules'
 | 
					modules_path = pathlib.Path(__file__).parent / 'modules'
 | 
				
			||||||
 | 
				
			|||||||
@ -172,7 +172,7 @@ class NickNudgeCog(commands.Cog):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @commands.Cog.listener()
 | 
					    @commands.Cog.listener()
 | 
				
			||||||
    async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
 | 
					    async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
 | 
				
			||||||
        # 1) Handle DM nudge confirmations (user reacts ✅ in DM)
 | 
					        # 1) Handle DM nudge confirmations (user reacts with an accept in DM)
 | 
				
			||||||
        if payload.guild_id is None and is_accept(payload.emoji) and payload.user_id != self.bot.user.id:
 | 
					        if payload.guild_id is None and is_accept(payload.emoji) and payload.user_id != self.bot.user.id:
 | 
				
			||||||
            entry = next((m for m in self.bot.data_manager.get('nick_dm_map') if m['message_id'] == payload.message_id), None)
 | 
					            entry = next((m for m in self.bot.data_manager.get('nick_dm_map') if m['message_id'] == payload.message_id), None)
 | 
				
			||||||
            if not entry:
 | 
					            if not entry:
 | 
				
			||||||
@ -181,20 +181,21 @@ class NickNudgeCog(commands.Cog):
 | 
				
			|||||||
            member = guild.get_member(entry['user_id']) if guild else None
 | 
					            member = guild.get_member(entry['user_id']) if guild else None
 | 
				
			||||||
            if not member:
 | 
					            if not member:
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
            # Treat as a claim: mark pending + create review
 | 
					            # Treat as a claim: mark pending (idempotent) and open review only on first transition
 | 
				
			||||||
            dm = self.bot.data_manager
 | 
					            dm = self.bot.data_manager
 | 
				
			||||||
            if member.id not in dm.get('agreed_nickname'):
 | 
					            if member.id not in dm.get('agreed_nickname'):
 | 
				
			||||||
                dm.add('agreed_nickname', int(member.id))
 | 
					                dm.add('agreed_nickname', int(member.id))
 | 
				
			||||||
            # Always (re)mark pending & clear verified
 | 
					 | 
				
			||||||
            dm.remove('nick_verified', lambda x: x == member.id)
 | 
					            dm.remove('nick_verified', lambda x: x == member.id)
 | 
				
			||||||
 | 
					            newly_pending = False
 | 
				
			||||||
            if member.id not in dm.get('nick_claim_pending'):
 | 
					            if member.id not in dm.get('nick_claim_pending'):
 | 
				
			||||||
                dm.add('nick_claim_pending', int(member.id))
 | 
					                dm.add('nick_claim_pending', int(member.id))
 | 
				
			||||||
 | 
					                newly_pending = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Create review
 | 
					            if newly_pending:
 | 
				
			||||||
            try:
 | 
					                try:
 | 
				
			||||||
                await self.start_nick_review(guild, member, source="nick_same")
 | 
					                    await self.start_nick_review(guild, member, source="nick_same")
 | 
				
			||||||
            except Exception:
 | 
					                except Exception:
 | 
				
			||||||
                pass
 | 
					                    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Clean map entry
 | 
					            # Clean map entry
 | 
				
			||||||
            self.bot.data_manager.remove('nick_dm_map', lambda m: m['message_id'] == payload.message_id)
 | 
					            self.bot.data_manager.remove('nick_dm_map', lambda m: m['message_id'] == payload.message_id)
 | 
				
			||||||
 | 
				
			|||||||
@ -3,17 +3,15 @@ from discord.ext import commands
 | 
				
			|||||||
from modules.common.emoji_accept import is_accept
 | 
					from modules.common.emoji_accept import is_accept
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CHECKMARK = '✅'
 | 
					CHECKMARK = '✅'
 | 
				
			||||||
ACCEPT = {CHECKMARK, '🫡'}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReactionRoleCog(commands.Cog):
 | 
					class ReactionRoleCog(commands.Cog):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Records agreements and manages Full Access.
 | 
					    Records agreements and manages Full Access.
 | 
				
			||||||
    Now integrates nickname *pending/verified* flow:
 | 
					    Nickname flow:
 | 
				
			||||||
      • Nickname reaction add -> mark agreed + pending, open review via NickNudgeCog
 | 
					      • Add accept on nickname message -> mark agreed + pending (idempotent) and open ONE review
 | 
				
			||||||
      • Nickname reaction remove -> clear agreed/pending/verified and re-check access
 | 
					      • Remove accept on nickname message -> clear only if user has no accept reactions left
 | 
				
			||||||
      • /nick_same -> same as claim (no reaction required)
 | 
					    Full Access: granted when Rules ✅ + RoE ✅ + Nickname *claimed* (pending or verified).
 | 
				
			||||||
    Full Access: granted when Rules ✅ + RoE ✅ + Nickname **claimed (pending or verified)**.
 | 
					    Revoked when any of the three is missing.
 | 
				
			||||||
    Revoked only when nickname becomes unclaimed (rejected or unreacted) or when Rules/RoE are missing.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, bot):
 | 
					    def __init__(self, bot):
 | 
				
			||||||
@ -82,6 +80,22 @@ class ReactionRoleCog(commands.Cog):
 | 
				
			|||||||
                return None
 | 
					                return None
 | 
				
			||||||
        return m
 | 
					        return m
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _user_has_any_accept(self, guild: discord.Guild, channel_id: int, message_id: int, user_id: int) -> bool:
 | 
				
			||||||
 | 
					        """Return True if the user still has at least one 'accept' reaction on the message."""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            ch = guild.get_channel(channel_id)
 | 
				
			||||||
 | 
					            if not ch:
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					            msg = await ch.fetch_message(message_id)
 | 
				
			||||||
 | 
					            for rxn in msg.reactions:
 | 
				
			||||||
 | 
					                if is_accept(rxn.emoji):
 | 
				
			||||||
 | 
					                    async for u in rxn.users(limit=None):
 | 
				
			||||||
 | 
					                        if u.id == user_id:
 | 
				
			||||||
 | 
					                            return True
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # ---- commands (hybrid = prefix + slash) ----
 | 
					    # ---- commands (hybrid = prefix + slash) ----
 | 
				
			||||||
    @commands.hybrid_command(name='nick_same', description='Claim that your global display name matches your in-game name (triggers mod review)')
 | 
					    @commands.hybrid_command(name='nick_same', description='Claim that your global display name matches your in-game name (triggers mod review)')
 | 
				
			||||||
    async def nick_same(self, ctx: commands.Context):
 | 
					    async def nick_same(self, ctx: commands.Context):
 | 
				
			||||||
@ -94,16 +108,19 @@ class ReactionRoleCog(commands.Cog):
 | 
				
			|||||||
            dm.add('agreed_nickname', int(member.id))
 | 
					            dm.add('agreed_nickname', int(member.id))
 | 
				
			||||||
        # Mark pending (clear verified if present)
 | 
					        # Mark pending (clear verified if present)
 | 
				
			||||||
        dm.remove('nick_verified', lambda x: x == member.id)
 | 
					        dm.remove('nick_verified', lambda x: x == member.id)
 | 
				
			||||||
 | 
					        newly_pending = False
 | 
				
			||||||
        if member.id not in dm.get('nick_claim_pending'):
 | 
					        if member.id not in dm.get('nick_claim_pending'):
 | 
				
			||||||
            dm.add('nick_claim_pending', int(member.id))
 | 
					            dm.add('nick_claim_pending', int(member.id))
 | 
				
			||||||
 | 
					            newly_pending = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Open/refresh a review with NickNudge
 | 
					        # Open/refresh a review with NickNudge (only on first transition to pending)
 | 
				
			||||||
        nn = self.bot.get_cog('NickNudgeCog')
 | 
					        if newly_pending:
 | 
				
			||||||
        if nn and hasattr(nn, 'start_nick_review'):
 | 
					            nn = self.bot.get_cog('NickNudgeCog')
 | 
				
			||||||
            try:
 | 
					            if nn and hasattr(nn, 'start_nick_review'):
 | 
				
			||||||
                await nn.start_nick_review(ctx.guild, member, source="nick_same")
 | 
					                try:
 | 
				
			||||||
            except Exception:
 | 
					                    await nn.start_nick_review(ctx.guild, member, source="nick_same")
 | 
				
			||||||
                pass
 | 
					                except Exception:
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await self.maybe_apply_full_access(member)
 | 
					        await self.maybe_apply_full_access(member)
 | 
				
			||||||
        await ctx.reply("Thanks — your nickname claim was sent for moderator review.", ephemeral=True)
 | 
					        await ctx.reply("Thanks — your nickname claim was sent for moderator review.", ephemeral=True)
 | 
				
			||||||
@ -135,20 +152,23 @@ class ReactionRoleCog(commands.Cog):
 | 
				
			|||||||
                    dm.add('agreed_engagement', int(member.id))
 | 
					                    dm.add('agreed_engagement', int(member.id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            elif payload.message_id == self.nick_msg_id:
 | 
					            elif payload.message_id == self.nick_msg_id:
 | 
				
			||||||
                # Claim nickname via reaction -> mark agreed + pending, clear verified
 | 
					                # Claim nickname via reaction -> mark agreed + pending (idempotent)
 | 
				
			||||||
 | 
					                newly_pending = False
 | 
				
			||||||
                if member.id not in dm.get('agreed_nickname'):
 | 
					                if member.id not in dm.get('agreed_nickname'):
 | 
				
			||||||
                    dm.add('agreed_nickname', int(member.id))
 | 
					                    dm.add('agreed_nickname', int(member.id))
 | 
				
			||||||
                dm.remove('nick_verified', lambda x: x == member.id)
 | 
					                dm.remove('nick_verified', lambda x: x == member.id)
 | 
				
			||||||
                if member.id not in dm.get('nick_claim_pending'):
 | 
					                if member.id not in dm.get('nick_claim_pending'):
 | 
				
			||||||
                    dm.add('nick_claim_pending', int(member.id))
 | 
					                    dm.add('nick_claim_pending', int(member.id))
 | 
				
			||||||
 | 
					                    newly_pending = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Kick off a review in mod channel
 | 
					                # Only open a review when we just transitioned to pending
 | 
				
			||||||
                nn = self.bot.get_cog('NickNudgeCog')
 | 
					                if newly_pending:
 | 
				
			||||||
                if nn and hasattr(nn, 'start_nick_review'):
 | 
					                    nn = self.bot.get_cog('NickNudgeCog')
 | 
				
			||||||
                    try:
 | 
					                    if nn and hasattr(nn, 'start_nick_review'):
 | 
				
			||||||
                        await nn.start_nick_review(guild, member, source="claim")
 | 
					                        try:
 | 
				
			||||||
                    except Exception:
 | 
					                            await nn.start_nick_review(guild, member, source="claim")
 | 
				
			||||||
                        pass
 | 
					                        except Exception:
 | 
				
			||||||
 | 
					                            pass
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
@ -180,10 +200,14 @@ class ReactionRoleCog(commands.Cog):
 | 
				
			|||||||
                    await member.remove_roles(role, reason="Engagement un-ticked")
 | 
					                    await member.remove_roles(role, reason="Engagement un-ticked")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            elif payload.message_id == self.nick_msg_id:
 | 
					            elif payload.message_id == self.nick_msg_id:
 | 
				
			||||||
                # Un-claim nickname -> clear everything related
 | 
					                # Clear only if the user has NO accept reactions left on the message
 | 
				
			||||||
                dm.remove('agreed_nickname', lambda x: x == member.id)
 | 
					                still_has_accept = await self._user_has_any_accept(
 | 
				
			||||||
                dm.remove('nick_claim_pending', lambda x: x == member.id)
 | 
					                    guild, payload.channel_id, payload.message_id, member.id
 | 
				
			||||||
                dm.remove('nick_verified', lambda x: x == member.id)
 | 
					                )
 | 
				
			||||||
 | 
					                if not still_has_accept:
 | 
				
			||||||
 | 
					                    dm.remove('agreed_nickname', lambda x: x == member.id)
 | 
				
			||||||
 | 
					                    dm.remove('nick_claim_pending', lambda x: x == member.id)
 | 
				
			||||||
 | 
					                    dm.remove('nick_verified', lambda x: x == member.id)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user