From 8c7ae9b76c44f34128fd38f9b36787d4f9dc4371 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 18:20:26 -0300 Subject: [PATCH 01/24] add dm_channels blueprint --- litecord/blueprints/__init__.py | 4 +++- litecord/blueprints/dm_channels.py | 36 ++++++++++++++++++++++++++++++ run.py | 3 ++- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 litecord/blueprints/dm_channels.py diff --git a/litecord/blueprints/__init__.py b/litecord/blueprints/__init__.py index a22b5b1..9b021b4 100644 --- a/litecord/blueprints/__init__.py +++ b/litecord/blueprints/__init__.py @@ -32,7 +32,9 @@ from .icons import bp as icons from .nodeinfo import bp as nodeinfo from .static import bp as static from .attachments import bp as attachments +from .dm_channels import bp as dm_channels __all__ = ['gateway', 'auth', 'users', 'guilds', 'channels', 'webhooks', 'science', 'voice', 'invites', 'relationships', - 'dms', 'icons', 'nodeinfo', 'static', 'attachments'] + 'dms', 'icons', 'nodeinfo', 'static', 'attachments', + 'dm_channels'] diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py new file mode 100644 index 0000000..0e24d8e --- /dev/null +++ b/litecord/blueprints/dm_channels.py @@ -0,0 +1,36 @@ +""" + +Litecord +Copyright (C) 2018-2019 Luna Mendes + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +""" + +from quart import Blueprint, request, current_app as app, jsonify +from logbook import Logger + +log = Logger(__name__) +bp = Blueprint('dms', __name__) + + +@bp.route('//receipients/', methods=['PUT']) +async def add_to_group_dm(dm_chan, user_id): + """Adds a member to a group dm OR creates a group dm.""" + pass + + +@bp.route('//recipients/', methods=['DELETE']) +async def remove_from_group_dm(dm_chan, user_id): + """Remove users from group dm.""" + pass diff --git a/run.py b/run.py index f9ffde2..ddebbe8 100644 --- a/run.py +++ b/run.py @@ -35,7 +35,7 @@ import config from litecord.blueprints import ( gateway, auth, users, guilds, channels, webhooks, science, voice, invites, relationships, dms, icons, nodeinfo, static, - attachments + attachments, dm_channels ) # those blueprints are separated from the "main" ones @@ -128,6 +128,7 @@ def set_blueprints(app_): voice: '/voice', invites: None, dms: '/users', + dm_channels: '/channels', fake_store: None, From 682a527a551f8a3bbc8f93051bb18a4a293fefee Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 18:25:51 -0300 Subject: [PATCH 02/24] dm_channels: fix routes --- litecord/blueprints/dm_channels.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 0e24d8e..c908e7d 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -21,16 +21,16 @@ from quart import Blueprint, request, current_app as app, jsonify from logbook import Logger log = Logger(__name__) -bp = Blueprint('dms', __name__) +bp = Blueprint('dm_channels', __name__) -@bp.route('//receipients/', methods=['PUT']) +@bp.route('//receipients/', methods=['PUT']) async def add_to_group_dm(dm_chan, user_id): """Adds a member to a group dm OR creates a group dm.""" pass -@bp.route('//recipients/', methods=['DELETE']) +@bp.route('//recipients/', methods=['DELETE']) async def remove_from_group_dm(dm_chan, user_id): """Remove users from group dm.""" pass From 1bb2a46d9e28a3f00bdeaddb06751ded36a88816 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 18:40:21 -0300 Subject: [PATCH 03/24] checks: add only kwarg to filter allowed channels in route - dm_channels: add channel_check usage --- litecord/blueprints/checks.py | 16 ++++++++++++++-- litecord/blueprints/dm_channels.py | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/litecord/blueprints/checks.py b/litecord/blueprints/checks.py index 9c9253a..a27fcb4 100644 --- a/litecord/blueprints/checks.py +++ b/litecord/blueprints/checks.py @@ -17,6 +17,8 @@ along with this program. If not, see . """ +from typing import Union, List + from quart import current_app as app from litecord.enums import ChannelType, GUILD_CHANS @@ -53,16 +55,23 @@ async def guild_owner_check(user_id: int, guild_id: int): raise Forbidden('You are not the owner of the guild') -async def channel_check(user_id, channel_id): +async def channel_check(user_id, channel_id, *, + only: Union[ChannelType, List[ChannelType]] = None): """Check if the current user is authorized to read the channel's information.""" chan_type = await app.storage.get_chan_type(channel_id) if chan_type is None: - raise ChannelNotFound(f'channel type not found') + raise ChannelNotFound('channel type not found') ctype = ChannelType(chan_type) + if not isinstance(only, list): + only = [only] + + if only and ctype not in only: + raise ChannelNotFound('invalid channel type') + if ctype in GUILD_CHANS: guild_id = await app.db.fetchval(""" SELECT guild_id @@ -77,6 +86,9 @@ async def channel_check(user_id, channel_id): peer_id = await app.storage.get_dm_peer(channel_id, user_id) return ctype, peer_id + if ctype == ChannelType.GROUP_DM: + return ctype + async def guild_perm_check(user_id, guild_id, permission: str): """Check guild permissions for a user.""" diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index c908e7d..310150b 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -20,6 +20,10 @@ along with this program. If not, see . from quart import Blueprint, request, current_app as app, jsonify from logbook import Logger +from litecord.blueprints.auth import token_check +from litecord.blueprints.checks import channel_check +from litecord.enums import ChannelType + log = Logger(__name__) bp = Blueprint('dm_channels', __name__) @@ -27,10 +31,17 @@ bp = Blueprint('dm_channels', __name__) @bp.route('//receipients/', methods=['PUT']) async def add_to_group_dm(dm_chan, user_id): """Adds a member to a group dm OR creates a group dm.""" - pass + user_id = await token_check() + ctype = await channel_check( + user_id, dm_chan, + only=[ChannelType.DM, ChannelType.GROUP_DM] + ) @bp.route('//recipients/', methods=['DELETE']) async def remove_from_group_dm(dm_chan, user_id): """Remove users from group dm.""" - pass + user_id = await token_check() + ctype = await channel_check( + user_id, dm_chan, only=ChannelType.GROUP_DM + ) From 72cbd8017bb4ce7ccac515adcf5957e91f01e78b Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 19:00:38 -0300 Subject: [PATCH 04/24] dm_channels: add basic checks and dummy calls to _gdm prefixed funcs - checks: return group dm owner when channel_check is given a group dm - users: check for friendship on profile --- litecord/blueprints/checks.py | 8 +++++- litecord/blueprints/dm_channels.py | 40 ++++++++++++++++++++++++++---- litecord/blueprints/users.py | 3 ++- litecord/storage.py | 40 ++++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/litecord/blueprints/checks.py b/litecord/blueprints/checks.py index a27fcb4..5a6846c 100644 --- a/litecord/blueprints/checks.py +++ b/litecord/blueprints/checks.py @@ -87,7 +87,13 @@ async def channel_check(user_id, channel_id, *, return ctype, peer_id if ctype == ChannelType.GROUP_DM: - return ctype + owner_id = await app.db.fetchval(""" + SELECT owner_id + FROM group_dm_channels + WHERE id = $1 + """, channel_id) + + return ctype, owner_id async def guild_perm_check(user_id, guild_id, permission: str): diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 310150b..3b395a1 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -17,31 +17,61 @@ along with this program. If not, see . """ -from quart import Blueprint, request, current_app as app, jsonify +from quart import Blueprint, current_app as app, jsonify from logbook import Logger from litecord.blueprints.auth import token_check from litecord.blueprints.checks import channel_check from litecord.enums import ChannelType +from litecord.errors import BadRequest, Forbidden log = Logger(__name__) bp = Blueprint('dm_channels', __name__) -@bp.route('//receipients/', methods=['PUT']) -async def add_to_group_dm(dm_chan, user_id): +@bp.route('//receipients/', methods=['PUT']) +async def add_to_group_dm(dm_chan, peer_id): """Adds a member to a group dm OR creates a group dm.""" user_id = await token_check() - ctype = await channel_check( + + # other_id is the owner of the group dm (gdm) if the + # given channel is a gdm + + # other_id is the peer of the dm if the given channel is a dm + ctype, other_id = await channel_check( user_id, dm_chan, only=[ChannelType.DM, ChannelType.GROUP_DM] ) + # check relationship with the given user id + # and the user id making the request + friends = await app.user_storage.are_friends_with(user_id, peer_id) + + if not friends: + raise BadRequest('Cant insert peer into dm') + + if ctype == ChannelType.DM: + dm_chan = await _gdm_create( + user_id, other_id + ) + + await _gdm_add_recipient(dm_chan, peer_id, user_id=user_id) + + return jsonify( + await app.storage.get_channel(dm_chan) + ) + @bp.route('//recipients/', methods=['DELETE']) async def remove_from_group_dm(dm_chan, user_id): """Remove users from group dm.""" user_id = await token_check() - ctype = await channel_check( + _ctype, owner_id = await channel_check( user_id, dm_chan, only=ChannelType.GROUP_DM ) + + if owner_id != user_id: + raise Forbidden('You are now the owner of the group DM') + + await _gdm_remove_recipient(dm_chan, user_id) + return '', 204 diff --git a/litecord/blueprints/users.py b/litecord/blueprints/users.py index 969a043..8e9deb7 100644 --- a/litecord/blueprints/users.py +++ b/litecord/blueprints/users.py @@ -384,9 +384,10 @@ async def get_profile(peer_id: int): return '', 404 mutuals = await app.user_storage.get_mutual_guilds(user_id, peer_id) + friends = await app.user_storage.are_friends_with(user_id, peer_id) # don't return a proper card if no guilds are being shared. - if not mutuals: + if not mutuals and not friends: return '', 404 # actual premium status is determined by that diff --git a/litecord/storage.py b/litecord/storage.py index 3f96058..d44e035 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -353,6 +353,33 @@ class Storage: return list(map(_overwrite_convert, overwrite_rows)) + async def _gdm_recipient_ids(self, channel_id: int) -> List[int]: + """Get the list of user IDs that are recipients of the + given Group DM.""" + user_ids = await self.db.fetch(""" + SELECT member_id + FROM group_dm_members + JOIN users + ON member_id = users.id + WHERE group_dm_members.id = $1 + ORDER BY username DESC + """, channel_id) + + return [r['member_id'] for r in user_ids] + + async def _gdm_recipients(self, channel_id: int) -> List[int]: + """Get the list of users that are recipients of the + given Group DM.""" + recipients = await self._gdm_recipient_ids(channel_id) + res = [] + + for user_id in recipients: + res.append( + await self.get_user(user_id) + ) + + return res + async def get_channel(self, channel_id: int) -> Dict[str, Any]: """Fetch a single channel's information.""" chan_type = await self.get_chan_type(channel_id) @@ -401,8 +428,17 @@ class Storage: drow['id'] = str(drow['id']) return drow elif ctype == ChannelType.GROUP_DM: - # TODO: group dms - pass + gdm_row = await self.db.fetchrow(""" + SELECT id, owner_id::text, name, icon + FROM group_dm_channels + WHERE id = $1 + """, channel_id) + + drow = dict(gdm_row) + recipients + drow['recipients'] = await self._gdm_recipients(channel_id) + + return drow return None From 5fc6732892a8ebaef68994e0525f9d971df51155 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 19:08:22 -0300 Subject: [PATCH 05/24] dm_channels: add dummy functions for _gdm - storage: fix error --- litecord/blueprints/dm_channels.py | 40 ++++++++++++++++++++++++++++++ litecord/storage.py | 5 ++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 3b395a1..e30effd 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -24,11 +24,51 @@ from litecord.blueprints.auth import token_check from litecord.blueprints.checks import channel_check from litecord.enums import ChannelType from litecord.errors import BadRequest, Forbidden +from litecord.snowflake import get_snowflake log = Logger(__name__) bp = Blueprint('dm_channels', __name__) +async def _gdm_create(user_id, peer_id) -> int: + """Create a group dm, given two users. + + Returns the new GDM id. + """ + channel_id = get_snowflake() + + # TODO + + return channel_id + + +async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): + """Add a recipient to a Group DM. + + Dispatches: + - A system message with the join (depending of user_id) + - A CHANNEL_CREATE to the peer. + - A CHANNEL_UPDATE to all remaining recipients. + """ + + # TODO + pass + + +async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): + """Remove a member from a GDM. + + Dispatches: + - A system message with the leave or forced removal (depending if user_id) + exists or not. + - A CHANNEL_DELETE to the peer. + - A CHANNEL_UPDATE to all remaining recipients. + """ + + # TODO + pass + + @bp.route('//receipients/', methods=['PUT']) async def add_to_group_dm(dm_chan, peer_id): """Adds a member to a group dm OR creates a group dm.""" diff --git a/litecord/storage.py b/litecord/storage.py index d44e035..03b6dfe 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -353,7 +353,7 @@ class Storage: return list(map(_overwrite_convert, overwrite_rows)) - async def _gdm_recipient_ids(self, channel_id: int) -> List[int]: + async def gdm_recipient_ids(self, channel_id: int) -> List[int]: """Get the list of user IDs that are recipients of the given Group DM.""" user_ids = await self.db.fetch(""" @@ -370,7 +370,7 @@ class Storage: async def _gdm_recipients(self, channel_id: int) -> List[int]: """Get the list of users that are recipients of the given Group DM.""" - recipients = await self._gdm_recipient_ids(channel_id) + recipients = await self.gdm_recipient_ids(channel_id) res = [] for user_id in recipients: @@ -435,7 +435,6 @@ class Storage: """, channel_id) drow = dict(gdm_row) - recipients drow['recipients'] = await self._gdm_recipients(channel_id) return drow From 0f3d331c646268d22865260f0b748ff68fb4f551 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Feb 2019 23:30:51 -0300 Subject: [PATCH 06/24] add raw implementations for gdm functions - pubsub: add mass_sub() method - gateway.websocket: use mass_sub() - storage: add last_message_id to group dm channels - user_storage: add get_gdms_internal() --- litecord/blueprints/dm_channels.py | 45 ++++++++++++++++++++++++++---- litecord/dispatcher.py | 9 ++++++ litecord/gateway/websocket.py | 15 +++++++--- litecord/storage.py | 6 +++- litecord/user_storage.py | 10 +++++++ 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index e30effd..2e8f41f 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -30,6 +30,20 @@ log = Logger(__name__) bp = Blueprint('dm_channels', __name__) +async def _raw_gdm_add(channel_id, user_id): + await app.db.execute(""" + INSERT INTO group_dm_members (id, member_id) + VALUES ($1, $2) + """, channel_id, user_id) + + +async def _raw_gdm_remove(channel_id, user_id): + await app.db.execute(""" + DELETE FROM group_dm_members + WHERE id = $1 AND member_id = $2 + """, channel_id, user_id) + + async def _gdm_create(user_id, peer_id) -> int: """Create a group dm, given two users. @@ -37,7 +51,24 @@ async def _gdm_create(user_id, peer_id) -> int: """ channel_id = get_snowflake() - # TODO + await app.db.execute(""" + INSERT INTO channels (id, channel_type) + VALUES ($1, $2) + """, channel_id, ChannelType.GROUP_DM.value) + + await app.db.execute(""" + INSERT INTO group_dm_channels (id, owner_id, name, icon) + VALUES ($1, $2, NULL, NULL) + """, channel_id, user_id) + + await _raw_gdm_add(channel_id, user_id) + await _raw_gdm_add(channel_id, peer_id) + + await app.dispatcher.sub('channel', channel_id, user_id) + await app.dispatcher.sub('channel', channel_id, peer_id) + + chan = await app.storage.get_channel(channel_id) + await app.dispatcher.dispatch('channel', 'CHANNEL_CREATE', chan) return channel_id @@ -50,9 +81,11 @@ async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): - A CHANNEL_CREATE to the peer. - A CHANNEL_UPDATE to all remaining recipients. """ + await _raw_gdm_add(channel_id, peer_id) - # TODO - pass + chan = await app.storage.get_channel(channel_id) + await app.dispatcher.dispatch('user', user_id, 'CHANNEL_CREATE', chan) + await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): @@ -64,9 +97,11 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): - A CHANNEL_DELETE to the peer. - A CHANNEL_UPDATE to all remaining recipients. """ + await _raw_gdm_remove(channel_id, peer_id) - # TODO - pass + chan = await app.storage.get_channel(channel_id) + await app.dispatcher.dispatch('user', user_id, 'CHANNEL_DELETE', chan) + await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) @bp.route('//receipients/', methods=['PUT']) diff --git a/litecord/dispatcher.py b/litecord/dispatcher.py index 59e19a3..8d38303 100644 --- a/litecord/dispatcher.py +++ b/litecord/dispatcher.py @@ -103,6 +103,15 @@ class EventDispatcher: for key in keys: await self.subscribe(backend_str, key, identifier) + async def mass_sub(self, identifier: Any, + backends: List[tuple]): + """Mass subscribe to many backends at once.""" + for backend_str, keys in backends: + log.debug('subscribing {} to {} keys in backend {}', + identifier, len(keys), backend_str) + + await self.sub_many(backend_str, identifier, keys) + async def dispatch(self, backend_str: str, key: Any, *args, **kwargs): """Dispatch an event to the backend. diff --git a/litecord/gateway/websocket.py b/litecord/gateway/websocket.py index 9dc402c..3c7a67e 100644 --- a/litecord/gateway/websocket.py +++ b/litecord/gateway/websocket.py @@ -437,17 +437,24 @@ class GatewayWebsocket: by GuildDispatcher.sub """ user_id = self.state.user_id - guild_ids = await self._guild_ids() - log.info('subscribing to {} guilds', len(guild_ids)) - await self.ext.dispatcher.sub_many('guild', user_id, guild_ids) # subscribe the user to all dms they have OPENED. dms = await self.user_storage.get_dms(user_id) dm_ids = [int(dm['id']) for dm in dms] + # fetch all group dms the user is a member of. + gdm_ids = await self.user_storage.get_gdms_internal(user_id) + + log.info('subscribing to {} guilds', len(guild_ids)) log.info('subscribing to {} dms', len(dm_ids)) - await self.ext.dispatcher.sub_many('channel', user_id, dm_ids) + log.info('subscribing to {} group dms', len(gdm_ids)) + + await self.ext.dispatcher.mass_sub(user_id, [ + ('guild', guild_ids), + ('channel', dm_ids), + ('channel', gdm_ids) + ]) if not self.state.bot: # subscribe to all friends diff --git a/litecord/storage.py b/litecord/storage.py index 03b6dfe..380f97f 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -414,7 +414,8 @@ class Storage: drow['type'] = chan_type drow['last_message_id'] = await self.chan_last_message_str( - channel_id) + channel_id + ) # dms have just two recipients. drow['recipients'] = [ @@ -436,6 +437,9 @@ class Storage: drow = dict(gdm_row) drow['recipients'] = await self._gdm_recipients(channel_id) + drow['last_message_id'] = await self.chan_last_message_str( + channel_id + ) return drow diff --git a/litecord/user_storage.py b/litecord/user_storage.py index 972e16a..bcb3191 100644 --- a/litecord/user_storage.py +++ b/litecord/user_storage.py @@ -338,3 +338,13 @@ class UserStorage: ) ) """, user_id, peer_id) + + async def get_gdms_internal(self, user_id) -> List[int]: + """Return a list of Group DM IDs the user is a member of.""" + rows = await self.db.fetch(""" + SELECT id + FROM group_dm_members + WHERE member_id = $1 + """, user_id) + + return [r['id'] for r in rows] From 89f013c7785da4f6efc389ac72dc67a1bcc1f200 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:03:31 -0300 Subject: [PATCH 07/24] dm_channels: s/user_id/peer_id --- litecord/blueprints/dm_channels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 2e8f41f..4773b26 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -84,7 +84,7 @@ async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): await _raw_gdm_add(channel_id, peer_id) chan = await app.storage.get_channel(channel_id) - await app.dispatcher.dispatch('user', user_id, 'CHANNEL_CREATE', chan) + await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_CREATE', chan) await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) @@ -100,7 +100,7 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): await _raw_gdm_remove(channel_id, peer_id) chan = await app.storage.get_channel(channel_id) - await app.dispatcher.dispatch('user', user_id, 'CHANNEL_DELETE', chan) + await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_DELETE', chan) await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) From 3247323a9554de90dc5767214c4fed88ed562b77 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:13:16 -0300 Subject: [PATCH 08/24] errors: only use err msg map when args don't have it --- litecord/errors.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/litecord/errors.py b/litecord/errors.py index 25e7baa..1e9942a 100644 --- a/litecord/errors.py +++ b/litecord/errors.py @@ -77,14 +77,14 @@ class LitecordError(Exception): @property def message(self) -> str: """Get an error's message string.""" - err_code = getattr(self, 'error_code', None) - - if err_code is not None: - return ERR_MSG_MAP.get(err_code) or self.args[0] - try: return self.args[0] except IndexError: + err_code = getattr(self, 'error_code', None) + + if err_code is not None: + return ERR_MSG_MAP.get(err_code) or self.args[0] + return repr(self) @property From cae72fb9516ba4125ab5c2209297b933d9ec4f45 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:17:08 -0300 Subject: [PATCH 09/24] checks: fix bug on channel type restrictions --- litecord/blueprints/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litecord/blueprints/checks.py b/litecord/blueprints/checks.py index 5a6846c..e897c21 100644 --- a/litecord/blueprints/checks.py +++ b/litecord/blueprints/checks.py @@ -66,7 +66,7 @@ async def channel_check(user_id, channel_id, *, ctype = ChannelType(chan_type) - if not isinstance(only, list): + if only and not isinstance(only, list): only = [only] if only and ctype not in only: From c8c42f5a88044a666dd5100aca7ec80f4c156075 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:26:56 -0300 Subject: [PATCH 10/24] dm_channels: fix typo --- litecord/blueprints/dm_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 4773b26..9a46a02 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -104,7 +104,7 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) -@bp.route('//receipients/', methods=['PUT']) +@bp.route('//recipients/', methods=['PUT']) async def add_to_group_dm(dm_chan, peer_id): """Adds a member to a group dm OR creates a group dm.""" user_id = await token_check() From 39c5867c4650312cdf95a0524a229a0aafb1c72c Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:32:39 -0300 Subject: [PATCH 11/24] dm_channels: fix CHANNEL_CREATE dispatch in gdm_create --- litecord/blueprints/dm_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 9a46a02..b35b9c5 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -68,7 +68,7 @@ async def _gdm_create(user_id, peer_id) -> int: await app.dispatcher.sub('channel', channel_id, peer_id) chan = await app.storage.get_channel(channel_id) - await app.dispatcher.dispatch('channel', 'CHANNEL_CREATE', chan) + await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_CREATE', chan) return channel_id From 83108bc367f31b83c7df932711195c2c8b467df6 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:37:44 -0300 Subject: [PATCH 12/24] websocket: add gdms to private_channels list - user_storage: add get_gdms --- litecord/gateway/websocket.py | 7 ++++++- litecord/storage.py | 1 + litecord/user_storage.py | 13 +++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/litecord/gateway/websocket.py b/litecord/gateway/websocket.py index 3c7a67e..ca2d444 100644 --- a/litecord/gateway/websocket.py +++ b/litecord/gateway/websocket.py @@ -372,11 +372,16 @@ class GatewayWebsocket: # user, fetch info uready = await self._user_ready() + private_channels = ( + await self.user_storage.get_dms(user_id) + + await self.user_storage.get_gdms(user_id) + ) + await self.dispatch('READY', {**{ 'v': 6, 'user': user, - 'private_channels': await self.user_storage.get_dms(user_id), + 'private_channels': private_channels, 'guilds': guilds, 'session_id': self.state.session_id, diff --git a/litecord/storage.py b/litecord/storage.py index 380f97f..54fb8e6 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -436,6 +436,7 @@ class Storage: """, channel_id) drow = dict(gdm_row) + drow['type'] = chan_type drow['recipients'] = await self._gdm_recipients(channel_id) drow['last_message_id'] = await self.chan_last_message_str( channel_id diff --git a/litecord/user_storage.py b/litecord/user_storage.py index bcb3191..36e7cad 100644 --- a/litecord/user_storage.py +++ b/litecord/user_storage.py @@ -348,3 +348,16 @@ class UserStorage: """, user_id) return [r['id'] for r in rows] + + async def get_gdms(self, user_id) -> List[Dict[str, Any]]: + """Get list of group DMs a user is in.""" + gdm_ids = await self.get_gdms_internal(user_id) + + res = [] + + for gdm_id in gdm_ids: + res.append( + await self.storage.get_channel(gdm_id) + ) + + return res From 728f2251dee67dd3d2fcb96a4b15f065b8a1b15a Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 21:51:11 -0300 Subject: [PATCH 13/24] storage: add optional user_id kwarg to ignore recipient - storage: give gdm id as str --- litecord/storage.py | 14 ++++++++++---- litecord/user_storage.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/litecord/storage.py b/litecord/storage.py index 54fb8e6..4554589 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -367,20 +367,24 @@ class Storage: return [r['member_id'] for r in user_ids] - async def _gdm_recipients(self, channel_id: int) -> List[int]: + async def _gdm_recipients(self, channel_id: int, + reference_id: int = None) -> List[int]: """Get the list of users that are recipients of the given Group DM.""" recipients = await self.gdm_recipient_ids(channel_id) res = [] for user_id in recipients: + if user_id == reference_id: + continue + res.append( await self.get_user(user_id) ) return res - async def get_channel(self, channel_id: int) -> Dict[str, Any]: + async def get_channel(self, channel_id: int, **kwargs) -> Dict[str, Any]: """Fetch a single channel's information.""" chan_type = await self.get_chan_type(channel_id) ctype = ChannelType(chan_type) @@ -430,14 +434,16 @@ class Storage: return drow elif ctype == ChannelType.GROUP_DM: gdm_row = await self.db.fetchrow(""" - SELECT id, owner_id::text, name, icon + SELECT id::text, owner_id::text, name, icon FROM group_dm_channels WHERE id = $1 """, channel_id) drow = dict(gdm_row) drow['type'] = chan_type - drow['recipients'] = await self._gdm_recipients(channel_id) + drow['recipients'] = await self._gdm_recipients( + channel_id, kwargs.get('user_id') + ) drow['last_message_id'] = await self.chan_last_message_str( channel_id ) diff --git a/litecord/user_storage.py b/litecord/user_storage.py index 36e7cad..baaeb2a 100644 --- a/litecord/user_storage.py +++ b/litecord/user_storage.py @@ -357,7 +357,7 @@ class UserStorage: for gdm_id in gdm_ids: res.append( - await self.storage.get_channel(gdm_id) + await self.storage.get_channel(gdm_id, user_id=user_id) ) return res From 8bfc94f71275b4baa72b008001dbc42d77076d49 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 22:34:15 -0300 Subject: [PATCH 14/24] system_messages: add raw handlers for GDM system messages --- litecord/system_messages.py | 107 ++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/litecord/system_messages.py b/litecord/system_messages.py index 33a10a0..0c1ebfb 100644 --- a/litecord/system_messages.py +++ b/litecord/system_messages.py @@ -17,11 +17,14 @@ along with this program. If not, see . """ +from logbook import Logger + from litecord.snowflake import get_snowflake from litecord.enums import MessageType +log = Logger(__name__) -async def _handle_pin_msg(app, channel_id, pinned_id, author_id): +async def _handle_pin_msg(app, channel_id, _pinned_id, author_id): """Handle a message pin.""" new_id = get_snowflake() @@ -41,12 +44,108 @@ async def _handle_pin_msg(app, channel_id, pinned_id, author_id): return new_id +# TODO: decrease repetition between add and remove handlers +async def _handle_recp_add(app, channel_id, author_id, peer_id): + new_id = get_snowflake() + + await app.db.execute( + """ + INSERT INTO messages + (id, channel_id, author_id, webhook_id, + content, message_type) + VALUES + ($1, $2, $3, NULL, $4, $5) + """, + new_id, channel_id, author_id, + f'<@{peer_id}>', + MessageType.RECIPIENT_ADD.value + ) + + return new_id + + + +async def _handle_recp_rmv(app, channel_id, author_id, peer_id): + new_id = get_snowflake() + + await app.db.execute( + """ + INSERT INTO messages + (id, channel_id, author_id, webhook_id, + content, message_type) + VALUES + ($1, $2, $3, NULL, $4, $5) + """, + new_id, channel_id, author_id, + f'<@{peer_id}>', + MessageType.RECIPIENT_REMOVE.value + ) + + return new_id + + +async def _handle_gdm_name_edit(app, channel_id, author_id): + new_id = get_snowflake() + + gdm_name = await app.db.fetchval(""" + SELECT name FROM group_dm_channels + WHERE id = $1 + """, channel_id) + + if not gdm_name: + log.warning('no gdm name found for sys message') + return + + await app.db.execute( + """ + INSERT INTO messages + (id, channel_id, author_id, webhook_id, + content, message_type) + VALUES + ($1, $2, $3, NULL, $4, $5) + """, + new_id, channel_id, author_id, + gdm_name, + MessageType.RECIPIENT_ADD.value + ) + + return new_id + + +async def _handle_gdm_icon_edit(app, channel_id, author_id): + new_id = get_snowflake() + + await app.db.execute( + """ + INSERT INTO messages + (id, channel_id, author_id, webhook_id, + content, message_type) + VALUES + ($1, $2, $3, NULL, $4, $5) + """, + new_id, channel_id, author_id, + '', + MessageType.CHANNEL_ICON_CHANGE.value + ) + + return new_id + + async def send_sys_message(app, channel_id: int, m_type: MessageType, *args, **kwargs) -> int: """Send a system message.""" - handler = { - MessageType.CHANNEL_PINNED_MESSAGE: _handle_pin_msg, - }[m_type] + try: + handler = { + MessageType.CHANNEL_PINNED_MESSAGE: _handle_pin_msg, + + # gdm specific + MessageType.RECIPIENT_ADD: _handle_recp_add, + MessageType.RECIPIENT_REMOVE: _handle_recp_rmv, + MessageType.CHANNEL_NAME_CHANGE: _handle_gdm_name_edit, + MessageType.CHANNEL_ICON_CHANGE: _handle_gdm_icon_edit + }[m_type] + except KeyError: + raise ValueError('Invalid system message type') message_id = await handler(app, channel_id, *args, **kwargs) From 7a9453a1292336a46867baa00e6dd609c11665b8 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 22:40:06 -0300 Subject: [PATCH 15/24] add RECIPIENT_ADD / RECIPIENT_REMOVE sys messages --- litecord/blueprints/dm_channels.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index b35b9c5..1a66494 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -22,9 +22,10 @@ from logbook import Logger from litecord.blueprints.auth import token_check from litecord.blueprints.checks import channel_check -from litecord.enums import ChannelType +from litecord.enums import ChannelType, MessageType from litecord.errors import BadRequest, Forbidden from litecord.snowflake import get_snowflake +from litecord.system_messages import send_sys_message log = Logger(__name__) bp = Blueprint('dm_channels', __name__) @@ -87,6 +88,12 @@ async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_CREATE', chan) await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) + if user_id: + await send_sys_message( + app, channel_id, MessageType.RECIPIENT_ADD, + user_id, peer_id + ) + async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): """Remove a member from a GDM. @@ -101,7 +108,18 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): chan = await app.storage.get_channel(channel_id) await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_DELETE', chan) - await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) + await app.dispatcher.dispatch( + 'channel', channel_id, 'CHANNEL_RECIPIENT_REMOVE', { + 'channel_id': str(channel_id), + 'user': await app.storage.get_user(peer_id) + } + ) + + if user_id: + await send_sys_message( + app, channel_id, MessageType.RECIPIENT_REMOVE, + user_id, peer_id + ) @bp.route('//recipients/', methods=['PUT']) From 85a5cfc57a134eeccec4b0656718ddc6c5e22ba2 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 22:59:20 -0300 Subject: [PATCH 16/24] make update_channel more flexible against non-guild chans ..to handle gdm edits --- litecord/blueprints/channels.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index 585d6a6..28f6501 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -411,25 +411,39 @@ async def update_channel(channel_id): user_id = await token_check() ctype, guild_id = await channel_check(user_id, channel_id) - if ctype not in GUILD_CHANS: - raise ChannelNotFound('Can not edit non-guild channels.') + if ctype not in (ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE, + ChannelType.GROUP_DM): + raise ChannelNotFound('unable to edit unsupported chan type') - await channel_perm_check(user_id, channel_id, 'manage_channels') - j = validate(await request.get_json(), CHAN_UPDATE) + is_guild = ctype in GUILD_CHANS - # TODO: categories? + if is_guild: + await channel_perm_check(user_id, channel_id, 'manage_channels') + + j = validate(await request.get_json(), + CHAN_UPDATE if is_guild else GROUP_DM_UPDATE) + + # TODO: categories update_handler = { ChannelType.GUILD_TEXT: _update_text_channel, ChannelType.GUILD_VOICE: _update_voice_channel, + # ChannelType.GROUP_DM: _update_group_dm, }[ctype] - await _update_channel_common(channel_id, guild_id, j) + if is_guild: + await _update_channel_common(channel_id, guild_id, j) + await update_handler(channel_id, j) chan = await app.storage.get_channel(channel_id) + if is_guild: + await app.dispatcher.dispatch( + 'guild', guild_id, 'CHANNEL_UPDATE', chan) + else: + await app.dispatcher.dispatch( + 'channel', channel_id, 'CHANNEL_UPDATE', chan) - await app.dispatcher.dispatch('guild', guild_id, 'CHANNEL_UPDATE', chan) return jsonify(chan) From 0b99149053df7bf87db20e8c1c4044beaab5ef0c Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 23:13:07 -0300 Subject: [PATCH 17/24] add update gdm handler for update_channel --- litecord/blueprints/channels.py | 24 ++++++++++++++++++++++-- litecord/schemas.py | 8 ++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index 28f6501..d620268 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -26,7 +26,7 @@ from litecord.auth import token_check from litecord.enums import ChannelType, GUILD_CHANS from litecord.errors import ChannelNotFound from litecord.schemas import ( - validate, CHAN_UPDATE, CHAN_OVERWRITE, SEARCH_CHANNEL + validate, CHAN_UPDATE, CHAN_OVERWRITE, SEARCH_CHANNEL, GROUP_DM_UPDATE ) from litecord.blueprints.checks import channel_check, channel_perm_check @@ -405,6 +405,26 @@ async def _update_voice_channel(channel_id: int, j: dict): await _common_guild_chan(channel_id, j) +async def _update_group_dm(channel_id: int, j: dict): + if 'name' in j: + await app.db.execute(""" + UPDATE group_dm_channels + SET name = $1 + WHERE id = $2 + """, j['name'], channel_id) + + if 'icon' in j: + new_icon = await app.icons.update( + 'channel-icons', channel_id, j['icon'], always_icon=True + ) + + await app.db.execute(""" + UPDATE group_dm_channels + SET icon = $1 + WHERE id = $2 + """, new_icon, channel_id) + + @bp.route('/', methods=['PUT', 'PATCH']) async def update_channel(channel_id): """Update a channel's information""" @@ -427,7 +447,7 @@ async def update_channel(channel_id): update_handler = { ChannelType.GUILD_TEXT: _update_text_channel, ChannelType.GUILD_VOICE: _update_voice_channel, - # ChannelType.GROUP_DM: _update_group_dm, + ChannelType.GROUP_DM: _update_group_dm, }[ctype] if is_guild: diff --git a/litecord/schemas.py b/litecord/schemas.py index e336eba..c39c46d 100644 --- a/litecord/schemas.py +++ b/litecord/schemas.py @@ -581,6 +581,14 @@ CREATE_GROUP_DM = { }, } +GROUP_DM_UPDATE = { + 'name': { + 'type': 'guild_name', + 'required': False + }, + 'icon': {'type': 'b64_icon', 'required': False, 'nullable': True}, +} + SPECIFIC_FRIEND = { 'username': {'type': 'username'}, 'discriminator': {'type': 'discriminator'} From ed27bb605aac5f3c14e5b8f076cd0c028bbfbfd6 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 23:15:29 -0300 Subject: [PATCH 18/24] add channel-icons icon scope handling --- litecord/blueprints/icons.py | 6 ++++++ litecord/images.py | 13 +++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/litecord/blueprints/icons.py b/litecord/blueprints/icons.py index 86662b2..9daa45b 100644 --- a/litecord/blueprints/icons.py +++ b/litecord/blueprints/icons.py @@ -76,3 +76,9 @@ async def _get_user_avatar(user_id, avatar_file): # @bp.route('/app-icons//.') async def get_app_icon(application_id, icon_hash, ext): pass + + +@bp.route('/channel-icons//', methods=['GET']) +async def _get_gdm_icon(guild_id: int, icon_file: str): + icon_hash, ext = splitext_(icon_file) + return await send_icon('channel-icons', guild_id, icon_hash, ext=ext) diff --git a/litecord/images.py b/litecord/images.py index bf3434c..8c1012f 100644 --- a/litecord/images.py +++ b/litecord/images.py @@ -161,21 +161,18 @@ def parse_data_uri(string) -> tuple: def _gen_update_sql(scope: str) -> str: field = { 'user': 'avatar', - 'guild': 'icon' + 'guild': 'icon', + 'channel-icons': 'icon', }[scope] table = { 'user': 'users', - 'guild': 'guilds' - }[scope] - - col = { - 'user': 'id', - 'guild': 'id' + 'guild': 'guilds', + 'channel-icons': 'group_dm_channels' }[scope] return f""" - SELECT {field} FROM {table} WHERE {col} = $1 + SELECT {field} FROM {table} WHERE id = $1 """ From 24cc9e1d254c3454dcbef052bef0004614d559d0 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 23:22:42 -0300 Subject: [PATCH 19/24] channels: use Icon.icon_hash - images: handle generic_get with null icon - images: update group dms on delete icon --- litecord/blueprints/channels.py | 2 +- litecord/images.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index d620268..88f78b7 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -422,7 +422,7 @@ async def _update_group_dm(channel_id: int, j: dict): UPDATE group_dm_channels SET icon = $1 WHERE id = $2 - """, new_icon, channel_id) + """, new_icon.icon_hash, channel_id) @bp.route('/', methods=['PUT', 'PATCH']) diff --git a/litecord/images.py b/litecord/images.py index 8c1012f..854e2b9 100644 --- a/litecord/images.py +++ b/litecord/images.py @@ -274,6 +274,9 @@ class IconManager: async def generic_get(self, scope, key, icon_hash, **kwargs) -> Icon: """Get any icon.""" + if icon_hash is None: + return None + log.debug('GET {} {} {}', scope, key, icon_hash) key = str(key) @@ -406,6 +409,12 @@ class IconManager: WHERE icon = $1 """, icon.icon_hash) + await self.storage.db.execute(""" + UPDATE group_dm_channels + SET icon = NULL + WHERE icon = $1 + """, icon.icon_hash) + await self.storage.db.execute(""" DELETE FROM icons WHERE hash = $1 From 8a76b5a40749a799a3f78405b44abcac01e13156 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 23:39:47 -0300 Subject: [PATCH 20/24] pubsub.channel: add gdm recipient view for chan create/update --- litecord/pubsub/channel.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/litecord/pubsub/channel.py b/litecord/pubsub/channel.py index ef23a6a..3f805b4 100644 --- a/litecord/pubsub/channel.py +++ b/litecord/pubsub/channel.py @@ -22,10 +22,32 @@ from typing import Any from logbook import Logger from .dispatcher import DispatcherWithState +from litecord.enums import ChannelType +from litecord.utils import index_by_func log = Logger(__name__) +def _recipient_view(orig: dict, user_id: int) -> dict: + """Create a copy of the original channel object that doesn't + show the user we are dispatching it to. + + this only applies to group dms and discords' api design that says + a group dms' recipients must not show the original user. + """ + # make a copy or the original channel object + data = dict(orig) + + idx = index_by_func( + lambda user: user['id'] == str(user_id), + data['recipients'] + ) + + data['recipients'].pop(idx) + + return data + + class ChannelDispatcher(DispatcherWithState): """Main channel Pub/Sub logic.""" KEY_TYPE = int @@ -62,7 +84,19 @@ class ChannelDispatcher(DispatcherWithState): await self.unsub(channel_id, user_id) continue - cur_sess = await self._dispatch_states(states, event, data) + cur_sess = 0 + + if event in ('CHANNEL_CREATE', 'CHANNEL_UPDATE') \ + and data.get('type') == ChannelType.GROUP_DM.value: + # we edit the channel payload so it doesn't show + # the user as a recipient + + new_data = _recipient_view(data, user_id) + cur_sess = await self._dispatch_states( + states, event, new_data) + else: + cur_sess = await self._dispatch_states( + states, event, data) sessions.extend(cur_sess) dispatched += len(cur_sess) From ebd4a8a0cda24a1c302f5ccca0e03de91ed292f2 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Feb 2019 23:55:29 -0300 Subject: [PATCH 21/24] channels: send system messages on name/icon change in gdm --- litecord/blueprints/channels.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index 88f78b7..cff6823 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -23,13 +23,14 @@ from quart import Blueprint, request, current_app as app, jsonify from logbook import Logger from litecord.auth import token_check -from litecord.enums import ChannelType, GUILD_CHANS +from litecord.enums import ChannelType, GUILD_CHANS, MessageType from litecord.errors import ChannelNotFound from litecord.schemas import ( validate, CHAN_UPDATE, CHAN_OVERWRITE, SEARCH_CHANNEL, GROUP_DM_UPDATE ) from litecord.blueprints.checks import channel_check, channel_perm_check +from litecord.system_messages import send_sys_message log = Logger(__name__) bp = Blueprint('channels', __name__) @@ -405,7 +406,7 @@ async def _update_voice_channel(channel_id: int, j: dict): await _common_guild_chan(channel_id, j) -async def _update_group_dm(channel_id: int, j: dict): +async def _update_group_dm(channel_id: int, j: dict, author_id: int): if 'name' in j: await app.db.execute(""" UPDATE group_dm_channels @@ -413,6 +414,10 @@ async def _update_group_dm(channel_id: int, j: dict): WHERE id = $2 """, j['name'], channel_id) + await send_sys_message( + app, channel_id, MessageType.CHANNEL_NAME_CHANGE, author_id + ) + if 'icon' in j: new_icon = await app.icons.update( 'channel-icons', channel_id, j['icon'], always_icon=True @@ -424,6 +429,10 @@ async def _update_group_dm(channel_id: int, j: dict): WHERE id = $2 """, new_icon.icon_hash, channel_id) + await send_sys_message( + app, channel_id, MessageType.CHANNEL_ICON_CHANGE, author_id + ) + @bp.route('/', methods=['PUT', 'PATCH']) async def update_channel(channel_id): From 228468635fdfe3025a889ccc4c2e12b25af4ba85 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 17 Feb 2019 00:03:37 -0300 Subject: [PATCH 22/24] channels: pass uid to update_handler - system_messages: fix handle gdm name edit giving wrong msg type --- litecord/blueprints/channels.py | 2 +- litecord/system_messages.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index cff6823..76324dd 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -462,7 +462,7 @@ async def update_channel(channel_id): if is_guild: await _update_channel_common(channel_id, guild_id, j) - await update_handler(channel_id, j) + await update_handler(channel_id, j, user_id) chan = await app.storage.get_channel(channel_id) diff --git a/litecord/system_messages.py b/litecord/system_messages.py index 0c1ebfb..0f13b8e 100644 --- a/litecord/system_messages.py +++ b/litecord/system_messages.py @@ -106,7 +106,7 @@ async def _handle_gdm_name_edit(app, channel_id, author_id): """, new_id, channel_id, author_id, gdm_name, - MessageType.RECIPIENT_ADD.value + MessageType.CHANNEL_NAME_CHANGE.value ) return new_id From da669e5fb2ec2ceee3df4017c195a4edb56e6e43 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 17 Feb 2019 00:10:14 -0300 Subject: [PATCH 23/24] dm_channels: add sub/unsub logic on add/remove of recipient - fix gdm remove call on remove_from_group_dm --- litecord/blueprints/dm_channels.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 1a66494..1f0f4cc 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -87,6 +87,7 @@ async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): chan = await app.storage.get_channel(channel_id) await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_CREATE', chan) await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) + await app.dispatcher.sub('channel', peer_id) if user_id: await send_sys_message( @@ -115,6 +116,8 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): } ) + await app.dispatcher.unsub('channel', peer_id) + if user_id: await send_sys_message( app, channel_id, MessageType.RECIPIENT_REMOVE, @@ -155,8 +158,8 @@ async def add_to_group_dm(dm_chan, peer_id): ) -@bp.route('//recipients/', methods=['DELETE']) -async def remove_from_group_dm(dm_chan, user_id): +@bp.route('//recipients/', methods=['DELETE']) +async def remove_from_group_dm(dm_chan, peer_id): """Remove users from group dm.""" user_id = await token_check() _ctype, owner_id = await channel_check( @@ -166,5 +169,5 @@ async def remove_from_group_dm(dm_chan, user_id): if owner_id != user_id: raise Forbidden('You are now the owner of the group DM') - await _gdm_remove_recipient(dm_chan, user_id) + await _gdm_remove_recipient(dm_chan, peer_id) return '', 204 From 7f7c71b3ebe824793133d1d7027122fe187ff656 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 17 Feb 2019 00:16:26 -0300 Subject: [PATCH 24/24] dm_channels: fix some pubsub stuff - pubsub.channel: make gdm_recipient_view a public function --- litecord/blueprints/dm_channels.py | 19 ++++++++++++++----- litecord/pubsub/channel.py | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/litecord/blueprints/dm_channels.py b/litecord/blueprints/dm_channels.py index 1f0f4cc..a79ed8b 100644 --- a/litecord/blueprints/dm_channels.py +++ b/litecord/blueprints/dm_channels.py @@ -26,6 +26,7 @@ from litecord.enums import ChannelType, MessageType from litecord.errors import BadRequest, Forbidden from litecord.snowflake import get_snowflake from litecord.system_messages import send_sys_message +from litecord.pubsub.channel import gdm_recipient_view log = Logger(__name__) bp = Blueprint('dm_channels', __name__) @@ -85,8 +86,14 @@ async def _gdm_add_recipient(channel_id: int, peer_id: int, *, user_id=None): await _raw_gdm_add(channel_id, peer_id) chan = await app.storage.get_channel(channel_id) - await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_CREATE', chan) - await app.dispatcher.dispatch('channel', channel_id, 'CHANNEL_UPDATE', chan) + + # the reasoning behind gdm_recipient_view is in its docstring. + await app.dispatcher.dispatch( + 'user', peer_id, 'CHANNEL_CREATE', gdm_recipient_view(chan, peer_id)) + + await app.dispatcher.dispatch( + 'channel', channel_id, 'CHANNEL_UPDATE', chan) + await app.dispatcher.sub('channel', peer_id) if user_id: @@ -108,7 +115,11 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): await _raw_gdm_remove(channel_id, peer_id) chan = await app.storage.get_channel(channel_id) - await app.dispatcher.dispatch('user', peer_id, 'CHANNEL_DELETE', chan) + await app.dispatcher.dispatch( + 'user', peer_id, 'CHANNEL_DELETE', gdm_recipient_view(chan, user_id)) + + await app.dispatcher.unsub('channel', peer_id) + await app.dispatcher.dispatch( 'channel', channel_id, 'CHANNEL_RECIPIENT_REMOVE', { 'channel_id': str(channel_id), @@ -116,8 +127,6 @@ async def _gdm_remove_recipient(channel_id: int, peer_id: int, *, user_id=None): } ) - await app.dispatcher.unsub('channel', peer_id) - if user_id: await send_sys_message( app, channel_id, MessageType.RECIPIENT_REMOVE, diff --git a/litecord/pubsub/channel.py b/litecord/pubsub/channel.py index 3f805b4..2b32a1d 100644 --- a/litecord/pubsub/channel.py +++ b/litecord/pubsub/channel.py @@ -28,7 +28,7 @@ from litecord.utils import index_by_func log = Logger(__name__) -def _recipient_view(orig: dict, user_id: int) -> dict: +def gdm_recipient_view(orig: dict, user_id: int) -> dict: """Create a copy of the original channel object that doesn't show the user we are dispatching it to. @@ -91,7 +91,7 @@ class ChannelDispatcher(DispatcherWithState): # we edit the channel payload so it doesn't show # the user as a recipient - new_data = _recipient_view(data, user_id) + new_data = gdm_recipient_view(data, user_id) cur_sess = await self._dispatch_states( states, event, new_data) else: