aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/sync/cog.py1
-rw-r--r--bot/cogs/sync/syncers.py114
2 files changed, 96 insertions, 19 deletions
diff --git a/bot/cogs/sync/cog.py b/bot/cogs/sync/cog.py
index 8ef45aa50..84175745a 100644
--- a/bot/cogs/sync/cog.py
+++ b/bot/cogs/sync/cog.py
@@ -17,6 +17,7 @@ class Sync:
# An iterable of callables that are called when the bot is ready.
ON_READY_SYNCERS: Iterable[Callable[[Bot, Guild], None]] = (
syncers.sync_roles,
+ syncers.sync_users
)
def __init__(self, bot):
diff --git a/bot/cogs/sync/syncers.py b/bot/cogs/sync/syncers.py
index e1e51aea0..6d200f370 100644
--- a/bot/cogs/sync/syncers.py
+++ b/bot/cogs/sync/syncers.py
@@ -1,11 +1,36 @@
+import itertools
import logging
from collections import namedtuple
+from typing import Dict, Set, ValuesView
from discord import Guild
from discord.ext.commands import Bot
log = logging.getLogger(__name__)
+
+# These objects are declared as namedtuples because tuples are hashable,
+# something that we make use of when diffing site roles against guild roles.
Role = namedtuple('Role', ('id', 'name', 'colour', 'permissions'))
+User = namedtuple('User', ('id', 'name', 'discriminator', 'avatar', 'roles', 'in_guild'))
+
+
+def get_roles_for_update(guild_roles: Set[Role], api_roles: Set[Role]) -> Set[Role]:
+ """
+ Determine which roles should be updated on the site.
+
+ Arguments:
+ guild_roles (Set[Role]):
+ Roles that were found on the guild at startup.
+
+ api_roles (Set[Role]):
+ Roles that were retrieved from the API at startup.
+
+ Returns:
+ Set[Role]:
+ Roles to be sent to the site for an update or insert.
+ """
+
+ return guild_roles - api_roles
async def sync_roles(bot: Bot, guild: Guild):
@@ -13,27 +38,16 @@ async def sync_roles(bot: Bot, guild: Guild):
Synchronize roles found on the given `guild` with the ones on the API.
"""
- def convert_role(role: Role):
- return {
- 'id': role.id,
- 'name': role.name,
- 'colour': role.colour,
- 'permissions': role.permissions
- }
-
roles = await bot.api_client.get('bot/roles')
- site_roles = {
- Role(**role_dict)
- for role_dict in roles
- }
- server_roles = {
+ api_roles = {Role(**role_dict) for role_dict in roles}
+ guild_roles = {
Role(
id=role.id, name=role.name,
colour=role.colour.value, permissions=role.permissions.value
)
for role in guild.roles
}
- roles_to_update = server_roles - site_roles
+ roles_to_update = get_roles_for_update(guild_roles, api_roles)
for role in roles_to_update:
log.info(f"Updating role `{role.name}` on the site.")
@@ -48,12 +62,74 @@ async def sync_roles(bot: Bot, guild: Guild):
)
-async def sync_members(bot: Bot, guild: Guild):
+def get_users_for_update(
+ guild_users: Dict[int, User], api_users: Dict[int, User]
+) -> ValuesView[User]:
"""
- Synchronize members found on the given `guild` with the ones on the API.
+ Obtain a set of users to update on the website.
"""
- current_members = await bot.api_client.get('bot/members')
- site_members = {
- }
+ api_user_ids = set(api_users.keys())
+ guild_user_ids = set(guild_users.keys())
+ left_user_ids = api_user_ids - guild_user_ids
+
+ api_users.update(guild_users)
+ for left_id in left_user_ids:
+ if left_id in api_users:
+ user = api_users[left_id]
+ log.debug(
+ "User `%s#%s` (`%d`) left since the last sync, updating `in_guild` field.",
+ user.name, user.discriminator, user.discriminator
+ )
+ api_users[left_id]._replace(in_guild=False)
+ return api_users.values()
+
+
+# Taken from `https://docs.python.org/3.7/library/itertools.html#itertools-recipes`.
+def chunk_by(iterable, n, fillvalue=None):
+ "Collect data into fixed-length chunks or blocks"
+ args = [iter(iterable)] * n
+ return itertools.zip_longest(*args, fillvalue=fillvalue)
+
+
+async def sync_users(bot: Bot, guild: Guild):
+ """
+ Synchronize users found on the given
+ `guild` with the ones on the API.
+ """
+
+ current_users = await bot.api_client.get('bot/users')
+ api_users = {
+ user_dict['id']: User(
+ roles=set(user_dict.pop('roles')),
+ **user_dict
+ )
+ for user_dict in current_users
+ }
+ guild_users = {
+ member.id: User(
+ id=member.id, name=member.name,
+ discriminator=member.discriminator, avatar=member.avatar,
+ roles={role.id for role in member.roles}, in_guild=True
+ )
+ for member in guild.members
+ }
+ users_to_update = get_users_for_update(guild_users, api_users)
+ log.info("Updating a total of `%d` users on the site.", len(users_to_update))
+ for chunk in chunk_by(users_to_update, n=250):
+ await bot.api_client.post(
+ 'bot/users',
+ json=[
+ {
+ 'avatar': user.avatar,
+ 'discriminator': user.discriminator,
+ 'id': user.id,
+ 'in_guild': user.in_guild,
+ 'name': user.name,
+ 'roles': list(user.roles)
+ }
+ for user in chunk
+ ]
+ )
+ log.info("User update complete.")