diff options
| -rw-r--r-- | bot/constants.py | 2 | ||||
| -rw-r--r-- | bot/exts/moderation/voice_gate.py | 60 | ||||
| -rw-r--r-- | config-default.yml | 2 |
3 files changed, 60 insertions, 4 deletions
diff --git a/bot/constants.py b/bot/constants.py index 4d41f4eb2..66a049851 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -601,7 +601,7 @@ class VoiceGate(metaclass=YAMLGetter): minimum_messages: int bot_message_delete_delay: int minimum_activity_blocks: int - + voice_ping_delete_delay: int class Event(Enum): """ diff --git a/bot/exts/moderation/voice_gate.py b/bot/exts/moderation/voice_gate.py index 93d96693c..427fdfc1a 100644 --- a/bot/exts/moderation/voice_gate.py +++ b/bot/exts/moderation/voice_gate.py @@ -4,8 +4,9 @@ from contextlib import suppress from datetime import datetime, timedelta import discord +from async_rediscache import RedisCache from dateutil import parser -from discord import Colour +from discord import Colour, Member from discord.ext.commands import Cog, Context, command from bot.api import ResponseCodeError @@ -28,12 +29,26 @@ MESSAGE_FIELD_MAP = { "activity_blocks": f"have been active for fewer than {GateConf.minimum_activity_blocks} ten-minute blocks", } +VOICE_PING = ( + "Hello, {}! Wondering why you can't talk in the voice channels? " + "Use the `!voiceverify` command in here to verify. " + "If you don't yet qualify, you'll be told why!" +) + class VoiceGate(Cog): """Voice channels verification management.""" - def __init__(self, bot: Bot): + # RedisCache[t.Union[discord.User.id, discord.Member.id], t.Union[discord.Message.id, bool]] + redis_cache = RedisCache() + + def __init__(self, bot: Bot) -> None: self.bot = bot + self._init_task = self.bot.loop.create_task(self._async_init()) + + async def _async_init(self) -> None: + await self.bot.wait_until_guild_available() + self._voice_verification_channel = self.bot.get_channel(Channels.voice_gate) @property def mod_log(self) -> ModLog: @@ -53,6 +68,14 @@ class VoiceGate(Cog): - You must not be actively banned from using our voice channels - You must have been active for over a certain number of 10-minute blocks """ + + # If user has received a ping in voice_verification, delete the message + if message_id := await self.redis_cache.get(ctx.author.id, None): + with suppress(discord.NotFound): + ping_message = await ctx.channel.fetch_message(message_id) + await ping_message.delete() + await self.redis_cache.set(ctx.author.id, False) + try: data = await self.bot.api_client.get(f"bot/users/{ctx.author.id}/metricity_data") except ResponseCodeError as e: @@ -144,6 +167,10 @@ class VoiceGate(Cog): # When it's bot sent message, delete it after some time if message.author.bot: + # Comparing the message with the voice ping constant + if message.content.endswith(VOICE_PING[-45:]): + log.trace("Message is the voice verification ping. Ignore.") + return with suppress(discord.NotFound): await message.delete(delay=GateConf.bot_message_delete_delay) return @@ -160,6 +187,35 @@ class VoiceGate(Cog): with suppress(discord.NotFound): await message.delete() + @Cog.listener() + async def on_voice_state_update(self, member: Member, *_) -> None: + """Pings a user if they've never joined the voice chat before and aren't verified""" + if member.bot: + log.trace("User is a bot. Ignore.") + return + + in_cache = await self.redis_cache.get(member.id, None) + + # member.voice will return None if the user is not in a voice channel + if in_cache is None and member.voice is not None: + log.trace("User not in cache and is in a voice channel") + verified = any(Roles.voice_verified == role.id for role in member.roles) + if verified: + log.trace("User is verified, add to the cache and ignore") + # redis cache does not accept None, so False is used to signify no message + await self.redis_cache.set(member.id, False) + return + + log.trace("User is unverified. Send ping.") + message = await self._voice_verification_channel.send(VOICE_PING.format(member.mention)) + await self.redis_cache.set(member.id, message.id) + + # Message will try to be deleted after 1 minutes. If it fails, it'll do so silently + await message.delete(delay=GateConf.voice_ping_delete_delay) + else: + log.trace("User is either in the cache or not in a voice channel. Ignore.") + return + async def cog_command_error(self, ctx: Context, error: Exception) -> None: """Check for & ignore any InWhitelistCheckFailure.""" if isinstance(error, InWhitelistCheckFailure): diff --git a/config-default.yml b/config-default.yml index 2afdcd594..7de9faeda 100644 --- a/config-default.yml +++ b/config-default.yml @@ -522,7 +522,7 @@ voice_gate: minimum_messages: 50 # How many messages a user must have to be eligible for voice bot_message_delete_delay: 10 # Seconds before deleting bot's response in Voice Gate minimum_activity_blocks: 3 # Number of 10 minute blocks during which a user must have been active - + voice_ping_delete_delay: 60 # Seconds before deleting the bot's ping to user in Voice Gate config: required_keys: ['bot.token'] |