From b80b3293a5cc50d4482ead0903d516652f83b27e Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Thu, 17 Dec 2020 04:31:37 +0300 Subject: Adds Webhook Option Adds webhook option on form creation, and adds validation. Updates `SCHEMA.md`. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- SCHEMA.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'SCHEMA.md') diff --git a/SCHEMA.md b/SCHEMA.md index b37ed1d..894ffec 100644 --- a/SCHEMA.md +++ b/SCHEMA.md @@ -12,13 +12,14 @@ In this document: ## Form -| Field | Type | Description | Example | -| ----------- | ---------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------- | -| `id` | Unique identifier | A user selected, unique, descriptive identifier (used in URL routes, so no spaces) | `"ban-appeals"` | -| `features` | List of [form features](#form-features) | A list of features to change the behaviour of the form, described in the features section | `["OPEN", "COLLECT_EMAIL"]` | -| `questions` | List of [form questions](#form-question) | The list of questions to render on a specific form | Too long! See below | -| `name` | String | Name of the form | `"Summer Code Jam 2100"` | -| `description` | String | Form description | `"This is my amazing form description."` | +| Field | Type | Description | Example | +| ------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- | +| `id` | Unique identifier | A user selected, unique, descriptive identifier (used in URL routes, so no spaces) | `"ban-appeals"` | +| `features` | List of [form features](#form-features) | A list of features to change the behaviour of the form, described in the features section | `["OPEN", "COLLECT_EMAIL"]` | +| `meta` | Mapping of [meta options](#meta-options) | Meta properties for the form. | See meta-options section | +| `questions` | List of [form questions](#form-question) | The list of questions to render on a specific form | Too long! See below | +| `name` | String | Name of the form | `"Summer Code Jam 2100"` | +| `description` | String | Form description | `"This is my amazing form description."` | ### Form features @@ -29,6 +30,13 @@ In this document: | `OPEN` | The form is currently accepting responses. | | `COLLECT_EMAIL` | The form should collect the email from submissions. Requires `REQUIRES_LOGIN` | | `DISABLE_ANTISPAM` | Disable the anti-spam checks from running on a form submission. | +| `WEBHOOK_ENABLED` | The form should notify the webhook. Has no effect if no webhook is set. | + +### Meta options +| Field | Description | Example | +| --------- | ----------------------------------- | ------------------------------------------------------------------------------------------ | +| `webhook` | Mapping of webhook url and message. | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "Hello World!"}` | + ### Form question -- cgit v1.2.3 From bbbfa152ba14f37d0cd5686ed4cdcfc7cd83514e Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Thu, 17 Dec 2020 12:48:04 +0300 Subject: Adds Webhook Sending Functionality Builds and sends a discord webhook on form submission. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- SCHEMA.md | 6 ++--- backend/routes/forms/submit.py | 60 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 7 deletions(-) (limited to 'SCHEMA.md') diff --git a/SCHEMA.md b/SCHEMA.md index 4daa5dd..b55635f 100644 --- a/SCHEMA.md +++ b/SCHEMA.md @@ -33,9 +33,9 @@ In this document: | `WEBHOOK_ENABLED` | The form should notify the webhook. Has no effect if no webhook is set. | ### Meta options -| Field | Description | Example | -| --------- | ----------------------------------- | ------------------------------------------------------------------------------------------ | -| `webhook` | Mapping of webhook url and message. | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "Hello World!"}` | +| Field | Description | Example | +| --------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `webhook` | Mapping of webhook url and message. Message can use `_USER_MENTION_` to mention the submitting user. | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "Hello World! _USER_MENTION_"}` | ### Form question diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py index 48ae4f6..7cd7576 100644 --- a/backend/routes/forms/submit.py +++ b/backend/routes/forms/submit.py @@ -9,11 +9,11 @@ import uuid import httpx import pydnsbl from pydantic import ValidationError +from starlette.background import BackgroundTask from starlette.requests import Request - from starlette.responses import JSONResponse -from backend.constants import HCAPTCHA_API_SECRET, FormFeatures +from backend.constants import FRONTEND_URL, FormFeatures, HCAPTCHA_API_SECRET from backend.models import Form, FormResponse from backend.route import Route @@ -108,11 +108,63 @@ class SubmitForm(Route): response_obj.dict(by_alias=True) ) + send_webhook = None + if FormFeatures.WEBHOOK_ENABLED.value in form.features: + send_webhook = BackgroundTask( + self.send_submission_webhook, + form=form, + response=response_obj + ) + return JSONResponse({ - "form": form.dict(), + "form": form.dict(admin=False), "response": response_obj.dict() - }) + }, background=send_webhook) + else: return JSONResponse({ "error": "Open form not found" }) + + @staticmethod + def send_submission_webhook(form: Form, response: FormResponse) -> None: + """Helper to send a submission message to a discord webhook.""" + # Stop if webhook is not available + if form.meta.webhook is None: + raise ValueError("Got empty webhook.") + + user = response.user + username = f"{user.username}#{user.discriminator}" if user else None + user_mention = f"<@{user.id}>" if user else f"{username or 'User'}" + + # Build Embed + embed = { + "title": "New Form Response", + "description": f"{user_mention} submitted a response.", + "url": f"{FRONTEND_URL}/path_to_view_form/{response.id}", # noqa # TODO: Enter Form View URL + "timestamp": response.timestamp, + "color": 7506394, + } + + # Add author to embed + if user is not None: + embed["author"] = {"name": username} + + if user.avatar is not None: + url = f"https://cdn.discordapp.com/avatars/{user.id}/{user.avatar}.png" + embed["author"]["icon_url"] = url + + # Build Hook + hook = { + "embeds": [embed], + "allowed_mentions": {"parse": ["users", "roles"]}, + "username": form.name or "Python Discord Forms" + } + + # Set hook message + message = form.meta.webhook.message + if message: + hook["content"] = message.replace("_USER_MENTION_", f"<@{user.id}>") + + # Post hook + httpx.post(form.meta.webhook.url, json=hook).raise_for_status() -- cgit v1.2.3 From 9d8ca77b1f18891cef88e9b8235b41172067f9b0 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Mon, 21 Dec 2020 04:41:27 +0300 Subject: Adds and Documents Webhook Message Variables Adds better parsing and formatting for webhook message variables, and documents them in SCHEMA.md. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- SCHEMA.md | 18 +++++++++++++++--- backend/routes/forms/submit.py | 12 ++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'SCHEMA.md') diff --git a/SCHEMA.md b/SCHEMA.md index b55635f..fa5f247 100644 --- a/SCHEMA.md +++ b/SCHEMA.md @@ -33,9 +33,21 @@ In this document: | `WEBHOOK_ENABLED` | The form should notify the webhook. Has no effect if no webhook is set. | ### Meta options -| Field | Description | Example | -| --------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `webhook` | Mapping of webhook url and message. Message can use `_USER_MENTION_` to mention the submitting user. | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "Hello World! _USER_MENTION_"}` | +| Field | Description | Example | +| --------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `webhook` | Mapping of webhook url and message. Message can use certain [context variables](#webhook-variables). | `"webhook": {"url": "https://discord.com/api/webhooks/id/key", "message": "{user} submitted a form."}` | + + +#### Webhook Variables +The following variables can be used in a webhook's message. The variables must be wrapped by braces (`{}`). + +| Name | Description | +| ------------- | ---------------------------------------------------------------------------- | +| `user` | A discord mention of the user submitting the form, or "User" if unavailable. | +| `response_id` | ID of the submitted response. | +| `form` | Name of the submitted form. | +| `form_id` | ID of the submitted form. | +| `time` | ISO submission timestamp. | ### Form question diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py index 3b22155..5c0cfdd 100644 --- a/backend/routes/forms/submit.py +++ b/backend/routes/forms/submit.py @@ -197,6 +197,18 @@ class SubmitForm(Route): # Set hook message message = form.meta.webhook.message if message: + # Available variables, see SCHEMA.md + ctx = { + "user": mention, + "response_id": response.id, + "form": form.name, + "form_id": form.id, + "time": response.timestamp, + } + + for key in ctx: + message = message.replace(f"{{{key}}}", str(ctx[key])) + hook["content"] = message.replace("_USER_MENTION_", mention) # Post hook -- cgit v1.2.3