diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index d6266ee..5cdf9e0 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -99,6 +99,40 @@ async def _update_guild_chan_cat(guild_id: int, channel_id: int): ) +async def delete_messages(channel_id): + await app.db.execute(""" + DELETE FROM channel_pins + WHERE channel_id = $1 + """, channel_id) + + await app.db.execute(""" + DELETE FROM user_read_state + WHERE channel_id = $1 + """, channel_id) + + await app.db.execute(""" + DELETE FROM messages + WHERE channel_id = $1 + """, channel_id) + + +async def guild_cleanup(channel_id): + await app.db.execute(""" + DELETE FROM channel_overwrites + WHERE channel_id = $1 + """, channel_id) + + await app.db.execute(""" + DELETE FROM invites + WHERE channel_id = $1 + """, channel_id) + + await app.db.execute(""" + DELETE FROM webhooks + WHERE channel_id = $1 + """, channel_id) + + @bp.route('/', methods=['DELETE']) async def close_channel(channel_id): user_id = await token_check() @@ -118,15 +152,37 @@ async def close_channel(channel_id): ChannelType.GUILD_CATEGORY: _update_guild_chan_cat, }[ctype] + main_tbl = { + ChannelType.GUILD_TEXT: 'guild_text_channels', + ChannelType.GUILD_VOICE: 'guild_voice_channels', + + # TODO: categories? + }[ctype] + await _update_func(guild_id, channel_id) - # this should take care of deleting all messages as well - # (if any) + # for some reason ON DELETE CASCADE + # didn't work on my setup, so I delete + # everything before moving to the main + # channel table deletes + await delete_messages(channel_id) + await guild_cleanup(channel_id) + + await app.db.execute(f""" + DELETE FROM {main_tbl} + WHERE id = $1 + """, channel_id) + await app.db.execute(""" DELETE FROM guild_channels WHERE id = $1 """, channel_id) + await app.db.execute(""" + DELETE FROM channels + WHERE id = $1 + """, channel_id) + await app.dispatcher.dispatch_guild( guild_id, 'CHANNEL_DELETE', chan) return jsonify(chan) diff --git a/litecord/blueprints/guilds.py b/litecord/blueprints/guilds.py index d14f8f2..3de2927 100644 --- a/litecord/blueprints/guilds.py +++ b/litecord/blueprints/guilds.py @@ -209,22 +209,12 @@ async def create_channel(guild_id): VALUES ($1, $2) """, new_channel_id, channel_type.value) - max_pos = await app.db.fetch(""" + max_pos = await app.db.fetchval(""" SELECT MAX(position) FROM guild_channels WHERE guild_id = $1 """, guild_id) - channel = { - 'id': str(new_channel_id), - 'type': channel_type, - 'guild_id': str(guild_id), - 'position': max_pos + 1, - 'permission_overwrites': [], - 'nsfw': False, - 'name': j['name'], - } - if channel_type == ChannelType.GUILD_TEXT: await app.db.execute(""" INSERT INTO guild_channels (id, guild_id, name, position) @@ -236,15 +226,12 @@ async def create_channel(guild_id): VALUES ($1) """, new_channel_id) - channel['topic'] = None elif channel_type == ChannelType.GUILD_VOICE: - channel['user_limit'] = 0 - channel['bitrate'] = 64 - raise NotImplementedError() - await app.dispatcher.dispatch_guild(guild_id, 'CHANNEL_CREATE', channel) - return jsonify(channel) + chan = await app.storage.get_channel(new_channel_id) + await app.dispatcher.dispatch_guild(guild_id, 'CHANNEL_CREATE', chan) + return jsonify(chan) @bp.route('//channels', methods=['PATCH']) diff --git a/litecord/blueprints/relationships.py b/litecord/blueprints/relationships.py index c0170f9..46e3b03 100644 --- a/litecord/blueprints/relationships.py +++ b/litecord/blueprints/relationships.py @@ -2,7 +2,7 @@ from quart import Blueprint, jsonify, request, current_app as app from asyncpg import UniqueViolationError from ..auth import token_check -from ..schemas import validate, RELATIONSHIP +from ..schemas import validate, RELATIONSHIP, SPECIFIC_FRIEND from ..enums import RelationshipType @@ -15,12 +15,8 @@ async def get_me_relationships(): return jsonify(await app.storage.get_relationships(user_id)) -@bp.route('/@me/relationships/', methods=['PUT']) -async def add_relationship(peer_id: int): - """Add a relationship to the peer.""" - user_id = await token_check() - payload = validate(await request.get_json(), RELATIONSHIP) - rel_type = payload['type'] +async def make_friend(user_id: int, peer_id: int, + rel_type=RelationshipType.FRIEND.value): _friend = RelationshipType.FRIEND.value await app.db.execute(""" @@ -76,7 +72,44 @@ async def add_relationship(peer_id: int): return '', 204 - # its a block. + return + + +@bp.route('/@me/relationships', methods=['POST']) +async def post_relationship(): + user_id = await token_check() + j = validate(await request.get_json(), SPECIFIC_FRIEND) + + uid = await app.storage.search_user(j['username'], + str(j['discriminator'])) + + if not uid: + return '', 404 + + res = await make_friend(user_id, uid) + + # NOTE: don't know what status code should I send + if res is None: + return '', 500 + + return '', 204 + + +@bp.route('/@me/relationships/', methods=['PUT']) +async def add_relationship(peer_id: int): + """Add a relationship to the peer.""" + user_id = await token_check() + payload = validate(await request.get_json(), RELATIONSHIP) + rel_type = payload['type'] + + res = await make_friend(user_id, peer_id, rel_type) + + if res is not None: + return res + + # make_friend did not succeed, so we + # assume it is a block and dispatch + # the respective RELATIONSHIP_ADD. await app.dispatcher.dispatch_user(user_id, 'RELATIONSHIP_ADD', { 'id': str(peer_id), 'type': RelationshipType.BLOCK.value, diff --git a/litecord/schemas.py b/litecord/schemas.py index b4ecdf9..b7476e3 100644 --- a/litecord/schemas.py +++ b/litecord/schemas.py @@ -292,3 +292,8 @@ CREATE_GROUP_DM = { 'required': True, 'schema': {'type': 'snowflake'} } + +SPECIFIC_FRIEND = { + 'username': {'type': 'username'}, + 'discriminator': {'type': 'number'} +} diff --git a/litecord/storage.py b/litecord/storage.py index 9ebc051..0a1a83c 100644 --- a/litecord/storage.py +++ b/litecord/storage.py @@ -91,6 +91,17 @@ class Storage: return duser + async def search_user(self, username: str, discriminator: str) -> int: + """Search a user""" + if len(discriminator) < 4: + # how do we do this in f-strings again..? + discriminator = '%04d' % discriminator + + return await self.db.fetchval(""" + SELECT id FROM users + WHERE username = $1 AND discriminator = $2 + """, username, discriminator) + async def get_guild(self, guild_id: int, user_id=None) -> Dict: """Get gulid payload.""" row = await self.db.fetchrow(""" @@ -232,6 +243,10 @@ class Storage: WHERE channel_id = $1 """, channel_id) + async def chan_last_message_str(self, channel_id: int) -> int: + last_msg = await self.chan_last_message(channel_id) + return str(last_msg) if last_msg is not None else None + async def _channels_extra(self, row) -> Dict: """Fill in more information about a channel.""" channel_type = row['type'] @@ -244,10 +259,11 @@ class Storage: WHERE id = $1 """, row['id']) + last_msg = await self.chan_last_message_str(row['id']) + return {**row, **{ 'topic': topic, - 'last_message_id': str( - await self.chan_last_message(row['id'])) + 'last_message_id': last_msg, }} elif chan_type == ChannelType.GUILD_VOICE: vrow = await self.db.fetchval(""" @@ -328,8 +344,8 @@ class Storage: drow = dict(dm_row) drow['type'] = chan_type - drow['last_message_id'] = str( - await self.chan_last_message(channel_id)) + drow['last_message_id'] = await self.chan_last_message_str( + channel_id) # dms have just two recipients. drow['recipients'] = [ diff --git a/schema.sql b/schema.sql index 846eaff..137e947 100644 --- a/schema.sql +++ b/schema.sql @@ -298,7 +298,7 @@ CREATE TABLE IF NOT EXISTS group_dm_members ( CREATE TABLE IF NOT EXISTS channel_overwrites ( - channel_id bigint REFERENCES channels (id), + channel_id bigint REFERENCES channels (id) ON DELETE CASCADE, -- target_type = 0 -> use target_user -- target_type = 1 -> user target_role