mirror of https://gitlab.com/litecord/litecord.git
webhooks: "finish" execution impl
it can do messages. but many things are still missing.
- channel.messages: export msg_guild_text_mentions
- storage: revamp Storage._inject_author
- schema.sql: remove messages.webhook_id, add message_webhook_info
table
- add 16_messages_webhooks.sql migration
This commit is contained in:
parent
3866b1f2bc
commit
02c6741d74
|
|
@ -194,8 +194,9 @@ async def create_message(channel_id: int, actual_guild_id: int,
|
|||
|
||||
return message_id
|
||||
|
||||
async def _guild_text_mentions(payload: dict, guild_id: int,
|
||||
mentions_everyone: bool, mentions_here: bool):
|
||||
async def msg_guild_text_mentions(payload: dict, guild_id: int,
|
||||
mentions_everyone: bool, mentions_here: bool):
|
||||
"""Calculates mention data side-effects."""
|
||||
channel_id = int(payload['channel_id'])
|
||||
|
||||
# calculate the user ids we'll bump the mention count for
|
||||
|
|
@ -435,8 +436,8 @@ async def _create_message(channel_id):
|
|||
""", message_id, channel_id, user_id)
|
||||
|
||||
if ctype == ChannelType.GUILD_TEXT:
|
||||
await _guild_text_mentions(payload, guild_id,
|
||||
mentions_everyone, mentions_here)
|
||||
await msg_guild_text_mentions(
|
||||
payload, guild_id, mentions_everyone, mentions_here)
|
||||
|
||||
return jsonify(payload)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ from litecord.errors import WebhookNotFound, Unauthorized, ChannelNotFound
|
|||
|
||||
from litecord.blueprints.channel.messages import (
|
||||
msg_create_request, msg_create_check_content, msg_add_attachment,
|
||||
# create_message
|
||||
msg_guild_text_mentions
|
||||
)
|
||||
from litecord.embed.sanitizer import fill_embed
|
||||
from litecord.embed.messages import process_url_embed
|
||||
from litecord.utils import pg_set_json
|
||||
from litecord.enums import MessageType
|
||||
|
||||
bp = Blueprint('webhooks', __name__)
|
||||
|
||||
|
|
@ -304,9 +306,34 @@ async def del_webhook_tokened(webhook_id, webhook_token):
|
|||
return '', 204
|
||||
|
||||
|
||||
async def create_message_webhook(guild_id, channel_id, webhook_id, j):
|
||||
# TODO: impl
|
||||
pass
|
||||
async def create_message_webhook(guild_id, channel_id, webhook_id, data):
|
||||
"""Create a message, but for webhooks only."""
|
||||
message_id = get_snowflake()
|
||||
|
||||
async with app.db.acquire() as conn:
|
||||
await pg_set_json(conn)
|
||||
|
||||
await conn.execute(
|
||||
"""
|
||||
INSERT INTO messages (id, channel_id, guild_id, webhook_id,
|
||||
content, tts, mention_everyone, message_type, embeds)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
""",
|
||||
message_id,
|
||||
channel_id,
|
||||
guild_id,
|
||||
webhook_id,
|
||||
data['content'],
|
||||
|
||||
data['tts'],
|
||||
data['everyone_mention'],
|
||||
|
||||
MessageType.DEFAULT.value,
|
||||
data.get('embeds', [])
|
||||
)
|
||||
|
||||
return message_id
|
||||
|
||||
|
||||
|
||||
@bp.route('/webhooks/<int:webhook_id>/<webhook_token>', methods=['POST'])
|
||||
|
|
@ -318,6 +345,12 @@ async def execute_webhook(webhook_id: int, webhook_token):
|
|||
# TODO: ensure channel_id points to guild text channel
|
||||
|
||||
payload_json, files = await msg_create_request()
|
||||
|
||||
# NOTE: we really pop here instead of adding a kwarg
|
||||
# to msg_create_request just because of webhooks.
|
||||
# nonce isn't allowed on WEBHOOK_MESSAGE_CREATE
|
||||
payload_json.pop('nonce')
|
||||
|
||||
j = validate(payload_json, WEBHOOK_MESSAGE_CREATE)
|
||||
|
||||
msg_create_check_content(j, files)
|
||||
|
|
@ -326,13 +359,15 @@ async def execute_webhook(webhook_id: int, webhook_token):
|
|||
mentions_everyone = '@everyone' in j['content']
|
||||
mentions_here = '@here' in j['content']
|
||||
|
||||
given_embeds = j.get('embeds', [])
|
||||
|
||||
message_id = await create_message_webhook(
|
||||
guild_id, channel_id, webhook_id, {
|
||||
'content': j.get('content', ''),
|
||||
'tts': j.get('tts', False),
|
||||
|
||||
'everyone_mention': mentions_everyone or mentions_here,
|
||||
'embeds': [await fill_embed(e) for e in j['embeds']]
|
||||
'embeds': await async_map(fill_embed, given_embeds)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -341,6 +376,9 @@ async def execute_webhook(webhook_id: int, webhook_token):
|
|||
|
||||
payload = await app.storage.get_message(message_id)
|
||||
|
||||
await app.dispatcher.dispatch('channel', channel_id,
|
||||
'MESSAGE_CREATE', payload)
|
||||
|
||||
# spawn embedder in the background, even when we're on a webhook.
|
||||
app.sched.spawn(
|
||||
process_url_embed(
|
||||
|
|
@ -349,6 +387,10 @@ async def execute_webhook(webhook_id: int, webhook_token):
|
|||
)
|
||||
)
|
||||
|
||||
# we can assume its a guild text channel, so just call it
|
||||
await msg_guild_text_mentions(
|
||||
payload, guild_id, mentions_everyone, mentions_here)
|
||||
|
||||
# TODO: is it really 204?
|
||||
return '', 204
|
||||
|
||||
|
|
|
|||
|
|
@ -712,11 +712,12 @@ WEBHOOK_MESSAGE_CREATE = {
|
|||
|
||||
# TODO: url type, or something...
|
||||
'avatar_url': {
|
||||
'type': 'url', 'required': False
|
||||
# 'type': 'url', 'required': False
|
||||
'type': 'string', 'required': False
|
||||
},
|
||||
|
||||
'embeds': {
|
||||
'type': list,
|
||||
'type': 'list',
|
||||
'required': False,
|
||||
'schema': {'type': 'dict', 'schema': EMBED_OBJECT}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -796,18 +796,28 @@ class Storage:
|
|||
return res
|
||||
|
||||
async def _inject_author(self, res: dict):
|
||||
"""Inject a pseudo-user object when the message is made by a webhook."""
|
||||
author_id, webhook_id = res['author_id'], res['webhook_id']
|
||||
"""Inject a pseudo-user object when the message is
|
||||
made by a webhook."""
|
||||
author_id = res['author_id']
|
||||
|
||||
if author_id is not None:
|
||||
# if author_id is None, we fetch webhook info
|
||||
# from the message_webhook_info table.
|
||||
if author_id is None:
|
||||
wb_info = await self.db.fetchrow("""
|
||||
SELECT webhook_id, name, avatar
|
||||
FROM message_webhook_info
|
||||
WHERE message_id = $1
|
||||
""", int(res['id']))
|
||||
|
||||
res['author'] = {
|
||||
'id': str(wb_info['id']),
|
||||
'bot': True,
|
||||
'username': wb_info['name'],
|
||||
'avatar': wb_info['avatar']
|
||||
}
|
||||
else:
|
||||
res['author'] = await self.get_user(res['author_id'])
|
||||
res.pop('webhook_id')
|
||||
elif webhook_id is not None:
|
||||
res['author'] = {
|
||||
'id': webhook_id,
|
||||
'username': 'a',
|
||||
'avatar': None
|
||||
}
|
||||
|
||||
res.pop('author_id')
|
||||
|
||||
|
|
@ -815,7 +825,7 @@ class Storage:
|
|||
user_id: Optional[int] = None) -> Optional[Dict]:
|
||||
"""Get a single message's payload."""
|
||||
row = await self.fetchrow_with_json("""
|
||||
SELECT id::text, channel_id::text, author_id, webhook_id, content,
|
||||
SELECT id::text, channel_id::text, author_id, content,
|
||||
created_at AS timestamp, edited_at AS edited_timestamp,
|
||||
tts, mention_everyone, nonce, message_type, embeds
|
||||
FROM messages
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
-- this is a tricky one. blame discord
|
||||
|
||||
-- first, remove all messages made by webhooks
|
||||
DELETE FROM messages WHERE author_id is null;
|
||||
|
||||
-- delete the row, removing the fkey. no connection anymore.
|
||||
ALTER TABLE messages DROP COLUMN webhook_id;
|
||||
|
||||
-- add a message_webhook_info table. more on that in Storage._inject_author
|
||||
CREATE TABLE IF NOT EXISTS message_webhook_info (
|
||||
message_id bigint REFERENCES messages (id) PRIMARY KEY,
|
||||
|
||||
webhook_id bigint,
|
||||
name text DEFAULT '<invalid>',
|
||||
avatar text DEFAULT NULL
|
||||
);
|
||||
|
||||
17
schema.sql
17
schema.sql
|
|
@ -643,13 +643,9 @@ CREATE TABLE IF NOT EXISTS messages (
|
|||
-- this is good for search.
|
||||
guild_id bigint REFERENCES guilds (id) ON DELETE CASCADE,
|
||||
|
||||
-- those are mutually exclusive, only one of them
|
||||
-- can NOT be NULL at a time.
|
||||
|
||||
-- if author is NULL -> message from webhook
|
||||
-- if webhook is NULL -> message from author
|
||||
-- if author is NULL -> message from webhook ->
|
||||
-- fetch from message_webhook_info
|
||||
author_id bigint REFERENCES users (id),
|
||||
webhook_id bigint REFERENCES webhooks (id),
|
||||
|
||||
content text,
|
||||
|
||||
|
|
@ -666,6 +662,15 @@ CREATE TABLE IF NOT EXISTS messages (
|
|||
message_type int NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message_webhook_info (
|
||||
message_id bigint REFERENCES messages (id) PRIMARY KEY,
|
||||
|
||||
webhook_id bigint,
|
||||
name text DEFAULT '<invalid>',
|
||||
avatar text DEFAULT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message_reactions (
|
||||
message_id bigint REFERENCES messages (id),
|
||||
user_id bigint REFERENCES users (id),
|
||||
|
|
|
|||
Loading…
Reference in New Issue