aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar kwzrd <[email protected]>2020-08-04 18:13:37 +0200
committerGravatar kwzrd <[email protected]>2020-08-04 18:34:52 +0200
commitf0e0dcc599f974bb563e298199fcf5a76b1cfbe7 (patch)
tree1bec07e2d2247ecab6c8b8334a8062c3829fdaca
parentVerification: define time constants (diff)
Verification: implement `check_users` coroutine
See docstring for details. The coroutine will be registered as a task at a later point.
-rw-r--r--bot/cogs/verification.py60
1 files changed, 59 insertions, 1 deletions
diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py
index 95d92899b..ea4874450 100644
--- a/bot/cogs/verification.py
+++ b/bot/cogs/verification.py
@@ -1,5 +1,8 @@
+import asyncio
import logging
+import typing as t
from contextlib import suppress
+from datetime import datetime, timedelta
import discord
from discord.ext.commands import Cog, Context, command
@@ -51,6 +54,61 @@ class Verification(Cog):
def __init__(self, bot: Bot):
self.bot = bot
+ async def _kick_members(self, members: t.Set[discord.Member]) -> int:
+ """Kick `members` from the PyDis guild."""
+ ...
+
+ async def _give_role(self, members: t.Set[discord.Member], role: discord.Role) -> int:
+ """Give `role` to all `members`."""
+ ...
+
+ async def check_users(self) -> None:
+ """
+ Periodically check in on the verification status of PyDis members.
+
+ This coroutine performs two actions:
+ * Find members who have not verified for `UNVERIFIED_AFTER` and give them the @Unverified role
+ * Find members who have not verified for `KICKED_AFTER` and kick them from the guild
+
+ Within the body of this coroutine, we only select the members for each action. The work is then
+ delegated to `_kick_members` and `_give_role`. After each run, a report is sent via modlog.
+ """
+ await self.bot.wait_until_guild_available() # Ensure cache is ready
+ pydis = self.bot.get_guild(constants.Guild.id)
+
+ unverified = pydis.get_role(constants.Roles.unverified)
+ current_dt = datetime.utcnow() # Discord timestamps are UTC
+
+ # Users to be given the @Unverified role, and those to be kicked, these should be entirely disjoint
+ for_role, for_kick = set(), set()
+
+ log.debug("Checking verification status of guild members")
+ for member in pydis.members:
+
+ # Skip all bots and users for which we don't know their join date
+ # This should be extremely rare, but can happen according to `joined_at` docs
+ if member.bot or member.joined_at is None:
+ continue
+
+ # Now we check roles to determine whether this user has already verified
+ unverified_roles = {unverified, pydis.default_role} # Verified users have at least one more role
+ if set(member.roles) - unverified_roles:
+ continue
+
+ # At this point, we know that `member` is an unverified user, and we will decide what
+ # to do with them based on time passed since their join date
+ since_join = current_dt - member.joined_at
+
+ if since_join > timedelta(days=KICKED_AFTER):
+ for_kick.add(member) # User should be removed from the guild
+
+ elif since_join > timedelta(days=UNVERIFIED_AFTER) and unverified not in member.roles:
+ for_role.add(member) # User should be given the @Unverified role
+
+ log.debug(f"{len(for_role)} users will be given the {unverified} role, {len(for_kick)} users will be kicked")
+ n_kicks = await self._kick_members(for_kick)
+ n_roles = await self._give_role(for_role, unverified)
+
@property
def mod_log(self) -> ModLog:
"""Get currently loaded ModLog cog instance."""
@@ -184,7 +242,7 @@ class Verification(Cog):
log.debug(f"{ctx.author} called !unsubscribe. Removing the 'Announcements' role.")
await ctx.author.remove_roles(
- discord.Object(constants.Roles.announcements), reason="Unsubscribed from announcements"
+ discord.Object(constants.Roles.announcements), reason="Unsubscribed from announcements"
)
log.trace(f"Deleting the message posted by {ctx.author}.")