diff --git a/litecord/blueprints/channel/messages.py b/litecord/blueprints/channel/messages.py index f1c8a5c..b089e5c 100644 --- a/litecord/blueprints/channel/messages.py +++ b/litecord/blueprints/channel/messages.py @@ -18,11 +18,12 @@ along with this program. If not, see . """ import re +import json +from PIL import Image 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, channel_perm_check from litecord.blueprints.dms import try_dm_state @@ -311,9 +312,95 @@ async def process_url_embed(config, storage, dispatcher, session, payload: dict) 'channel', channel_id, 'MESSAGE_UPDATE', update_payload) +async def _msg_input() -> tuple: + """Extract the json input and any file information + the client gave to us in the request. + + This only applies to create message route. + """ + form = await request.form + request_json = await request.get_json() or {} + + # NOTE: embed isn't set on form data + json_from_form = { + 'content': form.get('content', ''), + 'nonce': form.get('nonce', '0'), + 'tts': json.loads(form.get('tts', 'false')), + } + + json_from_form.update(request_json) + + files = await request.files + # we don't really care about the given fields on the files dict + return json_from_form, [v for k, v in files.items()] + + +def _check_content(payload: dict, files: list): + """Check if there is actually any content being sent to us.""" + has_content = bool(payload.get('content', '')) + has_embed = 'embed' in payload + has_files = len(files) > 0 + + has_total_content = has_content or has_embed or has_files + + if not has_total_content: + raise BadRequest('No content has been provided.') + + +async def _add_attachment(message_id: int, attachment_file) -> int: + """Add an attachment to a message. + + Parameters + ---------- + message_id: int + The ID of the message getting the attachment. + attachment_file: quart.FileStorage + quart FileStorage instance of the file. + """ + + attachment_id = get_snowflake() + + # understand file info + is_image = attachment_file.mimetype.startswith('image/') + + img_width, img_height = None, None + + # extract file size + # TODO: this is probably inneficient + file_size = attachment_file.stream.getbuffer().nbytes + + if is_image: + # open with pillow, extract image size + image = Image.open(attachment_file.stream) + img_width, img_height = image.size + image.close() + + # reset it to 0 for later usage + attachment_file.stream.seek(0) + + await app.db.execute( + """ + INSERT INTO attachments + (id, filename, filesize, image, height, width) + VALUES + ($1, $2, $3, $4, $5, $6) + """, + attachment_id, attachment_file.filename, file_size, + is_image, img_width, img_height) + + # add the newly created attachment to the message + await app.db.execute(""" + INSERT INTO message_attachments (message_id, attachment_id) + VALUES ($1, $2) + """, message_id, attachment_id) + + return attachment_id + + @bp.route('//messages', methods=['POST']) async def _create_message(channel_id): """Create a message.""" + user_id = await token_check() ctype, guild_id = await channel_check(user_id, channel_id) @@ -323,7 +410,11 @@ async def _create_message(channel_id): await channel_perm_check(user_id, channel_id, 'send_messages') actual_guild_id = guild_id - j = validate(await request.get_json(), MESSAGE_CREATE) + payload_json, files = await _msg_input() + j = validate(payload_json, MESSAGE_CREATE) + + print(payload_json, files) + _check_content(payload_json, files) # TODO: check connection to the gateway @@ -355,6 +446,10 @@ async def _create_message(channel_id): if 'embed' in j else []), }) + # for each file given, we add it as an attachment + for pre_attachment in files: + await _add_attachment(message_id, pre_attachment) + payload = await app.storage.get_message(message_id, user_id) if ctype == ChannelType.DM: diff --git a/litecord/schemas.py b/litecord/schemas.py index c631572..d21ef54 100644 --- a/litecord/schemas.py +++ b/litecord/schemas.py @@ -384,7 +384,7 @@ MEMBER_UPDATE = { MESSAGE_CREATE = { - 'content': {'type': 'string', 'minlength': 1, 'maxlength': 2000}, + 'content': {'type': 'string', 'minlength': 0, 'maxlength': 2000}, 'nonce': {'type': 'snowflake', 'required': False}, 'tts': {'type': 'boolean', 'required': False},