From 0ca45c781e3a6d662cc18bdd6b55db77c8a7a7b1 Mon Sep 17 00:00:00 2001 From: Luna Mendes Date: Wed, 4 Jul 2018 17:40:27 -0300 Subject: [PATCH] blueprints.channels: add implementations for some routes more specifically edit/delete message, and all 3 pin-related routes. - snowflake: add snowflake_datetime --- litecord/blueprints/channels.py | 148 +++++++++++++++++++++++++++++++- litecord/snowflake.py | 11 ++- run.py | 2 +- schema.sql | 2 +- 4 files changed, 157 insertions(+), 6 deletions(-) diff --git a/litecord/blueprints/channels.py b/litecord/blueprints/channels.py index 86ef572..79a8468 100644 --- a/litecord/blueprints/channels.py +++ b/litecord/blueprints/channels.py @@ -4,7 +4,7 @@ from quart import Blueprint, request, current_app as app, jsonify from logbook import Logger from ..auth import token_check -from ..snowflake import get_snowflake +from ..snowflake import get_snowflake, snowflake_datetime from ..enums import ChannelType, MessageType from ..errors import Forbidden, BadRequest, ChannelNotFound, MessageNotFound from ..schemas import validate, MESSAGE_CREATE @@ -73,7 +73,6 @@ async def get_messages(channel_id): result.append(msg) log.info('Fetched {} messages', len(result)) - print(result) return jsonify(result) @@ -119,6 +118,151 @@ async def create_message(channel_id): return jsonify(payload) +@bp.route('//messages/', methods=['PATCH']) +async def edit_message(channel_id, message_id): + user_id = await token_check() + guild_id = await channel_check(user_id, channel_id) + + author_id = await app.db.fetchval(""" + SELECT author_id FROM messages + WHERE messages.id = $1 + """, message_id) + + if not author_id == user_id: + raise Forbidden('You can not edit this message') + + j = await request.get_json() + updated = 'content' in j or 'embed' in j + + if 'content' in j: + await app.db.execute(""" + UPDATE messages + SET content=$1 + WHERE messages.id = $2 + """, j['content'], message_id) + + # TODO: update embed + + message = await app.storage.get_message(message_id) + + # only dispatch MESSAGE_CREATE if we actually had any update to start with + if updated: + await app.dispatcher.dispatch_guild(guild_id, + 'MESSAGE_UPDATE', message) + + return jsonify(message) + + +@bp.route('//messages/', methods=['DELETE']) +async def delete_message(channel_id, message_id): + user_id = await token_check() + guild_id = await channel_check(user_id, channel_id) + + author_id = await app.db.fetchval(""" + SELECT author_id FROM messages + WHERE messages.id = $1 + """, message_id) + + # TODO: MANAGE_MESSAGES permission check + if not author_id == user_id: + raise Forbidden('You can not delete this message') + + await app.db.execute(""" + DELETE FROM messages + WHERE messages.id = $1 + """, message_id) + + await app.dispatcher.dispatch_guild(guild_id, 'MESSAGE_DELETE', { + 'id': str(message_id), + 'channel_id': str(channel_id) + }) + + return '', 204 + + +@bp.route('//pins', methods=['GET']) +async def get_pins(channel_id): + user_id = await token_check() + await channel_check(user_id, channel_id) + + ids = await app.db.fetch(""" + SELECT message_id + FROM channel_pins + WHERE channel_id = $1 + ORDER BY message_id ASC + """, channel_id) + + ids = [r['message_id'] for r in ids] + res = [] + + for message_id in ids: + message = await app.storage.get_message(message_id) + if message is not None: + res.append(message) + + return jsonify(message) + + +@bp.route('//pins/', methods=['PUT']) +async def add_pin(channel_id, message_id): + user_id = await token_check() + guild_id = await channel_check(user_id, channel_id) + + # TODO: check MANAGE_MESSAGES permission + + await app.db.execute(""" + INSERT INTO channel_pins (channel_id, message_id) + VALUES ($1, $2) + """, channel_id, message_id) + + row = await app.db.fetchrow(""" + SELECT message_id + FROM channel_pins + WHERE channel_id = $1 + ORDER BY message_id ASC + LIMIT 1 + """, channel_id) + + timestamp = snowflake_datetime(row['message_id']) + + await app.dispatcher.dispatch_guild(guild_id, 'CHANNEL_PINS_UPDATE', { + 'channel_id': str(channel_id), + 'last_pin_timestamp': timestamp.isoformat() + }) + + return '', 204 + + +@bp.route('//pins/', methods=['DELETE']) +async def delete_pin(channel_id, message_id): + user_id = await token_check() + guild_id = await channel_check(user_id, channel_id) + + # TODO: check MANAGE_MESSAGES permission + + await app.db.execute(""" + DELETE FROM channel_pins + WHERE channel_id = $1 AND message_id = $2 + """, channel_id, message_id) + + row = await app.db.fetchrow(""" + SELECT message_id + FROM channel_pins + WHERE channel_id = $1 + ORDER BY message_id ASC + LIMIT 1 + """, channel_id) + + timestamp = snowflake_datetime(row['message_id']) + + await app.dispatcher.dispatch_guild(guild_id, 'CHANNEL_PINS_UPDATE', { + 'channel_id': str(channel_id), + 'last_pin_timestamp': timestamp.isoformat() + }) + + return '', 204 + + @bp.route('//typing', methods=['POST']) async def trigger_typing(channel_id): user_id = await token_check() diff --git a/litecord/snowflake.py b/litecord/snowflake.py index f1c473f..2962cd7 100644 --- a/litecord/snowflake.py +++ b/litecord/snowflake.py @@ -5,10 +5,11 @@ snowflake.py - snowflake helper functions File brought in from litecord-reference(https://github.com/lnmds/litecord-reference) """ -import time -import hashlib import os +import time import base64 +import hashlib +import datetime # encoded in ms EPOCH = 1420070400000 @@ -90,6 +91,12 @@ def snowflake_time(snowflake: Snowflake) -> float: return timestamp / 1000 +def snowflake_datetime(snowflake: Snowflake) -> datetime.datetime: + """Return a datetime object representing the snowflake.""" + unix_ts = snowflake_time(snowflake) + return datetime.datetime.fromtimestamp(unix_ts) + + def get_snowflake(): """Generate a snowflake""" return _snowflake(int(time.time() * 1000)) diff --git a/run.py b/run.py index b3794a6..0130513 100644 --- a/run.py +++ b/run.py @@ -64,7 +64,7 @@ async def app_before_serving(): # We wrap the main websocket_handler # so we can pass quart's app object. await websocket_handler((app.db, app.state_manager, app.storage, - app.loop, app.dispatcher), ws, url) + app.loop, app.dispatcher), ws, url) ws_future = websockets.serve(_wrapper, host, port) diff --git a/schema.sql b/schema.sql index 67df08a..aa63764 100644 --- a/schema.sql +++ b/schema.sql @@ -372,6 +372,6 @@ CREATE TABLE IF NOT EXISTS message_reactions ( CREATE TABLE IF NOT EXISTS channel_pins ( channel_id bigint REFERENCES channels (id) UNIQUE, - message_id bigint REFERENCEs messages (id), + message_id bigint REFERENCES messages (id), PRIMARY KEY (channel_id, message_id) );