mirror of https://gitlab.com/litecord/litecord.git
blueprints.guild.roles: add basic comms between roles bp and lazy guilds
- pubsub.lazy_guild: add LazyGuildDispatcher.dispatch - pubsub.lazy_guild: add GuildMemberList.role_delete
This commit is contained in:
parent
6245f08289
commit
6c63e014ae
|
|
@ -29,6 +29,23 @@ async def get_guild_roles(guild_id):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _maybe_lg(guild_id: int, event: str,
|
||||||
|
role, force: bool = False):
|
||||||
|
# sometimes we want to dispatch an event
|
||||||
|
# even if the role isn't hoisted
|
||||||
|
|
||||||
|
# an example of such a case is when a role loses
|
||||||
|
# its hoist status.
|
||||||
|
|
||||||
|
# check if is a dict first because role_delete
|
||||||
|
# only receives the role id.
|
||||||
|
if isinstance(role, dict) and not role['hoist'] and not force:
|
||||||
|
return
|
||||||
|
|
||||||
|
await app.dispatcher.dispatch(
|
||||||
|
'lazy_guild', guild_id, event, role)
|
||||||
|
|
||||||
|
|
||||||
async def create_role(guild_id, name: str, **kwargs):
|
async def create_role(guild_id, name: str, **kwargs):
|
||||||
"""Create a role in a guild."""
|
"""Create a role in a guild."""
|
||||||
new_role_id = get_snowflake()
|
new_role_id = get_snowflake()
|
||||||
|
|
@ -64,6 +81,10 @@ async def create_role(guild_id, name: str, **kwargs):
|
||||||
)
|
)
|
||||||
|
|
||||||
role = await app.storage.get_role(new_role_id, guild_id)
|
role = await app.storage.get_role(new_role_id, guild_id)
|
||||||
|
|
||||||
|
# we need to update the lazy guild handlers for the newly created group
|
||||||
|
await _maybe_lg(guild_id, 'new_role', role)
|
||||||
|
|
||||||
await app.dispatcher.dispatch_guild(
|
await app.dispatcher.dispatch_guild(
|
||||||
guild_id, 'GUILD_ROLE_CREATE', {
|
guild_id, 'GUILD_ROLE_CREATE', {
|
||||||
'guild_id': str(guild_id),
|
'guild_id': str(guild_id),
|
||||||
|
|
@ -96,6 +117,8 @@ async def _role_update_dispatch(role_id: int, guild_id: int):
|
||||||
"""Dispatch a GUILD_ROLE_UPDATE with updated information on a role."""
|
"""Dispatch a GUILD_ROLE_UPDATE with updated information on a role."""
|
||||||
role = await app.storage.get_role(role_id, guild_id)
|
role = await app.storage.get_role(role_id, guild_id)
|
||||||
|
|
||||||
|
await _maybe_lg(guild_id, 'role_pos_upd', role)
|
||||||
|
|
||||||
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_ROLE_UPDATE', {
|
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_ROLE_UPDATE', {
|
||||||
'guild_id': str(guild_id),
|
'guild_id': str(guild_id),
|
||||||
'role': role,
|
'role': role,
|
||||||
|
|
@ -285,6 +308,7 @@ async def update_guild_role(guild_id, role_id):
|
||||||
""", j[field], role_id, guild_id)
|
""", j[field], role_id, guild_id)
|
||||||
|
|
||||||
role = await _role_update_dispatch(role_id, guild_id)
|
role = await _role_update_dispatch(role_id, guild_id)
|
||||||
|
await _maybe_lg(guild_id, 'role_update', role, True)
|
||||||
return jsonify(role)
|
return jsonify(role)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -307,6 +331,8 @@ async def delete_guild_role(guild_id, role_id):
|
||||||
if res == 'DELETE 0':
|
if res == 'DELETE 0':
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
await _maybe_lg(guild_id, 'role_delete', role_id, True)
|
||||||
|
|
||||||
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_ROLE_DELETE', {
|
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_ROLE_DELETE', {
|
||||||
'guild_id': str(guild_id),
|
'guild_id': str(guild_id),
|
||||||
'role_id': str(role_id),
|
'role_id': str(role_id),
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,30 @@ class GroupInfo:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MemberList:
|
class MemberList:
|
||||||
"""Total information on the guild's member list."""
|
"""Total information on the guild's member list.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
groups: List[:class:`GroupInfo`]
|
||||||
|
List with all group information, sorted
|
||||||
|
by their actual position in the member list.
|
||||||
|
data:
|
||||||
|
Actual dictionary holding a list of presences
|
||||||
|
that are connected to the given group.
|
||||||
|
overwrites:
|
||||||
|
Holds the channel overwrite information
|
||||||
|
for the list (a list is tied to a single
|
||||||
|
channel, and since only roles with Read Messages
|
||||||
|
can be in the list, we need to store that information)
|
||||||
|
"""
|
||||||
groups: List[GroupInfo] = None
|
groups: List[GroupInfo] = None
|
||||||
|
|
||||||
|
#: this attribute is not actively used
|
||||||
|
# but i'm keeping it here to future-proof
|
||||||
|
# in the case where we need to fetch info
|
||||||
|
# by the group id.
|
||||||
group_info: Dict[GroupID, GroupInfo] = None
|
group_info: Dict[GroupID, GroupInfo] = None
|
||||||
data: Dict[GroupID, Presence] = None
|
data: Dict[GroupID, List[Presence]] = None
|
||||||
overwrites: Dict[int, Dict[str, Any]] = None
|
overwrites: Dict[int, Dict[str, Any]] = None
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
|
|
@ -139,6 +159,11 @@ class GuildMemberList:
|
||||||
# type is{session_id: set[list]}
|
# type is{session_id: set[list]}
|
||||||
self.state = defaultdict(set)
|
self.state = defaultdict(set)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loop(self):
|
||||||
|
"""Get the main asyncio loop instance."""
|
||||||
|
return self.main.app.loop
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def storage(self):
|
def storage(self):
|
||||||
"""Get the global :class:`Storage` instance."""
|
"""Get the global :class:`Storage` instance."""
|
||||||
|
|
@ -385,6 +410,7 @@ class GuildMemberList:
|
||||||
self._set_empty_list()
|
self._set_empty_list()
|
||||||
|
|
||||||
def get_state(self, session_id: str):
|
def get_state(self, session_id: str):
|
||||||
|
"""Get the state for a session id."""
|
||||||
try:
|
try:
|
||||||
state = self.state_man.fetch_raw(session_id)
|
state = self.state_man.fetch_raw(session_id)
|
||||||
return state
|
return state
|
||||||
|
|
@ -668,20 +694,74 @@ class GuildMemberList:
|
||||||
return await self._pres_update_complex(
|
return await self._pres_update_complex(
|
||||||
user_id, old_group, old_index, new_group)
|
user_id, old_group, old_index, new_group)
|
||||||
|
|
||||||
async def dispatch(self, event: str, data: Any):
|
async def role_delete(self, role_id: int):
|
||||||
"""Modify the member list and dispatch the respective
|
"""Called when a role is deleted, so we should
|
||||||
events to subscribed shards.
|
delete it off the list."""
|
||||||
|
|
||||||
GuildMemberList stores the current guilds' list
|
|
||||||
in its :attr:`GuildMemberList.list` attribute,
|
|
||||||
with that attribute being modified via different
|
|
||||||
calls to :meth:`GuildMemberList.dispatch`
|
|
||||||
"""
|
|
||||||
|
|
||||||
# if no subscribers, drop event
|
|
||||||
if not self.list:
|
if not self.list:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# before we delete anything, we need to find the
|
||||||
|
# states we'll resend the list info to.
|
||||||
|
|
||||||
|
# find the item id for the group info
|
||||||
|
role_item_index = index_by_func(
|
||||||
|
lambda d: d.get('group', {}).get('id') == role_id,
|
||||||
|
self.items
|
||||||
|
)
|
||||||
|
|
||||||
|
sess_ids_resync = self.get_subs(role_item_index)
|
||||||
|
|
||||||
|
# remove the group info off the list
|
||||||
|
groups_index = index_by_func(
|
||||||
|
lambda group: group.gid == role_id,
|
||||||
|
self.list.groups
|
||||||
|
)
|
||||||
|
|
||||||
|
if groups_index is not None:
|
||||||
|
del self.list.groups[groups_index]
|
||||||
|
else:
|
||||||
|
log.warning('list unstable: {} not on group list', role_id)
|
||||||
|
|
||||||
|
# then the state of it in the group_info list
|
||||||
|
try:
|
||||||
|
self.list.group_info.pop(role_id)
|
||||||
|
except KeyError:
|
||||||
|
log.warning('list unstable: {} not on group info', role_id)
|
||||||
|
|
||||||
|
# now the data info
|
||||||
|
try:
|
||||||
|
self.list.data.pop(role_id)
|
||||||
|
except KeyError:
|
||||||
|
log.warning('list unstable: {} not in data dict', role_id)
|
||||||
|
|
||||||
|
# and then overwrites.
|
||||||
|
try:
|
||||||
|
self.list.overwrites.pop(role_id)
|
||||||
|
except KeyError:
|
||||||
|
log.warning('list unstable: {} not in overwrites dict', role_id)
|
||||||
|
|
||||||
|
# after removing, we do a resync with the
|
||||||
|
# shards that had the group.
|
||||||
|
|
||||||
|
for session_id in sess_ids_resync:
|
||||||
|
# find the list range that the group was on
|
||||||
|
# so we resync only the given range, instead
|
||||||
|
# of the whole list state.
|
||||||
|
ranges = self.state[session_id]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# get the only range where the group is in
|
||||||
|
role_range = next((r_min, r_max) for r_min, r_max in ranges
|
||||||
|
if r_min < role_item_index < r_max)
|
||||||
|
except StopIteration:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# do resync-ing in the background
|
||||||
|
self.loop.create_task(
|
||||||
|
self.shard_query(session_id, role_range)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LazyGuildDispatcher(Dispatcher):
|
class LazyGuildDispatcher(Dispatcher):
|
||||||
"""Main class holding the member lists for lazy guilds."""
|
"""Main class holding the member lists for lazy guilds."""
|
||||||
|
|
@ -734,3 +814,34 @@ class LazyGuildDispatcher(Dispatcher):
|
||||||
async def unsub(self, chan_id, session_id):
|
async def unsub(self, chan_id, session_id):
|
||||||
gml = await self.get_gml(chan_id)
|
gml = await self.get_gml(chan_id)
|
||||||
gml.unsub(session_id)
|
gml.unsub(session_id)
|
||||||
|
|
||||||
|
async def dispatch(self, guild_id, event: str, *args, **kwargs):
|
||||||
|
"""Call a function specialized in handling the given event"""
|
||||||
|
try:
|
||||||
|
handler = getattr(self, f'_handle_{event}')
|
||||||
|
await handler(guild_id, *args, **kwargs)
|
||||||
|
except AttributeError:
|
||||||
|
log.warning('unknown event: {}', event)
|
||||||
|
return
|
||||||
|
|
||||||
|
async def _call_all_lists(self, guild_id, method: str, *args):
|
||||||
|
lists = self.get_gml_guild(guild_id)
|
||||||
|
|
||||||
|
for lazy_list in lists:
|
||||||
|
method = getattr(lazy_list, method)
|
||||||
|
await method(*args)
|
||||||
|
|
||||||
|
async def _handle_new_role(self, guild_id: int, new_role: dict):
|
||||||
|
"""Handle the addition of a new group by dispatching it to
|
||||||
|
the member lists."""
|
||||||
|
await self._call_all_lists(guild_id, 'new_role', new_role)
|
||||||
|
|
||||||
|
async def _handle_role_pos_upd(self, guild_id, role: dict):
|
||||||
|
await self._call_all_lists(guild_id, 'role_pos_update', role)
|
||||||
|
|
||||||
|
async def _handle_role_update(self, guild_id, role: dict):
|
||||||
|
# handle name and hoist changes
|
||||||
|
await self._call_all_lists(guild_id, 'role_update', role)
|
||||||
|
|
||||||
|
async def _handle_role_delete(self, guild_id, role_id: int):
|
||||||
|
await self._call_all_lists(guild_id, 'role_delete', role_id)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue