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
This commit is contained in:
Luna Mendes 2018-11-09 19:02:26 -03:00
parent f34c3f6296
commit 864f6a5d9f
3 changed files with 71 additions and 21 deletions

View File

@ -101,6 +101,7 @@ async def modify_guild_member(guild_id, member_id):
await guild_owner_check(user_id, guild_id) await guild_owner_check(user_id, guild_id)
j = validate(await request.get_json(), MEMBER_UPDATE) j = validate(await request.get_json(), MEMBER_UPDATE)
nick_flag = False
if 'nick' in j: if 'nick' in j:
# TODO: check MANAGE_NICKNAMES # 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 WHERE user_id = $2 AND guild_id = $3
""", j['nick'], member_id, guild_id) """, j['nick'], member_id, guild_id)
nick_flag = True
if 'mute' in j: if 'mute' in j:
# TODO: check MUTE_MEMBERS # 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 = await app.storage.get_member_data_one(guild_id, member_id)
member.pop('joined_at') member.pop('joined_at')
lazy_guilds = app.dispatcher.backends['lazy_guild'] # call pres_update for role and nick changes.
lists = lazy_guilds.get_gml_guild(guild_id) partial = {
'roles': member['roles']
}
for member_list in lists: if nick_flag:
# just call pres_update but only for role changes. partial['nick'] = j['nick']
await member_list.pres_update(member_id, {
'roles': member['roles'], await app.dispatcher.dispatch(
}) 'lazy_guild', guild_id, 'pres_update', user_id, partial)
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{ await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{
'guild_id': str(guild_id) '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 = await app.storage.get_member_data_one(guild_id, user_id)
member.pop('joined_at') 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', {**{ await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_UPDATE', {**{
'guild_id': str(guild_id) 'guild_id': str(guild_id)
}, **member}) }, **member})

View File

@ -119,7 +119,7 @@ class GatewayWebsocket:
if len(encoded) < 1024: if len(encoded) < 1024:
log.debug('sending\n{}', pprint.pformat(payload)) log.debug('sending\n{}', pprint.pformat(payload))
else: else:
log.debug('sending {}', pprint.pformat(payload)) # log.debug('sending {}', pprint.pformat(payload))
log.debug('sending op={} s={} t={} (too big)', log.debug('sending op={} s={} t={} (too big)',
payload.get('op'), payload.get('op'),
payload.get('s'), payload.get('s'),

View File

@ -2,6 +2,7 @@
Main code for Lazy Guild implementation in litecord. Main code for Lazy Guild implementation in litecord.
""" """
import pprint import pprint
import asyncio
from dataclasses import dataclass, asdict from dataclasses import dataclass, asdict
from collections import defaultdict from collections import defaultdict
from typing import Any, List, Dict, Union from typing import Any, List, Dict, Union
@ -164,6 +165,8 @@ class GuildMemberList:
# type is{session_id: set[list]} # type is{session_id: set[list]}
self.state = defaultdict(set) self.state = defaultdict(set)
self._list_lock = asyncio.Lock()
@property @property
def loop(self): def loop(self):
"""Get the main asyncio loop instance.""" """Get the main asyncio loop instance."""
@ -342,9 +345,13 @@ class GuildMemberList:
# this should update the list in-place # this should update the list in-place
group_members.sort( 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.""" """Generate the main member list with groups."""
member_ids = await self.storage.get_member_ids(self.guild_id) member_ids = await self.storage.get_member_ids(self.guild_id)
@ -367,6 +374,13 @@ class GuildMemberList:
# by the display name # by the display name
await self._sort_groups() 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 @property
def items(self) -> list: def items(self) -> list:
"""Main items list.""" """Main items list."""
@ -597,22 +611,31 @@ class GuildMemberList:
for presence in presences: for presence in presences:
name = display_name(member_nicks, presence) 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 # TODO: check if this works
if name < current_name: if current_name < name:
break break
best_index += 1 best_index += 1
# insert the presence at the index # 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) new_item_index = self.get_item_index(user_id)
log.debug('assigned new item index {} to uid {}', log.debug('assigned new item index {} to uid {}',
new_item_index, user_id) new_item_index, user_id)
print('items')
pprint.pprint(self.items)
session_ids_old = self.get_subs(old_item_index) session_ids_old = self.get_subs(old_item_index)
session_ids_new = self.get_subs(new_item_index) session_ids_new = self.get_subs(new_item_index)
@ -644,21 +667,32 @@ class GuildMemberList:
- from 'offline' to any - from 'offline' to any
- from any to 'offline' - from any to 'offline'
- from any to any - 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 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 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 the group a user is on, and so there's less overhead
involved. involved.
""" """
await self._init_check() await self._init_check()
old_group, old_index, old_presence = None, None, None 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: for group, presences in self.list:
p_idx = index_by_func( p_idx = index_by_func(
@ -694,9 +728,9 @@ class GuildMemberList:
log.debug('pres update: gid={} cid={} old_g={} new_g={}', log.debug('pres update: gid={} cid={} old_g={} new_g={}',
self.guild_id, self.channel_id, old_group, new_group) self.guild_id, self.channel_id, old_group, new_group)
# if we're going to the same group, # if we're going to the same group AND there are no
# treat this as a simple update # nickname changes, treat this as a simple update
if old_group == new_group: if old_group == new_group and not has_nick:
return await self._pres_update_simple(user_id) return await self._pres_update_simple(user_id)
return await self._pres_update_complex( return await self._pres_update_complex(
@ -1039,3 +1073,8 @@ class LazyGuildDispatcher(Dispatcher):
async def _handle_role_delete(self, guild_id, role_id: int): async def _handle_role_delete(self, guild_id, role_id: int):
await self._call_all_lists(guild_id, 'role_delete', role_id) 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)