diff options
Diffstat (limited to 'backend/routes')
| -rw-r--r-- | backend/routes/forms/index.py | 18 | ||||
| -rw-r--r-- | backend/routes/forms/submit.py | 86 | 
2 files changed, 97 insertions, 7 deletions
diff --git a/backend/routes/forms/index.py b/backend/routes/forms/index.py index d1373e4..0e1dee8 100644 --- a/backend/routes/forms/index.py +++ b/backend/routes/forms/index.py @@ -6,8 +6,10 @@ from starlette.authentication import requires  from starlette.requests import Request  from starlette.responses import JSONResponse -from backend.route import Route +from backend.constants import Meta, WebHook  from backend.models import Form, FormList +from backend.models.form import validate_hook_url +from backend.route import Route  from backend.validation import ErrorMessage, OkayResponse, api @@ -46,6 +48,20 @@ class FormsList(Route):          """Create a new form."""          form_data = await request.json() +        # Verify Webhook +        try: +            # Get url from request +            path = (Meta.__name__.lower(), WebHook.__name__.lower(), WebHook.URL.value) +            url = form_data[path[0]][path[1]][path[2]] + +            # Validate URL +            validation = await validate_hook_url(url) +            if validation: +                return JSONResponse(validation.errors(), status_code=422) + +        except KeyError: +            pass +          form = Form(**form_data)          if await request.state.db.forms.find_one({"_id": form.id}): diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py index 5bcdeff..82caa81 100644 --- a/backend/routes/forms/submit.py +++ b/backend/routes/forms/submit.py @@ -4,18 +4,18 @@ Submit a form.  import binascii  import hashlib -from typing import Any, Optional  import uuid +from typing import Any, Optional  import httpx -from pydantic.main import BaseModel  from pydantic import ValidationError +from pydantic.main import BaseModel  from spectree import Response +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  from backend.validation import AuthorizationHeaders, ErrorMessage, api @@ -128,11 +128,85 @@ 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, +                    request_user=request.user +                ) +              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"              }, status_code=404) + +    @staticmethod +    async def send_submission_webhook( +            form: Form, +            response: FormResponse, +            request_user: Request.user +    ) -> 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.") + +        try: +            mention = request_user.discord_mention +        except AttributeError: +            mention = "User" + +        user = response.user + +        # Build Embed +        embed = { +            "title": "New Form Response", +            "description": f"{mention} submitted a response to `{form.name}`.", +            "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 request_user.is_authenticated: +            embed["author"] = {"name": request_user.display_name} + +            if user and user.avatar: +                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: +            # 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 +        async with httpx.AsyncClient() as client: +            r = await client.post(form.meta.webhook.url, json=hook) +            r.raise_for_status()  |