mirror of https://gitlab.com/litecord/litecord.git
webhooks: add drafts for execution impl
- channel.messages: export msg_create_request,
msg_create_check_payload, msg_add_attachment
- schemas: add WEBHOOK_MESSAGE_CREATE
This commit is contained in:
parent
8360638356
commit
712f1d9ff2
|
|
@ -249,7 +249,7 @@ async def _guild_text_mentions(payload: dict, guild_id: int,
|
|||
""", user_id, channel_id)
|
||||
|
||||
|
||||
async def _msg_input() -> tuple:
|
||||
async def msg_create_request() -> tuple:
|
||||
"""Extract the json input and any file information
|
||||
the client gave to us in the request.
|
||||
|
||||
|
|
@ -277,19 +277,21 @@ async def _msg_input() -> tuple:
|
|||
return json_from_form, [v for k, v in files.items()]
|
||||
|
||||
|
||||
def _check_content(payload: dict, files: list):
|
||||
def msg_create_check_content(payload: dict, files: list, *, use_embeds=False):
|
||||
"""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
|
||||
|
||||
embed_field = 'embeds' if use_embeds else 'embed'
|
||||
has_embed = embed_field in payload
|
||||
|
||||
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, channel_id: int,
|
||||
async def msg_add_attachment(message_id: int, channel_id: int,
|
||||
attachment_file) -> int:
|
||||
"""Add an attachment to a message.
|
||||
|
||||
|
|
@ -297,6 +299,12 @@ async def _add_attachment(message_id: int, channel_id: int,
|
|||
----------
|
||||
message_id: int
|
||||
The ID of the message getting the attachment.
|
||||
channel_id: int
|
||||
The ID of the channel the message belongs to.
|
||||
|
||||
Exists because the attachment URL scheme contains
|
||||
a channel id. The purpose is unknown, but we are
|
||||
implementing Discord's behavior.
|
||||
attachment_file: quart.FileStorage
|
||||
quart FileStorage instance of the file.
|
||||
"""
|
||||
|
|
@ -362,10 +370,10 @@ async def _create_message(channel_id):
|
|||
await channel_perm_check(user_id, channel_id, 'send_messages')
|
||||
actual_guild_id = guild_id
|
||||
|
||||
payload_json, files = await _msg_input()
|
||||
payload_json, files = await msg_create_request()
|
||||
j = validate(payload_json, MESSAGE_CREATE)
|
||||
|
||||
_check_content(payload_json, files)
|
||||
msg_create_check_content(payload_json, files)
|
||||
|
||||
# TODO: check connection to the gateway
|
||||
|
||||
|
|
@ -399,7 +407,7 @@ async def _create_message(channel_id):
|
|||
|
||||
# for each file given, we add it as an attachment
|
||||
for pre_attachment in files:
|
||||
await _add_attachment(message_id, channel_id, pre_attachment)
|
||||
await msg_add_attachment(message_id, channel_id, pre_attachment)
|
||||
|
||||
payload = await app.storage.get_message(message_id, user_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,12 +27,21 @@ from litecord.blueprints.checks import (
|
|||
channel_check, channel_perm_check, guild_check, guild_perm_check
|
||||
)
|
||||
|
||||
from litecord.schemas import validate, WEBHOOK_CREATE, WEBHOOK_UPDATE
|
||||
from litecord.schemas import (
|
||||
validate, WEBHOOK_CREATE, WEBHOOK_UPDATE, WEBHOOK_MESSAGE_CREATE
|
||||
)
|
||||
from litecord.enums import ChannelType
|
||||
from litecord.snowflake import get_snowflake
|
||||
from litecord.utils import async_map
|
||||
from litecord.errors import WebhookNotFound, Unauthorized
|
||||
|
||||
from litecord.blueprints.channel.messages import (
|
||||
msg_create_request, msg_create_check_content, msg_add_attachment,
|
||||
# create_message
|
||||
)
|
||||
from litecord.embed.sanitizer import fill_embed
|
||||
from litecord.embed.messages import process_url_embed
|
||||
|
||||
bp = Blueprint('webhooks', __name__)
|
||||
|
||||
|
||||
|
|
@ -108,15 +117,17 @@ async def _webhook_many(where_clause, arg: int):
|
|||
|
||||
async def webhook_token_check(webhook_id: int, webhook_token: str):
|
||||
"""token_check() equivalent for webhooks."""
|
||||
webhook_id_fetch = await app.db.fetchrow("""
|
||||
SELECT id
|
||||
row = await app.db.fetchrow("""
|
||||
SELECT guild_id, channel_id
|
||||
FROM webhooks
|
||||
WHERE id = $1 AND token = $2
|
||||
""", webhook_id, webhook_token)
|
||||
|
||||
if webhook_id_fetch is None:
|
||||
if row is None:
|
||||
raise Unauthorized('webhook not found or unauthorized')
|
||||
|
||||
return row['guild_id'], row['channel_id']
|
||||
|
||||
|
||||
@bp.route('/channels/<int:channel_id>/webhooks', methods=['POST'])
|
||||
async def create_webhook(channel_id: int):
|
||||
|
|
@ -259,17 +270,64 @@ async def del_webhook_tokened(webhook_id, webhook_token):
|
|||
return '', 204
|
||||
|
||||
|
||||
@bp.route('/webhooks/<int:webhook_id>/<webhook_token>', methods=['POST'])
|
||||
async def execute_webhook(webhook_id, webhook_token):
|
||||
async def create_message_webhook(guild_id, channel_id, webhook_id, j):
|
||||
# TODO: impl
|
||||
pass
|
||||
|
||||
|
||||
@bp.route('/webhooks/<int:webhook_id>/<webhook_token>', methods=['POST'])
|
||||
async def execute_webhook(webhook_id: int, webhook_token):
|
||||
"""Execute a webhook. Sends a message to the channel the webhook
|
||||
is tied to."""
|
||||
guild_id, channel_id = await webhook_token_check(webhook_id, webhook_token)
|
||||
|
||||
# TODO: ensure channel_id points to guild text channel
|
||||
|
||||
payload_json, files = await msg_create_request()
|
||||
j = validate(payload_json, WEBHOOK_MESSAGE_CREATE)
|
||||
|
||||
msg_create_check_content(j, files)
|
||||
|
||||
# webhooks don't need permissions.
|
||||
mentions_everyone = '@everyone' in j['content']
|
||||
mentions_here = '@here' in j['content']
|
||||
|
||||
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']]
|
||||
}
|
||||
)
|
||||
|
||||
for pre_attachment in files:
|
||||
await msg_add_attachment(message_id, channel_id, pre_attachment)
|
||||
|
||||
payload = await app.storage.get_message(message_id)
|
||||
|
||||
# spawn embedder in the background, even when we're on a webhook.
|
||||
app.sched.spawn(
|
||||
process_url_embed(
|
||||
app.config, app.storage, app.dispatcher, app.session,
|
||||
payload
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: is it really 204?
|
||||
return '', 204
|
||||
|
||||
@bp.route('/webhooks/<int:webhook_id>/<webhook_token>/slack',
|
||||
methods=['POST'])
|
||||
async def execute_slack_webhook(webhook_id, webhook_token):
|
||||
pass
|
||||
"""Execute a webhook but expecting Slack data."""
|
||||
# TODO: know slack webhooks
|
||||
await webhook_token_check(webhook_id, webhook_token)
|
||||
|
||||
|
||||
@bp.route('/webhooks/<int:webhook_id>/<webhook_token>/github', methods=['POST'])
|
||||
async def execute_github_webhook(webhook_id, webhook_token):
|
||||
pass
|
||||
"""Execute a webhook but expecting GitHub data."""
|
||||
# TODO: know github webhooks
|
||||
await webhook_token_check(webhook_id, webhook_token)
|
||||
|
|
|
|||
|
|
@ -697,3 +697,27 @@ WEBHOOK_UPDATE = {
|
|||
'avatar': {'type': 'b64_icon', 'required': False, 'nullable': False},
|
||||
'channel_id': {'coerce': int, 'required': False, 'nullable': False}
|
||||
}
|
||||
|
||||
WEBHOOK_MESSAGE_CREATE = {
|
||||
'content': {
|
||||
'type': 'string',
|
||||
'minlength': 0, 'maxlength': 2000, 'required': False
|
||||
},
|
||||
'tts': {'type': 'boolean', 'required': False},
|
||||
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'minlength': 2, 'maxlength': 32, 'required': False
|
||||
},
|
||||
|
||||
# TODO: url type, or something...
|
||||
'avatar_url': {
|
||||
'type': 'url', 'required': False
|
||||
},
|
||||
|
||||
'embeds': {
|
||||
'type': list,
|
||||
'required': False,
|
||||
'schema': {'type': 'dict', 'schema': EMBED_OBJECT}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue