aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pydis_site/apps/api/models/__init__.py5
-rw-r--r--pydis_site/apps/api/models/bot/__init__.py2
-rw-r--r--pydis_site/apps/api/models/bot/filter_list.py42
-rw-r--r--pydis_site/apps/api/models/bot/filters.py187
-rw-r--r--pydis_site/apps/api/viewsets/bot/filter_list.py2
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