From 864f6a5d9f0c0de9e7ed1debdd174c5b5927e40c Mon Sep 17 00:00:00 2001 From: Luna Mendes Date: Fri, 9 Nov 2018 19:02:26 -0300 Subject: [PATCH] guild.members: add basic handling of nicks in lazy guilds - pubsub.lazy_guilds: add a list lock to prevent inconsistencies - pubsub.lazy_guilds: add a lot of debug info related to list sorting --- litecord/blueprints/guild/members.py | 25 ++++++++--- litecord/gateway/websocket.py | 2 +- litecord/pubsub/lazy_guild.py | 65 ++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/litecord/blueprints/guild/members.py b/litecord/blueprints/guild/members.py index 2400498..8a6e48a 100644 --- a/litecord/blueprints/guild/members.py +++ b/litecord/blueprints/guild/members.py @@ -101,6 +101,7 @@ async def modify_guild_member(guild_id, member_id): await guild_owner_check(user_id, guild_id) j = validate(await request.get_json(), MEMBER_UPDATE) + nick_flag = False if 'nick' in j: # TODO: check MANAGE_NICKNAMES @@ -111,6 +112,8 @@ async def modify_guild_member(guild_id, member_id): WHERE user_id = $2 AND guild_id = $3 """, j['nick'], member_id, guild_id) + nick_flag = True + if 'mute' in j: # TODO: check MUTE_MEMBERS @@ -141,14 +144,16 @@ async def modify_guild_member(guild_id, member_id): member = await app.storage.get_member_data_one(guild_id, member_id) member.pop('joined_at') - lazy_guilds = app.dispatcher.backends['lazy_guild'] - lists = lazy_guilds.get_gml_guild(guild_id) + # call pres_update for role and nick changes. + partial = { + 'roles': member['roles'] + } - for member_list in lists: - # just call pres_update but only for role changes. - await member_list.pres_update(member_id, { - 'roles': member['roles'], - }) + if nick_flag: + partial['nick'] = j['nick'] + + await app.dispatcher.dispatch( + 'lazy_guild', guild_id, 'pres_update', user_id, partial) await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{ 'guild_id': str(guild_id) @@ -174,6 +179,12 @@ async def update_nickname(guild_id): member = await app.storage.get_member_data_one(guild_id, user_id) member.pop('joined_at') + # call pres_update for nick changes, etc. + await app.dispatcher.dispatch( + 'lazy_guild', guild_id, 'pres_update', user_id, { + 'nick': j['nick'] + }) + await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{ 'guild_id': str(guild_id) }, **member}) diff --git a/litecord/gateway/websocket.py b/litecord/gateway/websocket.py index 8001a28..e14ca41 100644 --- a/litecord/gateway/websocket.py +++ b/litecord/gateway/websocket.py @@ -119,7 +119,7 @@ class GatewayWebsocket: if len(encoded) < 1024: log.debug('sending\n{}', pprint.pformat(payload)) else: - log.debug('sending {}', pprint.pformat(payload)) + # log.debug('sending {}', pprint.pformat(payload)) log.debug('sending op={} s={} t={} (too big)', payload.get('op'), payload.get('s'), diff --git a/litecord/pubsub/lazy_guild.py b/litecord/pubsub/lazy_guild.py index 58e3548..419cfe1 100644 --- a/litecord/pubsub/lazy_guild.py +++ b/litecord/pubsub/lazy_guild.py @@ -2,6 +2,7 @@ Main code for Lazy Guild implementation in litecord. """ import pprint +import asyncio from dataclasses import dataclass, asdict from collections import defaultdict from typing import Any, List, Dict, Union @@ -164,6 +165,8 @@ class GuildMemberList: # type is{session_id: set[list]} self.state = defaultdict(set) + self._list_lock = asyncio.Lock() + @property def loop(self): """Get the main asyncio loop instance.""" @@ -342,9 +345,13 @@ class GuildMemberList: # this should update the list in-place group_members.sort( - key=lambda p: display_name(member_nicks, p)) + key=lambda p: display_name(member_nicks, p), + reverse=True) - async def _init_member_list(self): + print('post sort') + pprint.pprint(group_members) + + async def __init_member_list(self): """Generate the main member list with groups.""" member_ids = await self.storage.get_member_ids(self.guild_id) @@ -367,6 +374,13 @@ class GuildMemberList: # by the display name await self._sort_groups() + async def _init_member_list(self): + try: + await self._list_lock.acquire() + await self.__init_member_list() + finally: + self._list_lock.release() + @property def items(self) -> list: """Main items list.""" @@ -597,22 +611,31 @@ class GuildMemberList: for presence in presences: name = display_name(member_nicks, presence) - print(name, current_name, name < current_name) + print(name, current_name, current_name < name) # TODO: check if this works - if name < current_name: + if current_name < name: break best_index += 1 # insert the presence at the index - presences.insert(best_index + 1, current_presence) + print('pre insert') + pprint.pprint(presences) + + presences.insert(best_index - 1, current_presence) + log.debug('inserted cur pres @ pres idx {}', best_index - 1) + + print('post insert') + pprint.pprint(presences) new_item_index = self.get_item_index(user_id) - log.debug('assigned new item index {} to uid {}', new_item_index, user_id) + print('items') + pprint.pprint(self.items) + session_ids_old = self.get_subs(old_item_index) session_ids_new = self.get_subs(new_item_index) @@ -644,21 +667,32 @@ class GuildMemberList: - from 'offline' to any - from any to 'offline' - from any to any - - from G to G (with G being any group) + - from G to G (with G being any group), while changing position + - from G to G (with G being any group), but not changing position any: 'online' | role_id - All first, second, and third updates are 'complex' updates, + All 1st, 2nd, 3rd, and 4th updates are 'complex' updates, which means we'll have to change the group the user is on - to account for them. + to account for them, or we'll change the position a user + is in inside a group (for the 4th update). - The fourth is a 'simple' change, since we're not changing + The fifth is a 'simple' change, since we're not changing the group a user is on, and so there's less overhead involved. """ await self._init_check() old_group, old_index, old_presence = None, None, None + has_nick = 'nick' in partial_presence + + # partial presences don't have 'nick'. we only use it + # as a flag that we're doing a mixed update (complex + # but without any inter-group changes) + try: + partial_presence.pop('nick') + except KeyError: + pass for group, presences in self.list: p_idx = index_by_func( @@ -694,9 +728,9 @@ class GuildMemberList: log.debug('pres update: gid={} cid={} old_g={} new_g={}', self.guild_id, self.channel_id, old_group, new_group) - # if we're going to the same group, - # treat this as a simple update - if old_group == new_group: + # if we're going to the same group AND there are no + # nickname changes, treat this as a simple update + if old_group == new_group and not has_nick: return await self._pres_update_simple(user_id) return await self._pres_update_complex( @@ -1039,3 +1073,8 @@ class LazyGuildDispatcher(Dispatcher): async def _handle_role_delete(self, guild_id, role_id: int): await self._call_all_lists(guild_id, 'role_delete', role_id) + + async def _handle_pres_update(self, guild_id, user_id: int, + partial: dict): + await self._call_all_lists( + guild_id, 'pres_update', user_id, partial)