diff options
author | 2020-11-22 13:07:46 +0300 | |
---|---|---|
committer | 2020-11-22 13:07:46 +0300 | |
commit | 06f9c48e136644a15044b84a0b25f1b6feba0e09 (patch) | |
tree | 945dc9d21eeb1174062acb056260f59c8f6894c7 | |
parent | Merge branch 'master' into voicechannel-mute (diff) |
Add VC Mute Functionality
Adds and calls a function to force a voice channel member to sync
permissions. See #1160 for why this is necessary.
Signed-off-by: Hassan Abouelela <[email protected]>
-rw-r--r-- | bot/exts/moderation/silence.py | 66 |
1 files changed, 64 insertions, 2 deletions
diff --git a/bot/exts/moderation/silence.py b/bot/exts/moderation/silence.py index 314aa946e..415ab19a6 100644 --- a/bot/exts/moderation/silence.py +++ b/bot/exts/moderation/silence.py @@ -6,7 +6,7 @@ from operator import attrgetter from typing import Optional, Union from async_rediscache import RedisCache -from discord import TextChannel, VoiceChannel +from discord import HTTPException, Member, PermissionOverwrite, TextChannel, VoiceChannel from discord.ext import commands, tasks from discord.ext.commands import Context @@ -93,9 +93,13 @@ class Silence(commands.Cog): await self.bot.wait_until_guild_available() guild = self.bot.get_guild(Guild.id) + self._verified_msg_role = guild.get_role(Roles.verified) self._verified_voice_role = guild.get_role(Roles.voice_verified) + self._helper_role = guild.get_role(Roles.helpers) + self._mod_alerts_channel = self.bot.get_channel(Channels.mod_alerts) + self.notifier = SilenceNotifier(self.bot.get_channel(Channels.mod_log)) await self._reschedule() @@ -226,12 +230,70 @@ class Silence(commands.Cog): else: overwrite.update(speak=False) await channel.set_permissions(self._verified_voice_role, overwrite=overwrite) + try: + await self._force_voice_silence(channel) + except HTTPException: + # TODO: Relay partial failure to invocation channel + pass await self.previous_overwrites.set(channel.id, json.dumps(prev_overwrites)) return True - async def _schedule_unsilence(self, ctx: Context, channel: typing.Union[TextChannel, VoiceChannel], + async def _force_voice_silence(self, channel: VoiceChannel, member: Optional[Member] = None) -> None: + """ + Move all non-staff members from `channel` to a temporary channel and back to force toggle role mute. + + If `member` is passed, the mute only occurs to that member. + Permission modification has to happen before this function. + + Raises `discord.HTTPException` if the task fails. + """ + # Obtain temporary channel + afk_channel = channel.guild.afk_channel + if afk_channel is None: + try: + overwrites = { + channel.guild.default_role: PermissionOverwrite(speak=False, connect=False, view_channel=False) + } + afk_channel = await channel.guild.create_voice_channel("mute-temp", overwrites=overwrites) + log.info(f"Failed to get afk-channel, created temporary channel #{afk_channel} ({afk_channel.id})") + + # Schedule channel deletion in case function errors out + self.scheduler.schedule_later(30, afk_channel.id, + afk_channel.delete(reason="Deleting temp mute channel.")) + + except HTTPException as e: + log.warning("Failed to create temporary mute channel.", exc_info=e) + raise e + + # Handle member picking logic + if member is not None: + members = [member] + else: + members = channel.members + + # Move all members to temporary channel and back + for member in members: + # Skip staff + if self._helper_role in member.roles: + continue + + try: + await member.move_to(afk_channel, reason="Muting member.") + log.debug(f"Moved {member.name} to afk channel.") + + await member.move_to(channel, reason="Muting member.") + log.debug(f"Moved {member.name} to original voice channel.") + + except HTTPException as e: + log.warning(f"Failed to move {member.name} while muting, falling back to kick.", exc_info=e) + try: + await member.move_to(None, reason="Forcing member mute.") + except HTTPException: + pass + + async def _schedule_unsilence(self, ctx: Context, channel: Union[TextChannel, VoiceChannel], duration: Optional[int]) -> None: """Schedule `ctx.channel` to be unsilenced if `duration` is not None.""" if duration is None: |