aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/api/validators.py
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site/apps/api/validators.py')
-rw-r--r--pydis_site/apps/api/validators.py164
1 files changed, 164 insertions, 0 deletions
diff --git a/pydis_site/apps/api/validators.py b/pydis_site/apps/api/validators.py
new file mode 100644
index 00000000..ac2fb739
--- /dev/null
+++ b/pydis_site/apps/api/validators.py
@@ -0,0 +1,164 @@
+from collections.abc import Mapping
+
+from django.core.exceptions import ValidationError
+from django.core.validators import MaxLengthValidator, MinLengthValidator
+
+
+def validate_tag_embed_fields(fields):
+ field_validators = {
+ 'name': (MaxLengthValidator(limit_value=256),),
+ 'value': (MaxLengthValidator(limit_value=1024),)
+ }
+
+ for field in fields:
+ if not isinstance(field, Mapping):
+ raise ValidationError("Embed fields must be a mapping.")
+
+ for field_name, value in field.items():
+ if field_name not in field_validators:
+ raise ValidationError(f"Unknown embed field field: {field_name!r}.")
+
+ for validator in field_validators[field_name]:
+ validator(value)
+
+
+def validate_tag_embed_footer(footer):
+ field_validators = {
+ 'text': (
+ MinLengthValidator(
+ limit_value=1,
+ message="Footer text must not be empty."
+ ),
+ MaxLengthValidator(limit_value=2048)
+ ),
+ 'icon_url': (),
+ 'proxy_icon_url': ()
+ }
+
+ if not isinstance(footer, Mapping):
+ raise ValidationError("Embed footer must be a mapping.")
+
+ for field_name, value in footer.items():
+ if field_name not in field_validators:
+ raise ValidationError(f"Unknown embed footer field: {field_name!r}.")
+
+ for validator in field_validators[field_name]:
+ validator(value)
+
+
+def validate_tag_embed_author(author):
+ field_validators = {
+ 'name': (
+ MinLengthValidator(
+ limit_value=1,
+ message="Embed author name must not be empty."
+ ),
+ MaxLengthValidator(limit_value=256)
+ ),
+ 'url': (),
+ 'icon_url': (),
+ 'proxy_icon_url': ()
+ }
+
+ if not isinstance(author, Mapping):
+ raise ValidationError("Embed author must be a mapping.")
+
+ for field_name, value in author.items():
+ if field_name not in field_validators:
+ raise ValidationError(f"Unknown embed author field: {field_name!r}.")
+
+ for validator in field_validators[field_name]:
+ validator(value)
+
+
+def validate_tag_embed(embed):
+ """
+ Validate a JSON document containing an embed as possible to send
+ on Discord. This attempts to rebuild the validation used by Discord
+ as well as possible by checking for various embed limits so we can
+ ensure that any embed we store here will also be accepted as a
+ valid embed by the Discord API.
+
+ Using this directly is possible, although not intended - you usually
+ stick this onto the `validators` keyword argument of model fields.
+
+ Example:
+
+ >>> from django.contrib.postgres import fields as pgfields
+ >>> from django.db import models
+ >>> from pydis_site.apps.api import validate_tag_embed
+ >>> class MyMessage(models.Model):
+ ... embed = pgfields.JSONField(
+ ... validators=(
+ ... validate_tag_embed,
+ ... )
+ ... )
+ ... # ...
+ ...
+
+ Args:
+ embed (Dict[str, Union[str, List[dict], dict]]):
+ A dictionary describing the contents of this embed.
+ See the official documentation for a full reference
+ of accepted keys by this dictionary:
+ https://discordapp.com/developers/docs/resources/channel#embed-object
+
+ Raises:
+ ValidationError:
+ In case the given embed is deemed invalid, a `ValidationError`
+ is raised which in turn will allow Django to display errors
+ as appropriate.
+ """
+
+ all_keys = {
+ 'title', 'type', 'description', 'url', 'timestamp',
+ 'color', 'footer', 'image', 'thumbnail', 'video',
+ 'provider', 'author', 'fields'
+ }
+ one_required_of = {'description', 'fields', 'image', 'title', 'video'}
+ field_validators = {
+ 'title': (
+ MinLengthValidator(
+ limit_value=1,
+ message="Embed title must not be empty."
+ ),
+ MaxLengthValidator(limit_value=256)
+ ),
+ 'description': (MaxLengthValidator(limit_value=2048),),
+ 'fields': (
+ MaxLengthValidator(limit_value=25),
+ validate_tag_embed_fields
+ ),
+ 'footer': (validate_tag_embed_footer,),
+ 'author': (validate_tag_embed_author,)
+ }
+
+ if not embed:
+ raise ValidationError("Tag embed must not be empty.")
+
+ elif not isinstance(embed, Mapping):
+ raise ValidationError("Tag embed must be a mapping.")
+
+ elif not any(field in embed for field in one_required_of):
+ raise ValidationError(f"Tag embed must contain one of the fields {one_required_of}.")
+
+ for required_key in one_required_of:
+ if required_key in embed and not embed[required_key]:
+ raise ValidationError(f"Key {required_key!r} must not be empty.")
+
+ for field_name, value in embed.items():
+ if field_name not in all_keys:
+ raise ValidationError(f"Unknown field name: {field_name!r}")
+
+ if field_name in field_validators:
+ for validator in field_validators[field_name]:
+ validator(value)
+
+
+def validate_bot_setting_name(name):
+ KNOWN_SETTINGS = (
+ 'defcon',
+ )
+
+ if name not in KNOWN_SETTINGS:
+ raise ValidationError(f"`{name}` is not a known setting name.")