aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/exts/moderation/voice_gate.py112
1 files changed, 111 insertions, 1 deletions
diff --git a/bot/exts/moderation/voice_gate.py b/bot/exts/moderation/voice_gate.py
index 198617857..dae19d49e 100644
--- a/bot/exts/moderation/voice_gate.py
+++ b/bot/exts/moderation/voice_gate.py
@@ -1,6 +1,26 @@
-from discord.ext.commands import Cog
+import logging
+from contextlib import suppress
+from datetime import datetime, timedelta
+import discord
+from dateutil import parser
+
+from discord.ext.commands import Cog, Context, command
+
+from bot.api import ResponseCodeError
from bot.bot import Bot
+from bot.constants import Channels, Roles, VoiceGate as VoiceGateConf, MODERATION_ROLES, Event
+from bot.decorators import has_no_roles, in_whitelist
+from bot.exts.moderation.modlog import ModLog
+
+log = logging.getLogger(__name__)
+
+# Messages for case when user don't meet with requirements
+NOT_ENOUGH_MESSAGES = f"haven't sent at least {VoiceGateConf.minimum_messages} messages"
+NOT_ENOUGH_DAYS_AFTER_VERIFICATION = f"haven't been verified for at least {VoiceGateConf.minimum_days_verified} days"
+VOICE_BANNED = "are voice banned"
+
+FAILED_MESSAGE = """{user} you don't meet with our current requirements to pass Voice Gate. You {reasons}."""
class VoiceGate(Cog):
@@ -9,6 +29,96 @@ class VoiceGate(Cog):
def __init__(self, bot: Bot):
self.bot = bot
+ @property
+ def mod_log(self) -> ModLog:
+ """Get the currently loaded ModLog cog instance."""
+ return self.bot.get_cog("ModLog")
+
+ @command(aliases=('voiceverify', 'vverify', 'voicev', 'vv'))
+ @has_no_roles(Roles.voice_verified)
+ @in_whitelist(channels=(Channels.voice_gate,), redirect=None)
+ async def voice_verify(self, ctx: Context, *_) -> None:
+ """
+ Apply to be able to use voice within the Discord server.
+
+ In order to use voice you must meet all three of the following criteria:
+ - You must have over a certain number of messages within the Discord server
+ - You must have accepted our rules over a certain number of days ago
+ - You must not be actively banned from using our voice channels
+ """
+ try:
+ data = await self.bot.api_client.get(f"bot/users/{ctx.author.id}/metricity_data")
+ except ResponseCodeError as e:
+ if e.status == 404:
+ await ctx.send(f":x: {ctx.author.mention} Unable to find Metricity data about you.")
+ log.info(f"Unable to find Metricity data about {ctx.author} ({ctx.author.id})")
+ else:
+ log.warning(f"Got response code {e.status} while trying to get {ctx.author.id} metricity data.")
+ await ctx.send(f":x: Got unexpected response from site. Please let us know about this.")
+ return
+
+ # Pre-parse this for better code style
+ data["verified_at"] = parser.isoparse(data["verified_at"])
+
+ failed = False
+ failed_reasons = []
+
+ if data["verified_at"] > datetime.utcnow() - timedelta(days=VoiceGateConf.minimum_days_verified):
+ failed_reasons.append(NOT_ENOUGH_DAYS_AFTER_VERIFICATION)
+ failed = True
+ self.bot.stats.incr("voice_gate.failed.verified_at")
+ if data["total_messages"] < VoiceGateConf.minimum_messages:
+ failed_reasons.append(NOT_ENOUGH_MESSAGES)
+ failed = True
+ self.bot.stats.incr("voice_gate.failed.total_messages")
+ if data["voice_banned"]:
+ failed_reasons.append(VOICE_BANNED)
+ failed = True
+ self.bot.stats.incr("voice_gate.failed.voice_banned")
+
+ if failed:
+ if len(failed_reasons) > 1:
+ reasons = f"{', '.join(failed_reasons[:-1])} and {failed_reasons[-1]}"
+ else:
+ reasons = failed_reasons[0]
+
+ await ctx.send(
+ FAILED_MESSAGE.format(
+ user=ctx.author.mention,
+ reasons=reasons
+ )
+ )
+ return
+
+ self.mod_log.ignore(Event.member_update, ctx.author.id)
+ await ctx.author.add_roles(discord.Object(Roles.voice_verified), reason="Voice Gate passed")
+ await ctx.author.send(
+ ":tada: Congratulations! You are now Voice Verified and have access to PyDis Voice Channels."
+ )
+ self.bot.stats.incr("voice_gate.passed")
+
+ @Cog.listener()
+ async def on_message(self, message: discord.Message) -> None:
+ """Delete all non-staff messages from voice gate channel that don't invoke voice verify command."""
+ # Check is channel voice gate
+ if message.channel.id != Channels.voice_gate:
+ return
+
+ # When it's bot sent message, delete it after some time
+ if message.author.bot:
+ with suppress(discord.NotFound):
+ await message.delete(delay=VoiceGateConf.bot_message_delete_delay)
+ return
+
+ # Then check is member moderator+, because we don't want to delete their messages.
+ if any(role.id in MODERATION_ROLES for role in message.author.roles):
+ log.trace(f"Excluding moderator message {message.id} from deletion in #{message.channel}.")
+ return
+
+ self.mod_log.ignore(Event.message_delete, message.id)
+ with suppress(discord.NotFound):
+ await message.delete()
+
def setup(bot: Bot) -> None:
"""Loads the VoiceGate cog."""