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)
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})

View File

@ -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'),

View File

@ -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)