diff options
author | 2019-12-17 23:18:00 +0100 | |
---|---|---|
committer | 2019-12-17 23:18:00 +0100 | |
commit | c469b82144eacca992647f8321992d66e6bad3c8 (patch) | |
tree | 5e9729739fd335b611e3ddcca051717694b569fc | |
parent | Use OAuth to be Reddit API compliant (#696) (diff) |
Use on_user_update to properly sync users with db
It's important to us that we keep the information we have about users
in the database in sync with the actual user information the bot can
observe in our guild. To do this, we relied on the `on_member_update`
event listener to synchronize a user's information when an update of
the information was detected. However, unfortunately, this does not
work for user account information (i.e., the username, avatar, and
discriminator of the user).
The solution is to use the `on_user_update` event listener to watch
for updates in the user settings and to use the `on_member_update`
event listener to watch for updates in guild-related information for
that user. (We currently only sync the roles the user has.)
See:
- https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_update
- https://discordpy.readthedocs.io/en/stable/api.html#discord.on_user_update
Note:
The docs for `discord.py` make it *seem* like the `on_member_update`
event does not fire for updates of theusername, discriminator, and
avatar attributes. However, experimentation shows that this event
*does* fire; it's just that the member objects provided as `before`
and `after` to the listener will already have been updated in cache
by the `on_user_update` event that fires *before* it.
This means that if the only changes made were to the username,
avatar, and discriminator, the `on_member_update` event does fire,
but with two *equal* Member objects. This makes it appear as if you
may be able to use `on_member_update`, since it fires, but it does
not actually contain anything useful.
-rw-r--r-- | bot/cogs/sync/cog.py | 55 |
1 files changed, 26 insertions, 29 deletions
diff --git a/bot/cogs/sync/cog.py b/bot/cogs/sync/cog.py index 90d4c40fe..4e6ed156b 100644 --- a/bot/cogs/sync/cog.py +++ b/bot/cogs/sync/cog.py @@ -1,7 +1,7 @@ import logging -from typing import Callable, Iterable +from typing import Callable, Dict, Iterable, Union -from discord import Guild, Member, Role +from discord import Guild, Member, Role, User from discord.ext import commands from discord.ext.commands import Cog, Context @@ -51,6 +51,15 @@ class Sync(Cog): f"deleted `{total_deleted}`." ) + async def patch_user(self, user_id: int, updated_information: Dict[str, Union[str, int]]) -> None: + """Send a PATCH request to partially update a user in the database.""" + try: + await self.bot.api_client.patch("bot/users/" + str(user_id), json=updated_information) + except ResponseCodeError as e: + if e.response.status != 404: + raise + log.warning("Unable to update user, got 404. Assuming race condition from join event.") + @Cog.listener() async def on_guild_role_create(self, role: Role) -> None: """Adds newly create role to the database table over the API.""" @@ -143,33 +152,21 @@ class Sync(Cog): @Cog.listener() async def on_member_update(self, before: Member, after: Member) -> None: - """Updates the user information if any of relevant attributes have changed.""" - if ( - before.name != after.name - or before.avatar != after.avatar - or before.discriminator != after.discriminator - or before.roles != after.roles - ): - try: - await self.bot.api_client.put( - 'bot/users/' + str(after.id), - json={ - 'avatar_hash': after.avatar, - 'discriminator': int(after.discriminator), - 'id': after.id, - 'in_guild': True, - 'name': after.name, - 'roles': sorted(role.id for role in after.roles) - } - ) - except ResponseCodeError as e: - if e.response.status != 404: - raise - - log.warning( - "Unable to update user, got 404. " - "Assuming race condition from join event." - ) + """Update the roles of the member in the database if a change is detected.""" + if before.roles != after.roles: + updated_information = {"roles": sorted(role.id for role in after.roles)} + await self.patch_user(after.id, updated_information=updated_information) + + @Cog.listener() + async def on_user_update(self, before: User, after: User) -> None: + """Update the user information in the database if a relevant change is detected.""" + if any(getattr(before, attr) != getattr(after, attr) for attr in ("name", "discriminator", "avatar")): + updated_information = { + "name": after.name, + "discriminator": int(after.discriminator), + "avatar_hash": after.avatar, + } + await self.patch_user(after.id, updated_information=updated_information) @commands.group(name='sync') @commands.has_permissions(administrator=True) |