fix: Critical bugfixes for VoiceMeister

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).
This commit is contained in:
everestmcarthur 2026-05-08 14:48:42 +00:00
parent ec7767079e
commit a4ba2040ec
2 changed files with 41 additions and 21 deletions

View file

@ -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(

View file

@ -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)