From a4ba2040ec06e10d3f19d1091bbe7c804c7cdd90 Mon Sep 17 00:00:00 2001 From: everestmcarthur Date: Fri, 8 May 2026 14:48:42 +0000 Subject: [PATCH] fix: Critical bugfixes for VoiceMeister MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. CRITICAL: Permission overwrites now properly merge instead of replace. set_permissions with keyword args REPLACES the entire overwrite, so Lockβ†’Hide would undo the Lock. Now uses overwrites_for() to fetch existing state and modify only the target field. Affects: Lock, Unlock, Hide, Unhide, Ghost, Reveal (buttons & commands) 2. CRITICAL: Removed broken async generator in any() for game rename check. await in generator expression creates async generator that any() can't consume. Game rename loop now always starts and checks config per-guild. 3. Minor: Fixed double-write in _save_temp_channels. 4. Minor: Removed unused imports (datetime, Set). --- voicemeister/views.py | 26 ++++++++++++++++++++------ voicemeister/voicemeister.py | 36 +++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/voicemeister/views.py b/voicemeister/views.py index 4938da9..e4e22cd 100644 --- a/voicemeister/views.py +++ b/voicemeister/views.py @@ -341,8 +341,10 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.connect = False await vc.set_permissions( - vc.guild.default_role, connect=False, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: locked by {member}", ) await interaction.response.send_message( @@ -356,8 +358,10 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.connect = None await vc.set_permissions( - vc.guild.default_role, connect=None, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: unlocked by {member}", ) await interaction.response.send_message( @@ -371,8 +375,11 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.connect = False + overwrite.view_channel = False await vc.set_permissions( - vc.guild.default_role, connect=False, view_channel=False, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: ghosted by {member}", ) await interaction.response.send_message( @@ -386,8 +393,11 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.connect = None + overwrite.view_channel = None await vc.set_permissions( - vc.guild.default_role, connect=None, view_channel=None, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: revealed by {member}", ) await interaction.response.send_message( @@ -401,8 +411,10 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.view_channel = False await vc.set_permissions( - vc.guild.default_role, view_channel=False, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: hidden by {member}", ) await interaction.response.send_message( @@ -462,8 +474,10 @@ class VoiceMeisterPanel(ui.View): cog, member, vc, owner_id = await _require_owner(interaction) if cog is None: return + overwrite = vc.overwrites_for(vc.guild.default_role) + overwrite.view_channel = None await vc.set_permissions( - vc.guild.default_role, view_channel=None, + vc.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: unhidden by {member}", ) await interaction.response.send_message( diff --git a/voicemeister/voicemeister.py b/voicemeister/voicemeister.py index 4b97f79..f8bc7e0 100644 --- a/voicemeister/voicemeister.py +++ b/voicemeister/voicemeister.py @@ -7,9 +7,8 @@ name templates, game detection, logging, blacklist/whitelist, cooldowns. from __future__ import annotations import asyncio -import datetime import time -from typing import Dict, Optional, Set +from typing import Dict, Optional import discord from redbot.core import Config, commands, checks @@ -64,11 +63,8 @@ class VoiceMeister(commands.Cog): self.bot.add_view(self._panel_view) await self._load_all_guilds() self._bg_tasks.append(asyncio.create_task(self._cleanup_loop())) - if any( - (await self.config.guild_from_id(gid).auto_rename_game()) - for gid in [g.id for g in self.bot.guilds] - ): - self._bg_tasks.append(asyncio.create_task(self._game_rename_loop())) + # Always start the game rename loop β€” it checks per-guild config each cycle + self._bg_tasks.append(asyncio.create_task(self._game_rename_loop())) async def cog_unload(self): for task in self._bg_tasks: @@ -97,14 +93,13 @@ class VoiceMeister(commands.Cog): gid = channel.guild.id guild_map.setdefault(gid, {})[str(ch_id)] = owner_id - # Also handle guilds that might have had all channels removed + # Clear guilds that no longer have temp channels all_guilds = await self.config.all_guilds() for gid in all_guilds: if gid not in guild_map: await self.config.guild_from_id(gid).temp_channels.set({}) - else: - await self.config.guild_from_id(gid).temp_channels.set(guild_map[gid]) + # Save guilds that have temp channels for gid, data in guild_map.items(): await self.config.guild_from_id(gid).temp_channels.set(data) @@ -965,7 +960,9 @@ class VoiceMeister(commands.Cog): vc, _ = await self._get_user_vc(ctx) if not vc: return - await vc.set_permissions(ctx.guild.default_role, connect=False, + overwrite = vc.overwrites_for(ctx.guild.default_role) + overwrite.connect = False + await vc.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: locked by {ctx.author}") await ctx.send(embed=ok_embed("Channel **locked** πŸ”’.")) await self._log_action(ctx.guild, "πŸ”’ Locked", member=ctx.author, channel=vc) @@ -976,7 +973,9 @@ class VoiceMeister(commands.Cog): vc, _ = await self._get_user_vc(ctx) if not vc: return - await vc.set_permissions(ctx.guild.default_role, connect=None, + overwrite = vc.overwrites_for(ctx.guild.default_role) + overwrite.connect = None + await vc.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: unlocked by {ctx.author}") await ctx.send(embed=ok_embed("Channel **unlocked** πŸ”“.")) await self._log_action(ctx.guild, "πŸ”“ Unlocked", member=ctx.author, channel=vc) @@ -987,7 +986,9 @@ class VoiceMeister(commands.Cog): vc, _ = await self._get_user_vc(ctx) if not vc: return - await vc.set_permissions(ctx.guild.default_role, view_channel=False, + overwrite = vc.overwrites_for(ctx.guild.default_role) + overwrite.view_channel = False + await vc.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: hidden by {ctx.author}") await ctx.send(embed=ok_embed("Channel **hidden** πŸ‘€.")) await self._log_action(ctx.guild, "πŸ‘€ Hidden", member=ctx.author, channel=vc) @@ -998,7 +999,9 @@ class VoiceMeister(commands.Cog): vc, _ = await self._get_user_vc(ctx) if not vc: return - await vc.set_permissions(ctx.guild.default_role, view_channel=None, + overwrite = vc.overwrites_for(ctx.guild.default_role) + overwrite.view_channel = None + await vc.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: unhidden by {ctx.author}") await ctx.send(embed=ok_embed("Channel **visible** πŸ‘οΈ.")) await self._log_action(ctx.guild, "πŸ‘οΈ Unhidden", member=ctx.author, channel=vc) @@ -1009,7 +1012,10 @@ class VoiceMeister(commands.Cog): vc, _ = await self._get_user_vc(ctx) if not vc: return - await vc.set_permissions(ctx.guild.default_role, connect=False, view_channel=False, + overwrite = vc.overwrites_for(ctx.guild.default_role) + overwrite.connect = False + overwrite.view_channel = False + await vc.set_permissions(ctx.guild.default_role, overwrite=overwrite, reason=f"VoiceMeister: ghosted by {ctx.author}") await ctx.send(embed=ok_embed("Channel **ghosted** πŸ‘» β€” hidden and locked.")) await self._log_action(ctx.guild, "πŸ‘» Ghosted", member=ctx.author, channel=vc)