diff options
Diffstat (limited to 'pydis_site/apps/api')
-rw-r--r-- | pydis_site/apps/api/models/__init__.py | 5 | ||||
-rw-r--r-- | pydis_site/apps/api/models/bot/__init__.py | 2 | ||||
-rw-r--r-- | pydis_site/apps/api/models/bot/filter_list.py | 42 | ||||
-rw-r--r-- | pydis_site/apps/api/models/bot/filters.py | 187 | ||||
-rw-r--r-- | pydis_site/apps/api/viewsets/bot/filter_list.py | 2 |
5 files changed, 194 insertions, 44 deletions
diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index fd5bf220..72f59b57 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -1,6 +1,11 @@ # flake8: noqa from .bot import ( FilterList, + FilterSettings, + FilterAction, + ChannelRange, + Filter, + FilterOverride, BotSetting, DocumentationLink, DeletedMessage, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index ac864de3..1bfe0063 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa -from .filter_list import FilterList +from .filters import FilterList, FilterSettings, FilterAction, ChannelRange, Filter, FilterOverride from .bot_setting import BotSetting from .deleted_message import DeletedMessage from .documentation_link import DocumentationLink diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py deleted file mode 100644 index d30f7213..00000000 --- a/pydis_site/apps/api/models/bot/filter_list.py +++ /dev/null @@ -1,42 +0,0 @@ -from django.db import models - -from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin - - -class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): - """An item that is either allowed or denied.""" - - FilterListType = models.TextChoices( - 'FilterListType', - 'GUILD_INVITE ' - 'FILE_FORMAT ' - 'DOMAIN_NAME ' - 'FILTER_TOKEN ' - 'REDIRECT ' - ) - type = models.CharField( - max_length=50, - help_text="The type of allowlist this is on.", - choices=FilterListType.choices, - ) - allowed = models.BooleanField( - help_text="Whether this item is on the allowlist or the denylist." - ) - content = models.TextField( - help_text="The data to add to the allow or denylist." - ) - comment = models.TextField( - help_text="Optional comment on this entry.", - null=True - ) - - class Meta: - """Metaconfig for this model.""" - - # This constraint ensures only one filterlist with the - # same content can exist. This means that we cannot have both an allow - # and a deny for the same item, and we cannot have duplicates of the - # same item. - constraints = [ - models.UniqueConstraint(fields=['content', 'type'], name='unique_filter_list'), - ] diff --git a/pydis_site/apps/api/models/bot/filters.py b/pydis_site/apps/api/models/bot/filters.py new file mode 100644 index 00000000..dfc38e82 --- /dev/null +++ b/pydis_site/apps/api/models/bot/filters.py @@ -0,0 +1,187 @@ +from typing import List + +from django.contrib.postgres.fields import ArrayField +from django.core.exceptions import ValidationError +from django.db import models +from django.db.models import UniqueConstraint + + +class FilterListType(models.IntegerChoices): + """Choice between allow or deny for a list type.""" + + ALLOW: 1 + DENY: 0 + + +class InfractionType(models.TextChoices): + """Possible type of infractions.""" + + NOTE = "Note" + WARN = "Warn" + MUTE = "Mute" + KICK = "Kick" + BAN = "Ban" + + +# Valid special values in ping related fields +VALID_PINGS = ("everyone", "here", "moderators", "onduty", "admins") + + +def validate_ping_field(value_list: List[str]) -> None: + """Validate that the values are either a special value or a UID.""" + for value in value_list: + # Check if it is a special value + if value in VALID_PINGS: + continue + # Check if it is a UID + if value.isnumeric(): + continue + + raise ValidationError(f"{value!r} isn't a valid ping type.") + + +class FilterList(models.Model): + """Represent a list in its allow or deny form.""" + + name = models.CharField(max_length=50, help_text="The unique name of this list.") + list_type = models.IntegerField( + choices=FilterListType.choices, + help_text="Whenever this list is an allowlist or denylist" + ) + + filters = models.ManyToManyField("Filter", help_text="The content of this list.") + default_settings = models.ForeignKey( + "FilterSettings", + models.CASCADE, + help_text="Default parameters of this list." + ) + + class Meta: + """Constrain name and list_type unique.""" + + constraints = ( + UniqueConstraint(fields=("name", "list_type"), name="unique_name_type"), + ) + + def __str__(self) -> str: + return f"Filter {'allow' if self.list_type == 1 else 'deny'}list {self.name!r}" + + +class FilterSettings(models.Model): + """Persistent settings of a filter list.""" + + ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers." + ) + filter_dm = models.BooleanField(help_text="Whenever DMs should be filtered.") + dm_ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers on a DM." + ) + delete_messages = models.BooleanField( + help_text="Whenever this filter should delete messages triggering it." + ) + bypass_roles = ArrayField( + models.BigIntegerField(), + help_text="Roles and users who can bypass this filter." + ) + enabled = models.BooleanField( + help_text="Whenever ths filter is currently enabled." + ) + default_action = models.ForeignKey( + "FilterAction", + models.CASCADE, + help_text="The default action to perform." + ) + default_range = models.ForeignKey( + "ChannelRange", + models.CASCADE, + help_text="Where does this filter apply." + ) + + +class FilterAction(models.Model): + """The action to take when a filter is triggered.""" + + user_dm = models.CharField( + max_length=1000, + null=True, + help_text="The DM to send to a user triggering this filter." + ) + infraction_type = models.CharField( + choices=InfractionType.choices, + max_length=4, + null=True, + help_text="The infraction to apply to this user." + ) + infraction_reason = models.CharField( + max_length=1000, + help_text="The reason to give for the infraction." + ) + infraction_duration = models.DurationField( + null=True, + help_text="The duration of the infraction. Null if permanent." + ) + + +class ChannelRange(models.Model): + """ + Where a filter should apply. + + The resolution is done in the following order: + - disallowed channels + - disallowed categories + - allowed categories + - allowed channels + - default + """ + + disallowed_channels = ArrayField(models.IntegerField()) + disallowed_categories = ArrayField(models.IntegerField()) + allowed_channels = ArrayField(models.IntegerField()) + allowed_category = ArrayField(models.IntegerField()) + default = models.BooleanField() + + +class Filter(models.Model): + """One specific trigger of a list.""" + + content = models.CharField(max_length=100, help_text="The definition of this filter.") + description = models.CharField(max_length=200, help_text="Why this filter has been added.") + additional_field = models.BooleanField(null=True, help_text="Implementation specific field.") + override = models.ForeignKey( + "FilterOverride", + models.SET_NULL, + null=True, + help_text="Override the default settings." + ) + + def __str__(self) -> str: + return f"Filter {self.content!r}" + + +class FilterOverride(models.Model): + """ + Setting overrides of a specific filter. + + Any non-null value will override the default ones. + """ + + ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), null=True + ) + filter_dm = models.BooleanField(null=True) + dm_ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + null=True + ) + delete_messages = models.BooleanField(null=True) + bypass_roles = ArrayField(models.IntegerField(), null=True) + enabled = models.BooleanField(null=True) + default_action = models.ForeignKey("FilterAction", models.CASCADE, null=True) + default_range = models.ForeignKey("ChannelRange", models.CASCADE, null=True) diff --git a/pydis_site/apps/api/viewsets/bot/filter_list.py b/pydis_site/apps/api/viewsets/bot/filter_list.py index 4b05acee..3eacdaaa 100644 --- a/pydis_site/apps/api/viewsets/bot/filter_list.py +++ b/pydis_site/apps/api/viewsets/bot/filter_list.py @@ -3,7 +3,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet -from pydis_site.apps.api.models.bot.filter_list import FilterList +from pydis_site.apps.api.models.bot.filters import FilterList from pydis_site.apps.api.serializers import FilterListSerializer |