aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/constants.py2
-rw-r--r--bot/exts/moderation/voice_gate.py60
-rw-r--r--config-default.yml2
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']