mirror of https://gitlab.com/litecord/litecord.git
pubsub.lazy_guild: better algorithm for presence updates
this gives the separation between "complex" and "simple" presence updates we can generalize on.
This commit is contained in:
parent
7be9d30f5d
commit
8b093d3d16
|
|
@ -245,6 +245,27 @@ class GuildMemberList:
|
||||||
]
|
]
|
||||||
self.list.group_info = {g.gid: g for g in role_groups}
|
self.list.group_info = {g.gid: g for g in role_groups}
|
||||||
|
|
||||||
|
async def get_group(self, member_id: int,
|
||||||
|
roles: List[Union[str, int]],
|
||||||
|
status: str) -> int:
|
||||||
|
"""Return a fitting group ID for the user."""
|
||||||
|
member_roles = list(map(int, roles))
|
||||||
|
|
||||||
|
# get the member's permissions relative to the channel
|
||||||
|
# (accounting for channel overwrites)
|
||||||
|
member_perms = await get_permissions(
|
||||||
|
member_id, self.channel_id, storage=self.storage)
|
||||||
|
|
||||||
|
if not member_perms.bits.read_messages:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# if the member is offline, we
|
||||||
|
# default give them the offline group.
|
||||||
|
group_id = ('offline' if status == 'offline'
|
||||||
|
else self._calc_member_group(member_roles, status))
|
||||||
|
|
||||||
|
return group_id
|
||||||
|
|
||||||
async def _pass_1(self, guild_presences: List[Presence]):
|
async def _pass_1(self, guild_presences: List[Presence]):
|
||||||
"""First pass on generating the member list.
|
"""First pass on generating the member list.
|
||||||
|
|
||||||
|
|
@ -253,22 +274,9 @@ class GuildMemberList:
|
||||||
for presence in guild_presences:
|
for presence in guild_presences:
|
||||||
member_id = int(presence['user']['id'])
|
member_id = int(presence['user']['id'])
|
||||||
|
|
||||||
# list of roles for the member
|
group_id = await self.get_group(
|
||||||
member_roles = list(map(int, presence['roles']))
|
member_id, presence['roles'], presence['status']
|
||||||
|
)
|
||||||
# get the member's permissions relative to the channel
|
|
||||||
# (accounting for channel overwrites)
|
|
||||||
member_perms = await get_permissions(
|
|
||||||
member_id, self.channel_id, storage=self.storage)
|
|
||||||
|
|
||||||
if not member_perms.bits.read_messages:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# if the member is offline, we
|
|
||||||
# default give them the offline group.
|
|
||||||
status = presence['status']
|
|
||||||
group_id = ('offline' if status == 'offline'
|
|
||||||
else self._calc_member_group(member_roles, status))
|
|
||||||
|
|
||||||
self.list.data[group_id].append(presence)
|
self.list.data[group_id].append(presence)
|
||||||
|
|
||||||
|
|
@ -460,24 +468,12 @@ class GuildMemberList:
|
||||||
|
|
||||||
await self._dispatch_sess([session_id], ops)
|
await self._dispatch_sess([session_id], ops)
|
||||||
|
|
||||||
async def pres_update(self, user_id: int,
|
async def _pres_update_simple(self, user_id: int):
|
||||||
partial_presence: Dict[str, Any]):
|
def _get_id(item):
|
||||||
"""Update a presence inside the member listlist."""
|
# item can be a group item or a member item
|
||||||
await self._init_check()
|
return item.get('member', {}).get('user', {}).get('id')
|
||||||
|
|
||||||
for _group, presences in self.list:
|
|
||||||
p_idx = index_by_func(
|
|
||||||
lambda p: p['user']['id'] == str(user_id),
|
|
||||||
presences)
|
|
||||||
|
|
||||||
if not p_idx:
|
|
||||||
continue
|
|
||||||
|
|
||||||
presences[p_idx].update(partial_presence)
|
|
||||||
|
|
||||||
def _get_id(p):
|
|
||||||
return p.get('member', {}).get('user', {}).get('id')
|
|
||||||
|
|
||||||
|
# get the updated item's index
|
||||||
item_index = index_by_func(
|
item_index = index_by_func(
|
||||||
lambda p: _get_id(p) == str(user_id),
|
lambda p: _get_id(p) == str(user_id),
|
||||||
self.items
|
self.items
|
||||||
|
|
@ -490,6 +486,8 @@ class GuildMemberList:
|
||||||
|
|
||||||
item = self.items[item_index]
|
item = self.items[item_index]
|
||||||
|
|
||||||
|
# only dispatch to sessions
|
||||||
|
# that are subscribed to the given item's index
|
||||||
def _is_in(sess_id):
|
def _is_in(sess_id):
|
||||||
ranges = self.state[sess_id]
|
ranges = self.state[sess_id]
|
||||||
|
|
||||||
|
|
@ -501,6 +499,8 @@ class GuildMemberList:
|
||||||
|
|
||||||
session_ids = filter(_is_in, self.state.keys())
|
session_ids = filter(_is_in, self.state.keys())
|
||||||
|
|
||||||
|
# simple update means we just give an UPDATE
|
||||||
|
# operation
|
||||||
return await self._dispatch_sess(
|
return await self._dispatch_sess(
|
||||||
session_ids,
|
session_ids,
|
||||||
[
|
[
|
||||||
|
|
@ -511,6 +511,70 @@ class GuildMemberList:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _pres_update_complex(self, user_id: int,
|
||||||
|
old_group: str, new_group: str):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def pres_update(self, user_id: int,
|
||||||
|
partial_presence: Dict[str, Any]):
|
||||||
|
"""Update a presence inside the member list.
|
||||||
|
|
||||||
|
There are 4 types of updates that can happen for a user in a group:
|
||||||
|
- from 'offline' to any
|
||||||
|
- from any to 'offline'
|
||||||
|
- from any to any
|
||||||
|
- from G to G (with G being any group)
|
||||||
|
|
||||||
|
any: 'online' | role_id
|
||||||
|
|
||||||
|
All first, second, and third updates are 'complex' updates,
|
||||||
|
which means we'll have to change the group the user is on
|
||||||
|
to account for them.
|
||||||
|
|
||||||
|
The fourth 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_presence = None, None
|
||||||
|
|
||||||
|
for group, presences in self.list:
|
||||||
|
p_idx = index_by_func(
|
||||||
|
lambda p: p['user']['id'] == str(user_id),
|
||||||
|
presences)
|
||||||
|
|
||||||
|
if not p_idx:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# make a copy since we're modifying in-place
|
||||||
|
old_group = group.gid
|
||||||
|
old_presence = dict(presences[p_idx])
|
||||||
|
|
||||||
|
# be ready if it is a simple update
|
||||||
|
presences[p_idx].update(partial_presence)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not old_group:
|
||||||
|
log.warning('pres update with unknown old group uid={}',
|
||||||
|
user_id)
|
||||||
|
return []
|
||||||
|
|
||||||
|
roles = partial_presence.get('roles', old_presence['roles'])
|
||||||
|
new_status = partial_presence.get('status', old_presence['status'])
|
||||||
|
|
||||||
|
new_group = await self.get_group(user_id, roles, new_status)
|
||||||
|
|
||||||
|
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:
|
||||||
|
return await self._pres_update_simple(user_id)
|
||||||
|
|
||||||
|
return await self._pres_update_complex(user_id, old_group, new_group)
|
||||||
|
|
||||||
async def dispatch(self, event: str, data: Any):
|
async def dispatch(self, event: str, data: Any):
|
||||||
"""Modify the member list and dispatch the respective
|
"""Modify the member list and dispatch the respective
|
||||||
events to subscribed shards.
|
events to subscribed shards.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue