blueprints: split channels to channel.messages bp

This commit is contained in:
Luna Mendes 2018-10-28 17:29:43 -03:00
parent 8352a3cab4
commit f2d5913672
5 changed files with 223 additions and 203 deletions

View File

@ -0,0 +1 @@
from .messages import bp as channel_messages

View File

@ -0,0 +1,212 @@
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.blueprints.dms import try_dm_state
from litecord.errors import MessageNotFound, Forbidden
from litecord.enums import MessageType, ChannelType, GUILD_CHANS
from litecord.snowflake import get_snowflake
from litecord.schemas import validate, MESSAGE_CREATE
log = Logger(__name__)
bp = Blueprint('channel_messages', __name__)
@bp.route('/<int:channel_id>/messages', methods=['GET'])
async def get_messages(channel_id):
user_id = await token_check()
await channel_check(user_id, channel_id)
# TODO: before, after, around keys
message_ids = await app.db.fetch(f"""
SELECT id
FROM messages
WHERE channel_id = $1
ORDER BY id DESC
LIMIT 100
""", channel_id)
result = []
for message_id in message_ids:
msg = await app.storage.get_message(message_id['id'])
if msg is None:
continue
result.append(msg)
log.info('Fetched {} messages', len(result))
return jsonify(result)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['GET'])
async def get_single_message(channel_id, message_id):
user_id = await token_check()
await channel_check(user_id, channel_id)
# TODO: check READ_MESSAGE_HISTORY permissions
message = await app.storage.get_message(message_id)
if not message:
raise MessageNotFound()
return jsonify(message)
async def _dm_pre_dispatch(channel_id, peer_id):
"""Do some checks pre-MESSAGE_CREATE so we
make sure the receiving party will handle everything."""
# check the other party's dm_channel_state
dm_state = await app.db.fetchval("""
SELECT dm_id
FROM dm_channel_state
WHERE user_id = $1 AND dm_id = $2
""", peer_id, channel_id)
if dm_state:
# the peer already has the channel
# opened, so we don't need to do anything
return
dm_chan = await app.storage.get_channel(channel_id)
# dispatch CHANNEL_CREATE so the client knows which
# channel the future event is about
await app.dispatcher.dispatch_user(peer_id, 'CHANNEL_CREATE', dm_chan)
# subscribe the peer to the channel
await app.dispatcher.sub('channel', channel_id, peer_id)
# insert it on dm_channel_state so the client
# is subscribed on the future
await try_dm_state(peer_id, channel_id)
@bp.route('/<int:channel_id>/messages', methods=['POST'])
async def create_message(channel_id):
user_id = await token_check()
ctype, guild_id = await channel_check(user_id, channel_id)
j = validate(await request.get_json(), MESSAGE_CREATE)
message_id = get_snowflake()
# TODO: check SEND_MESSAGES permission
# TODO: check connection to the gateway
await app.db.execute(
"""
INSERT INTO messages (id, channel_id, author_id, content, tts,
mention_everyone, nonce, message_type)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
""",
message_id,
channel_id,
user_id,
j['content'],
# TODO: check SEND_TTS_MESSAGES
j.get('tts', False),
# TODO: check MENTION_EVERYONE permissions
'@everyone' in j['content'],
int(j.get('nonce', 0)),
MessageType.DEFAULT.value
)
payload = await app.storage.get_message(message_id)
if ctype == ChannelType.DM:
# guild id here is the peer's ID.
await _dm_pre_dispatch(channel_id, guild_id)
await app.dispatcher.dispatch('channel', channel_id,
'MESSAGE_CREATE', payload)
# TODO: dispatch the MESSAGE_CREATE to any mentioning user.
if ctype == ChannelType.GUILD_TEXT:
for str_uid in payload['mentions']:
uid = int(str_uid)
await app.db.execute("""
UPDATE user_read_state
SET mention_count += 1
WHERE user_id = $1 AND channel_id = $2
""", uid, channel_id)
return jsonify(payload)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['PATCH'])
async def edit_message(channel_id, message_id):
user_id = await token_check()
_ctype, 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_UPDATE if we actually had any update to start with
if updated:
await app.dispatcher.dispatch('channel', channel_id,
'MESSAGE_UPDATE', message)
return jsonify(message)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['DELETE'])
async def delete_message(channel_id, message_id):
user_id = await token_check()
_ctype, 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 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(
'channel', channel_id,
'MESSAGE_DELETE', {
'id': str(message_id),
'channel_id': str(channel_id),
# for lazy guilds
'guild_id': str(guild_id),
})
return '', 204

View File

@ -4,13 +4,11 @@ from quart import Blueprint, request, current_app as app, jsonify
from logbook import Logger
from ..auth import token_check
from ..snowflake import get_snowflake, snowflake_datetime
from ..enums import ChannelType, MessageType, GUILD_CHANS
from ..errors import Forbidden, ChannelNotFound, MessageNotFound
from ..schemas import validate, MESSAGE_CREATE
from ..snowflake import snowflake_datetime
from ..enums import ChannelType, GUILD_CHANS
from ..errors import ChannelNotFound
from .checks import channel_check, guild_check
from .dms import try_dm_state
from .checks import channel_check
log = Logger(__name__)
bp = Blueprint('channels', __name__)
@ -215,202 +213,6 @@ async def close_channel(channel_id):
return '', 404
@bp.route('/<int:channel_id>/messages', methods=['GET'])
async def get_messages(channel_id):
user_id = await token_check()
await channel_check(user_id, channel_id)
# TODO: before, after, around keys
message_ids = await app.db.fetch(f"""
SELECT id
FROM messages
WHERE channel_id = $1
ORDER BY id DESC
LIMIT 100
""", channel_id)
result = []
for message_id in message_ids:
msg = await app.storage.get_message(message_id['id'])
if msg is None:
continue
result.append(msg)
log.info('Fetched {} messages', len(result))
return jsonify(result)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['GET'])
async def get_single_message(channel_id, message_id):
user_id = await token_check()
await channel_check(user_id, channel_id)
# TODO: check READ_MESSAGE_HISTORY permissions
message = await app.storage.get_message(message_id)
if not message:
raise MessageNotFound()
return jsonify(message)
async def _dm_pre_dispatch(channel_id, peer_id):
"""Do some checks pre-MESSAGE_CREATE so we
make sure the receiving party will handle everything."""
# check the other party's dm_channel_state
dm_state = await app.db.fetchval("""
SELECT dm_id
FROM dm_channel_state
WHERE user_id = $1 AND dm_id = $2
""", peer_id, channel_id)
if dm_state:
# the peer already has the channel
# opened, so we don't need to do anything
return
dm_chan = await app.storage.get_channel(channel_id)
# dispatch CHANNEL_CREATE so the client knows which
# channel the future event is about
await app.dispatcher.dispatch_user(peer_id, 'CHANNEL_CREATE', dm_chan)
# subscribe the peer to the channel
await app.dispatcher.sub('channel', channel_id, peer_id)
# insert it on dm_channel_state so the client
# is subscribed on the future
await try_dm_state(peer_id, channel_id)
@bp.route('/<int:channel_id>/messages', methods=['POST'])
async def create_message(channel_id):
user_id = await token_check()
ctype, guild_id = await channel_check(user_id, channel_id)
j = validate(await request.get_json(), MESSAGE_CREATE)
message_id = get_snowflake()
# TODO: check SEND_MESSAGES permission
# TODO: check connection to the gateway
await app.db.execute(
"""
INSERT INTO messages (id, channel_id, author_id, content, tts,
mention_everyone, nonce, message_type)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
""",
message_id,
channel_id,
user_id,
j['content'],
# TODO: check SEND_TTS_MESSAGES
j.get('tts', False),
# TODO: check MENTION_EVERYONE permissions
'@everyone' in j['content'],
int(j.get('nonce', 0)),
MessageType.DEFAULT.value
)
payload = await app.storage.get_message(message_id)
if ctype == ChannelType.DM:
# guild id here is the peer's ID.
await _dm_pre_dispatch(channel_id, guild_id)
await app.dispatcher.dispatch('channel', channel_id,
'MESSAGE_CREATE', payload)
# TODO: dispatch the MESSAGE_CREATE to any mentioning user.
if ctype == ChannelType.GUILD_TEXT:
for str_uid in payload['mentions']:
uid = int(str_uid)
await app.db.execute("""
UPDATE user_read_state
SET mention_count += 1
WHERE user_id = $1 AND channel_id = $2
""", uid, channel_id)
return jsonify(payload)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['PATCH'])
async def edit_message(channel_id, message_id):
user_id = await token_check()
_ctype, 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_UPDATE if we actually had any update to start with
if updated:
await app.dispatcher.dispatch('channel', channel_id,
'MESSAGE_UPDATE', message)
return jsonify(message)
@bp.route('/<int:channel_id>/messages/<int:message_id>', methods=['DELETE'])
async def delete_message(channel_id, message_id):
user_id = await token_check()
_ctype, 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 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(
'channel', channel_id,
'MESSAGE_DELETE', {
'id': str(message_id),
'channel_id': str(channel_id),
# for lazy guilds
'guild_id': str(guild_id),
})
return '', 204
@bp.route('/<int:channel_id>/pins', methods=['GET'])
async def get_pins(channel_id):
user_id = await token_check()

View File

@ -183,4 +183,3 @@ async def begin_guild_prune(guild_id):
return jsonify({
'pruned': len(member_ids)
})

6
run.py
View File

@ -22,6 +22,10 @@ from litecord.blueprints.guild import (
guild_roles, guild_members, guild_channels, guild_mod
)
from litecord.blueprints.channel import (
channel_messages
)
from litecord.gateway import websocket_handler
from litecord.errors import LitecordError
from litecord.gateway.state_manager import StateManager
@ -68,6 +72,8 @@ bps = {
guild_mod: '/guilds',
channels: '/channels',
channel_messages: '/channels',
webhooks: None,
science: None,
voice: '/voice',