From 86074d2e13f3a4b947609335b09326e374b661b1 Mon Sep 17 00:00:00 2001 From: Corban-Lee Date: Tue, 24 Sep 2024 17:28:37 +0100 Subject: [PATCH] replace old models with new --- apps/api/permissions.py | 2 +- apps/api/serializers.py | 188 +---- apps/api/urls.py | 142 +--- apps/api/views.py | 739 ++---------------- .../authentication/migrations/0001_initial.py | 17 +- .../0002_discorduser_token_expires.py | 20 - .../0003_discorduser_refresh_token.py | 19 - .../migrations/0004_servermember.py | 26 - .../0005_alter_servermember_nick.py | 18 - ...servermember_nick_servermember_is_owner.py | 22 - apps/authentication/models.py | 2 +- apps/home/admin.py | 100 +-- apps/home/migrations/0001_initial.py | 166 ++-- apps/home/migrations/0002_articlemutator.py | 21 - apps/home/migrations/0002_predefined_data.py | 47 ++ .../migrations/0003_initial_mutator_data.py | 37 - .../0004_remove_subscription_uwuify.py | 17 - .../migrations/0005_subscription_mutators.py | 18 - ...content_guid_alter_trackedcontent_title.py | 23 - ...7_remove_subscription_mutators_and_more.py | 27 - ...bscription_article_fetch_limit_and_more.py | 24 - .../0009_subscription_embed_colour.py | 18 - ...lter_subscription_embed_colour_and_more.py | 23 - ...tent_unique_guid_guild_id_pair_and_more.py | 34 - ...tent_unique_guid_guild_id_pair_and_more.py | 34 - .../0013_subscription_published_theshold.py | 19 - ...eshold_subscription_published_threshold.py | 18 - ...options_remove_filter_keywords_and_more.py | 67 -- ...lter_filter_matching_algorithm_and_more.py | 24 - .../0017_trackedcontent_message_id.py | 19 - .../0018_subchannel_channel_name.py | 19 - .../0019_savedguilds_default_embed_colour.py | 18 - apps/home/migrations/0020_guildsettings.py | 21 - ...21_alter_guildsettings_options_and_more.py | 122 --- .../0022_populate_guild_settings.py | 22 - ...trule_subscription_unique_content_rules.py | 26 - .../0024_initial_uniquecontentrule_data.py | 22 - apps/home/migrations/0025_set_uniquerules.py | 25 - .../0026_temp_testonly_migration.py | 104 --- .../0027_alter_r_server_icon_hash.py | 18 - apps/home/models.py | 543 +++---------- apps/home/views.py | 4 +- apps/static/js/api.js | 101 +-- apps/static/js/home/index.js | 4 +- apps/static/js/home/servers.js | 1 - 45 files changed, 444 insertions(+), 2537 deletions(-) delete mode 100644 apps/authentication/migrations/0002_discorduser_token_expires.py delete mode 100644 apps/authentication/migrations/0003_discorduser_refresh_token.py delete mode 100644 apps/authentication/migrations/0004_servermember.py delete mode 100644 apps/authentication/migrations/0005_alter_servermember_nick.py delete mode 100644 apps/authentication/migrations/0006_remove_servermember_nick_servermember_is_owner.py delete mode 100644 apps/home/migrations/0002_articlemutator.py create mode 100644 apps/home/migrations/0002_predefined_data.py delete mode 100644 apps/home/migrations/0003_initial_mutator_data.py delete mode 100644 apps/home/migrations/0004_remove_subscription_uwuify.py delete mode 100644 apps/home/migrations/0005_subscription_mutators.py delete mode 100644 apps/home/migrations/0006_alter_trackedcontent_guid_alter_trackedcontent_title.py delete mode 100644 apps/home/migrations/0007_remove_subscription_mutators_and_more.py delete mode 100644 apps/home/migrations/0008_subscription_article_fetch_limit_and_more.py delete mode 100644 apps/home/migrations/0009_subscription_embed_colour.py delete mode 100644 apps/home/migrations/0010_alter_subscription_embed_colour_and_more.py delete mode 100644 apps/home/migrations/0011_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py delete mode 100644 apps/home/migrations/0012_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py delete mode 100644 apps/home/migrations/0013_subscription_published_theshold.py delete mode 100644 apps/home/migrations/0014_rename_published_theshold_subscription_published_threshold.py delete mode 100644 apps/home/migrations/0015_alter_filter_options_remove_filter_keywords_and_more.py delete mode 100644 apps/home/migrations/0016_alter_filter_matching_algorithm_and_more.py delete mode 100644 apps/home/migrations/0017_trackedcontent_message_id.py delete mode 100644 apps/home/migrations/0018_subchannel_channel_name.py delete mode 100644 apps/home/migrations/0019_savedguilds_default_embed_colour.py delete mode 100644 apps/home/migrations/0020_guildsettings.py delete mode 100644 apps/home/migrations/0021_alter_guildsettings_options_and_more.py delete mode 100644 apps/home/migrations/0022_populate_guild_settings.py delete mode 100644 apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py delete mode 100644 apps/home/migrations/0024_initial_uniquecontentrule_data.py delete mode 100644 apps/home/migrations/0025_set_uniquerules.py delete mode 100644 apps/home/migrations/0026_temp_testonly_migration.py delete mode 100644 apps/home/migrations/0027_alter_r_server_icon_hash.py diff --git a/apps/api/permissions.py b/apps/api/permissions.py index d86eb08..60e7c33 100644 --- a/apps/api/permissions.py +++ b/apps/api/permissions.py @@ -2,7 +2,7 @@ from rest_framework.permissions import BasePermission -from apps.home.models import r_Server +from apps.home.models import Server from apps.authentication.models import ServerMember diff --git a/apps/api/serializers.py b/apps/api/serializers.py index 2e766dc..8cd0db1 100644 --- a/apps/api/serializers.py +++ b/apps/api/serializers.py @@ -5,23 +5,13 @@ import logging from rest_framework import serializers from apps.home.models import ( - SubChannel, - Filter, + Server, + ContentFilter, + MessageMutator, + MessageStyle, Subscription, - SavedGuilds, - TrackedContent, - ArticleMutator, - GuildSettings, - UniqueContentRule, - - #rewrite - r_Server, - r_ContentFilter, - r_MessageMutator, - r_MessageStyle, - r_Subscription, - r_Content, - r_UniqueContentRule + Content, + UniqueContentRule ) log = logging.getLogger(__name__) @@ -127,149 +117,15 @@ class DynamicModelSerializer(serializers.ModelSerializer): abstract = True -#region Sub Channel - -class SubChannelSerializer(DynamicModelSerializer): - """ - Serializer for SubChannel Model. - """ - +class ServerSerializer(DynamicModelSerializer): class Meta: - model = SubChannel - fields = ("id", "channel_id", "channel_name", "subscription") - - -#region Filter - -class FilterSerializer(DynamicModelSerializer): - """ - Serializer for the Filter Model. - """ - - class Meta: - model = Filter - fields = ("id", "name", "matching_algorithm", "match", "is_insensitive", "is_whitelist", "guild_id") - - -#region Article Mutator - -class ArticleMutatorSerializer(DynamicModelSerializer): - - class Meta: - model = ArticleMutator - fields = ("id", "name", "value") - - -#region Unique Content Rule - -class UniqueContentRuleSerializer(DynamicModelSerializer): - """ - Serializer for the UniqueContentRule model. - """ - - class Meta: - model = UniqueContentRule - fields = ("id", "name", "value") - - -#region Subscription (GET) - -class SubscriptionSerializer_GET(DynamicModelSerializer): - """ - Serializer for the Subscription Model. - """ - - article_title_mutators = ArticleMutatorSerializer(many=True) - article_desc_mutators = ArticleMutatorSerializer(many=True) - unique_content_rules = UniqueContentRuleSerializer(many=True) - active = serializers.BooleanField(initial=True) - - class Meta: - model = Subscription - fields = ( - "id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters", - "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", - "unique_content_rules", "active" - ) - - -#region Subscription (POST) - -class SubscriptionSerializer_POST(DynamicModelSerializer): - """ - Serializer for the Subscription Model. - """ - - class Meta: - model = Subscription - fields = ( - "id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters", - "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", - "unique_content_rules", "active" - ) - - -#region Saved Guild - -class SavedGuildSerializer(DynamicModelSerializer): - """ - Serializer for the SavedGuild model. - """ - - class Meta: - model = SavedGuilds - fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "owner") - - -#region Guild Setting - -class GuildSettingsSerializer(DynamicModelSerializer): - """ - Serializer for the GuildSettings model. - """ - - class Meta: - model = GuildSettings - fields = ("id", "guild_id", "default_embed_colour", "active") - - -#region Tracked Content (GET) - -class TrackedContentSerializer_GET(DynamicModelSerializer): - """ - Serializer for the TrackedContent model. - """ - - subscription = SubscriptionSerializer_GET() - - class Meta: - model = TrackedContent - fields = ("id", "guid", "title", "url", "subscription", "channel_id", "message_id", "blocked", "creation_datetime") - - -#region Tracked Content (POST) - -class TrackedContentSerializer_POST(DynamicModelSerializer): - """ - Serializer for the TrackedContent model. - """ - - class Meta: - model = TrackedContent - fields = ("id", "guid", "title", "url", "subscription", "channel_id", "message_id", "blocked", "creation_datetime") - - -#region rewrite - -class r_ServerSerializer(DynamicModelSerializer): - class Meta: - model = r_Server + model = Server fields = ("id", "name", "icon_hash", "active") -class r_ContentFilterSerializer(DynamicModelSerializer): +class ContentFilterSerializer(DynamicModelSerializer): class Meta: - model = r_ContentFilter + model = ContentFilter fields = ( "id", "server", @@ -281,15 +137,15 @@ class r_ContentFilterSerializer(DynamicModelSerializer): ) -class r_MessageMutatorSerializer(DynamicModelSerializer): +class MessageMutatorSerializer(DynamicModelSerializer): class Meta: - model = r_MessageMutator + model = MessageMutator fields = ("id", "name", "value") -class r_MessageStyleSerializer(DynamicModelSerializer): +class MessageStyleSerializer(DynamicModelSerializer): class Meta: - model = r_MessageStyle + model = MessageStyle fields = ( "id", "server", @@ -304,14 +160,14 @@ class r_MessageStyleSerializer(DynamicModelSerializer): ) -class r_SubscriptionSerializer(DynamicModelSerializer): +class SubscriptionSerializer(DynamicModelSerializer): filters = serializers.PrimaryKeyRelatedField( - queryset=r_ContentFilter.objects.all(), + queryset=ContentFilter.objects.all(), many=True ) class Meta: - model = r_Subscription + model = Subscription fields = ( "id", "server", @@ -332,7 +188,7 @@ class r_SubscriptionSerializer(DynamicModelSerializer): # Prevent using filters from a different server selected_filters = data.get("filters", []) - valid_filter_ids = r_ContentFilter.objects.filter(server=server).values_list("id", flat=True) + valid_filter_ids = ContentFilter.objects.filter(server=server).values_list("id", flat=True) if any(fltr.id not in valid_filter_ids for fltr in selected_filters): raise serializers.ValidationError( {"filters": "All filters must belong to the specified server."} @@ -348,9 +204,9 @@ class r_SubscriptionSerializer(DynamicModelSerializer): return data -class r_ContentSerializer(DynamicModelSerializer): +class ContentSerializer(DynamicModelSerializer): class Meta: - model = r_Content + model = Content fields = ( "id", "subscription", @@ -362,7 +218,7 @@ class r_ContentSerializer(DynamicModelSerializer): ) -class r_UniqueContentRuleSerializer(DynamicModelSerializer): +class UniqueContentRuleSerializer(DynamicModelSerializer): class Meta: - model = r_UniqueContentRule + model = UniqueContentRule fields = ("id", "name", "value") diff --git a/apps/api/urls.py b/apps/api/urls.py index 9e289b0..bbce426 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -4,123 +4,61 @@ from django.urls import path, include from rest_framework.authtoken.views import obtain_auth_token from .views import ( - SubChannel_ListView, - SubChannel_DetailView, - Filter_ListView, - Filter_DetailView, + Server_ListView, + Server_DetailView, + ContentFilter_ListView, + ContentFilter_DetailView, + MessageMutator_ListView, + MessageMutator_DetailView, + MessageStyle_ListView, + MessageStyle_DetailView, Subscription_ListView, Subscription_DetailView, - Subscription_SubChannelView, - SavedGuild_ListView, - SavedGuild_DetailView, - TrackedContent_ListView, - TrackedContent_DetailView, - ArticleMutator_ListView, - ArticleMutator_DetailView, - GuildSettings_ListView, - GuildSettings_DetailView, + Content_ListView, + Content_DetailView, UniqueContentRule_ListView, - UniqueContentRule_DetailView, - - #rewrite - r_Server_ListView, - r_Server_DetailView, - r_ContentFilter_ListView, - r_ContentFilter_DetailView, - r_MessageMutator_ListView, - r_MessageMutator_DetailView, - r_MessageStyle_ListView, - r_MessageStyle_DetailView, - r_Subscription_ListView, - r_Subscription_DetailView, - r_Content_ListView, - r_Content_DetailView, - r_UniqueContentRule_ListView, - r_UniqueContentRule_DetailView + UniqueContentRule_DetailView ) urlpatterns = [ path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), path("api-token-auth/", obtain_auth_token), - path("subchannel/", include([ - path("", SubChannel_ListView.as_view(), name="subchannel"), - path("/", SubChannel_DetailView.as_view(), name="subchannel-detail") - ])), + path("servers/", include([ + path("", Server_ListView.as_view()), + path("/", include([ + path("", Server_DetailView.as_view()), - path("filter/", include([ - path("", Filter_ListView.as_view(), name="filter"), - path("/", Filter_DetailView.as_view(), name="filter-detail") - ])), + path("filters/", include([ + path("", ContentFilter_ListView.as_view()), + path("/", ContentFilter_DetailView.as_view()) + ])), - path("subscription/", include([ - path("", Subscription_ListView.as_view(), name="subscription"), - path("/", include([ - path("", Subscription_DetailView.as_view(), name="subscription-detail"), - path("subchannels/", Subscription_SubChannelView.as_view(), name="subscription-channels") + path("message-styles/", include([ + path("", MessageStyle_ListView.as_view()), + path("/", MessageStyle_DetailView.as_view()) + ])), + + path("subscriptions/", include([ + path("", Subscription_ListView.as_view()), + path("/", include([ + path("", Subscription_DetailView.as_view()), + path("content/", include([ + path("", Content_ListView.as_view()), + path("/", Content_DetailView.as_view()) + ])) + ])) + ])) ])) ])), - path("saved-guilds/", include([ - path("", SavedGuild_ListView.as_view(), name="saved-guilds"), - path("/", SavedGuild_DetailView.as_view(), name="saved-guilds-detail") + path("message-mutators/", include([ + path("", MessageMutator_ListView.as_view()), + path("/", MessageMutator_DetailView.as_view()) ])), - path("guild-settings/", include([ - path("", GuildSettings_ListView.as_view(), name="guild-settings"), - path("/", GuildSettings_DetailView.as_view(), name="guild-settings-detail") - ])), - - path("tracked-content/", include([ - path("", TrackedContent_ListView.as_view(), name="tracked-content"), - path("/", TrackedContent_DetailView.as_view(), name="tracked-content-detail") - ])), - - path("article-mutator/", include([ - path("", ArticleMutator_ListView.as_view(), name="article-mutator"), - path("/", ArticleMutator_DetailView.as_view(), name="article-mutator-detail") - ])), - - path("unique-content-rule/", include([ - path("", UniqueContentRule_ListView.as_view(), name="unique-content-rule"), - path("/", UniqueContentRule_DetailView.as_view(), name="unique-content-rule-detail") - ])), - - - #region rewrite - - path("r_servers/", include([ - path("", r_Server_ListView.as_view()), - path("/", r_Server_DetailView.as_view()) - ])), - - path("r_content-filters/", include([ - path("", r_ContentFilter_ListView.as_view()), - path("/", r_ContentFilter_DetailView.as_view()) - ])), - - path("r_message-mutators/", include([ - path("", r_MessageMutator_ListView.as_view()), - path("/", r_MessageMutator_DetailView.as_view()) - ])), - - path("r_message-styles/", include([ - path("", r_MessageStyle_ListView.as_view()), - path("/", r_MessageStyle_DetailView.as_view()) - ])), - - path("r_subscriptions/", include([ - path("", r_Subscription_ListView.as_view()), - path("/", r_Subscription_DetailView.as_view()) - ])), - - path("r_content/", include([ - path("", r_Content_ListView.as_view()), - path("/", r_Content_DetailView.as_view()) - ])), - - path("r_unique-content-rules/", include([ - path("", r_UniqueContentRule_ListView.as_view()), - path("/", r_UniqueContentRule_DetailView.as_view()) + path("unique-content-rules/", include([ + path("", UniqueContentRule_ListView.as_view()), + path("/", UniqueContentRule_DetailView.as_view()) ])) ] diff --git a/apps/api/views.py b/apps/api/views.py index b75c018..af4dd34 100644 --- a/apps/api/views.py +++ b/apps/api/views.py @@ -14,46 +14,24 @@ from rest_framework.authentication import SessionAuthentication, TokenAuthentica from rest_framework.parsers import MultiPartParser, FormParser from apps.home.models import ( - SubChannel, - Filter, + Server, + ContentFilter, + MessageMutator, + MessageStyle, Subscription, - SavedGuilds, - TrackedContent, - ArticleMutator, - GuildSettings, - UniqueContentRule, - - #rewrite - r_Server, - r_ContentFilter, - r_MessageMutator, - r_MessageStyle, - r_Subscription, - r_Content, - r_UniqueContentRule + Content, + UniqueContentRule ) from apps.authentication.models import DiscordUser, ServerMember from .metadata import ExpandedMetadata from .serializers import ( - SubChannelSerializer, - FilterSerializer, - SubscriptionSerializer_GET, - SubscriptionSerializer_POST, - SavedGuildSerializer, - TrackedContentSerializer_GET, - TrackedContentSerializer_POST, - ArticleMutatorSerializer, - GuildSettingsSerializer, - UniqueContentRuleSerializer, - - #rewrite - r_ServerSerializer, - r_ContentFilterSerializer, - r_MessageMutatorSerializer, - r_MessageStyleSerializer, - r_SubscriptionSerializer, - r_ContentSerializer, - r_UniqueContentRuleSerializer + ServerSerializer, + ContentFilterSerializer, + MessageMutatorSerializer, + MessageStyleSerializer, + SubscriptionSerializer, + ContentSerializer, + UniqueContentRuleSerializer ) from .permissions import HasServerAccess from .errors import NotAMemberError @@ -73,611 +51,6 @@ def is_automated_admin(user): return user.user_type == DiscordUser.USER_TYPES.AUTOMATED_USER and user.is_superuser -# ================================================================================================= -#region SubChannels - -class SubChannel_ListView(generics.ListCreateAPIView): - """ - View to provide a list of SubChannel model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - serializer_class = SubChannelSerializer - queryset = SubChannel.objects.all().order_by("id") - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "channel_id", "channel_name", "subscription"] - search_fields = ["channel_name"] - - def get_queryset(self): - if self.request.user.is_superuser: - return SubChannel.objects.all() - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return SubChannel.objects.filter(subscription__guild_id__in=guild_ids) - - def post(self, request): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class SubChannel_DetailView(generics.RetrieveUpdateDestroyAPIView): - """ - View to provide details on a particular SubChannel model instances. - - Supports: GET, PUT, PATCH, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = SubChannelSerializer - queryset = SubChannel.objects.all().order_by("id") - - def get_queryset(self): - if self.request.user.is_superuser: - return SubChannel.objects.all() - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return SubChannel.objects.filter(subscription__guild_id__in=guild_ids) - - -#region Unique Content Rule - -class UniqueContentRule_ListView(generics.ListAPIView): - """ - View to provide a list of UniqueContentRule model instances. - Can also be used to create a new instance. - - Supports GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - serializer_class = UniqueContentRuleSerializer - metadata_class = ExpandedMetadata - queryset = UniqueContentRule.objects.all().order_by("id") - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "name", "value"] - - -class UniqueContentRule_DetailView(generics.RetrieveAPIView): - """ - View to provide details on a particular UniqueContentRule model instances. - - Supports: GET - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = UniqueContentRuleSerializer - queryset = UniqueContentRule.objects.all().order_by("id") - - - -# ================================================================================================= -#region Filter - -class Filter_ListView(generics.ListCreateAPIView): - """ - View to provide a list of Filter model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - serializer_class = FilterSerializer - metadata_class = ExpandedMetadata - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "name", "matching_algorithm", "match", "is_insensitive", "is_whitelist", "guild_id"] - search_fields = ["name", "match"] - - def get_queryset(self): - if self.request.user.is_superuser: - return Filter.objects.all().order_by("id") - - saved_guild_ids = SavedGuilds.objects \ - .filter(added_by=self.request.user.id) \ - .values("guild_id") - - return Filter.objects \ - .filter(guild_id__in=Subquery(saved_guild_ids)) \ - .order_by("id") - - def post(self, request): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class Filter_DetailView(generics.RetrieveUpdateDestroyAPIView): - """ - View to provide details on a particular Filter model instances. - - Supports: GET, PUT, PATCH, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = FilterSerializer - - def get_queryset(self): - if self.request.user.is_superuser: - return Filter.objects.all().order_by("id") - - saved_guild_ids = SavedGuilds.objects \ - .filter(added_by=self.request.user.id) \ - .values("guild_id") - - return Filter.objects \ - .filter(guild_id__in=Subquery(saved_guild_ids)) \ - .order_by("id") - - -# ================================================================================================= -#region Subscription - -class Subscription_ListView(generics.ListCreateAPIView): - """ - View to provide a list of Subscription model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - metadata_class = ExpandedMetadata - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = [ - "id", "name", "url", "guild_id", "creation_datetime", "extra_notes", "filters", "unique_content_rules", - "article_title_mutators", "article_desc_mutators", "embed_colour", "published_threshold", "active" - ] - search_fields = ["name", "url", "extra_notes"] - ordering_fields = ["name", "creation_datetime", "active"] - - read_serializer_class = SubscriptionSerializer_GET - write_serializer_class = SubscriptionSerializer_POST - - def get_serializer_class(self): - if self.request.method == "POST": - return self.write_serializer_class - - return self.read_serializer_class - - def get_queryset(self): - if self.request.user.is_superuser: - return Subscription.objects.all().order_by("-creation_datetime") - - saved_guild_ids = SavedGuilds.objects \ - .filter(added_by=self.request.user.id) \ - .values("guild_id") - - return Subscription.objects \ - .filter(guild_id__in=Subquery(saved_guild_ids)) \ - .order_by("-creation_datetime") - - def post(self, request): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class Subscription_DetailView(generics.RetrieveUpdateDestroyAPIView): - """ - View to provide details on a particular Subscription model instances. - - Supports: GET, PUT, PATCH, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = SubscriptionSerializer_POST - - def get_queryset(self): - if self.request.user.is_superuser: - return Subscription.objects.all().order_by("-creation_datetime") - - saved_guild_ids = SavedGuilds.objects \ - .filter(added_by=self.request.user.id) \ - .values("guild_id") - - return Subscription.objects \ - .filter(guild_id__in=Subquery(saved_guild_ids)) \ - .order_by("-creation_datetime") - - -class Subscription_SubChannelView(generics.DestroyAPIView): - """ - View to erase all subscription channels quickly. - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - def delete(self, *args, **kwargs): - - if self.request.user.is_superuser: - subscriptions = Subscription.objects.all() - else: - saved_guild_ids = SavedGuilds.objects \ - .filter(added_by=self.request.user.id) \ - .values("guild_id") - - subscriptions = Subscription.objects \ - .filter(guild_id__in=Subquery(saved_guild_ids)) - - if not subscriptions: - return Response( - {"detail": "You are forbidden from viewing this subscription"}, - status=status.HTTP_403_FORBIDDEN - ) - - subscription = subscriptions.filter(id=kwargs["pk"]).first() - - if not subscription: - return Response( - {"detail": "not found"}, - status=status.HTTP_404_NOT_FOUND - ) - - channels = SubChannel.objects.filter(subscription=subscription) - channels.delete(); - - return Response(status=status.HTTP_204_NO_CONTENT) - -# ================================================================================================= -#region Saved Guilds - -class SavedGuild_ListView(generics.ListCreateAPIView): - """ - View to provide a list of SavedGuild model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = None - serializer_class = SavedGuildSerializer - metadata_class = ExpandedMetadata - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "guild_id", "name", "icon", "added_by", "permissions", "owner"] - search_fields = ["name"] - - def get_queryset(self): - if self.request.user.is_superuser: - return SavedGuilds.objects.all() - - return SavedGuilds.objects.filter(added_by=self.request.user) - - def post(self, request): - - # TODO: - # the data used for admin/owner verification is provided - # from the client, this is a potential attack vector, and - # should be rewritten. - - is_owner = request.data["owner"].lower() == "true" - - # Check user is admin in server - if not (self.is_server_admin(request.data["permissions"]) or is_owner): - return Response( - {"detail": "You must be a server administrator"}, - status=status.HTTP_403_FORBIDDEN, - exception=False - ) - - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - def is_server_admin(self, permissions) -> bool: - - return (int(permissions) & 1 << 3) == 1 << 3 - - -class SavedGuild_DetailView(generics.RetrieveDestroyAPIView): - """ - View to provide details on a particular SavedGuild model instances. - - Supports: GET, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = SavedGuildSerializer - - def get_queryset(self): - if self.request.user.is_superuser: - return SavedGuilds.objects.all() - - return SavedGuilds.objects.filter(added_by=self.request.user) - - -# ================================================================================================= -#region Guild Settings - -class GuildSettings_ListView(generics.ListCreateAPIView): - """ - View to provide a list of GuildSettings model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - serializer_class = GuildSettingsSerializer - metadata_class = ExpandedMetadata - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "guild_id", "default_embed_colour", "active"] - - def get_queryset(self): - if self.request.user.is_superuser: - return GuildSettings.objects.all() - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return GuildSettings.objects.filter(guild_id__in=guild_ids) - - def post(self, request): - saved_guilds = SavedGuilds.objects.filter(added_by=request.user) - - if not saved_guilds: - return Response( - {"detail": "You must have an instance of saved guild with this guild_id"}, - status=status.HTTP_403_FORBIDDEN, - exception=False - ) - - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - def is_server_admin(self, permissions) -> bool: - - return (int(permissions) & 1 << 3) == 1 << 3 - - -class GuildSettings_DetailView(generics.RetrieveUpdateDestroyAPIView): - """ - View to provide details on a particular GuildSettings model instances. - - Supports: GET, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = GuildSettingsSerializer - - def get_queryset(self): - if self.request.user.is_superuser: - return GuildSettings.objects.all() - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return GuildSettings.objects.filter(guild_id__in=guild_ids) - - -# ================================================================================================= -#region Tracked Content - -class TrackedContent_ListView(generics.ListCreateAPIView): - """ - View to provide a list of TrackedContent model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - pagination_class = DefaultPagination - metadata_class = ExpandedMetadata - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["guid", "title", "url", "subscription", "subscription__guild_id", "channel_id", "blocked", "creation_datetime"] - search_fields = ["guid", "title", "url", "subscription__name", "subscription__url"] - ordering_fields = ["title", "subscription", "creation_datetime", "blocked"] - - read_serializer_class = TrackedContentSerializer_GET - write_serializer_class = TrackedContentSerializer_POST - - def get_serializer_class(self): - if self.request.method == "POST": - return self.write_serializer_class - - return self.read_serializer_class - - def get_queryset(self): - if self.request.user.is_superuser: - return TrackedContent.objects.all().order_by("-creation_datetime") - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return TrackedContent.objects.filter(subscription__guild_id__in=guild_ids).order_by("-creation_datetime") - - def post(self, request): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - try: - self.perform_create(serializer) - except IntegrityError as err: - return Response( - {"detail": str(err)}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - exception=True - ) - - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class TrackedContent_DetailView(generics.RetrieveUpdateDestroyAPIView): - """ - View to provide details on a particular TrackedContent model instances. - - Supports: GET, PUT, PATCH, DELETE - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = TrackedContentSerializer_POST - - def get_queryset(self): - if self.request.user.is_superuser: - return TrackedContent.objects.all() - - saved_guilds = SavedGuilds.objects.filter(added_by=self.request.user) - guild_ids = [guild.guild_id for guild in saved_guilds] - - return TrackedContent.objects.filter(subscription__guild_id__in=guild_ids) - - -# ================================================================================================= -#region Article Mutator - -class ArticleMutator_ListView(generics.ListAPIView): - """ - View to provide a list of ArticleMutator model instances. - Can also be used to create a new instance. - - Supports: GET, POST - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - metadata_class = ExpandedMetadata - queryset = ArticleMutator.objects.all().order_by("id") - - filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["id", "name", "value"] - - serializer_class = ArticleMutatorSerializer - - -class ArticleMutator_DetailView(generics.RetrieveAPIView): - """ - View to provide details on a particular ArticleMutator model instances. - - Supports: GET - """ - - authentication_classes = [SessionAuthentication, TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - serializer_class = ArticleMutatorSerializer - queryset = ArticleMutator.objects.all().order_by("id") - - -#region rewrite - - class ListView(generics.ListAPIView): authentication_classes = [SessionAuthentication, TokenAuthentication] permission_classes = [permissions.IsAuthenticated] @@ -712,120 +85,122 @@ class DeletableDetailView(generics.RetrieveDestroyAPIView): parser_classes = [MultiPartParser, FormParser] -class r_Server_ListView(ListView): +class Server_ListView(ListView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_ServerSerializer + serializer_class = ServerSerializer def get_queryset(self): - return r_Server.objects.all() + servers = ServerMember.objects.filter(user=self.request.user).values_list("server", flat=True) + return Server.objects.filter(id__in=servers) -class r_Server_DetailView(DetailView): - serializer_class = r_ServerSerializer +class Server_DetailView(DetailView): + serializer_class = ServerSerializer def get_queryset(self): - return r_Server.objects.all() + servers = ServerMember.objects.filter(user=self.request.user).values_list("server", flat=True) + return Server.objects.filter(id__in=servers) -class r_ContentFilter_ListView(ListCreateView): +class ContentFilter_ListView(ListCreateView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_ContentFilterSerializer + serializer_class = ContentFilterSerializer def get_queryset(self): - return r_ContentFilter.objects.all() + return ContentFilter.objects.all() -class r_ContentFilter_DetailView(ChangableDetailView): - serializer_class = r_ContentFilterSerializer +class ContentFilter_DetailView(ChangableDetailView): + serializer_class = ContentFilterSerializer def get_queryset(self): - return r_ContentFilter.objects.all() + return ContentFilter.objects.all() -class r_MessageMutator_ListView(ListView): # instances of this one are pre-defined ONLY +class MessageMutator_ListView(ListView): # instances of this one are pre-defined ONLY filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_MessageMutatorSerializer + serializer_class = MessageMutatorSerializer def get_queryset(self): - return r_MessageMutator.objects.all() + return MessageMutator.objects.all() -class r_MessageMutator_DetailView(DetailView): - serializer_class = r_MessageMutatorSerializer +class MessageMutator_DetailView(DetailView): + serializer_class = MessageMutatorSerializer def get_queryset(self): - return r_MessageMutator.objects.all() + return MessageMutator.objects.all() -class r_MessageStyle_ListView(ListCreateView): +class MessageStyle_ListView(ListCreateView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_MessageStyleSerializer + serializer_class = MessageStyleSerializer def get_queryset(self): - return r_MessageStyle.objects.all() + return MessageStyle.objects.all() -class r_MessageStyle_DetailView(ChangableDetailView): - serializer_class = r_MessageStyleSerializer +class MessageStyle_DetailView(ChangableDetailView): + serializer_class = MessageStyleSerializer def get_queryset(self): - return r_MessageStyle.objects.all() + return MessageStyle.objects.all() -class r_Subscription_ListView(ListCreateView): +class Subscription_ListView(ListCreateView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_SubscriptionSerializer + serializer_class = SubscriptionSerializer def get_queryset(self): - return r_Subscription.objects.all() + return Subscription.objects.all() -class r_Subscription_DetailView(ChangableDetailView): - serializer_class = r_SubscriptionSerializer +class Subscription_DetailView(ChangableDetailView): + serializer_class = SubscriptionSerializer def get_queryset(self): - return r_Subscription.objects.all() + return Subscription.objects.all() -class r_Content_ListView(ListCreateView): +class Content_ListView(ListCreateView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_ContentSerializer + serializer_class = ContentSerializer def get_queryset(self): - return r_Content.objects.all() + return Content.objects.all() -class r_Content_DetailView(ChangableDetailView): - serializer_class = r_ContentSerializer +class Content_DetailView(ChangableDetailView): + serializer_class = ContentSerializer def get_queryset(self): - return r_Content.objects.all() + return Content.objects.all() -class r_UniqueContentRule_ListView(ListCreateView): +class UniqueContentRule_ListView(ListCreateView): filterset_fields = [] search_fields = [] ordering_fields = [] - serializer_class = r_UniqueContentRuleSerializer + serializer_class = UniqueContentRuleSerializer def get_queryset(self): - return r_UniqueContentRule.objects.all() + return UniqueContentRule.objects.all() -class r_UniqueContentRule_DetailView(ChangableDetailView): - serializer_class = r_UniqueContentRuleSerializer +class UniqueContentRule_DetailView(ChangableDetailView): + serializer_class = UniqueContentRuleSerializer def get_queryset(self): - return r_UniqueContentRule.objects.all() + return UniqueContentRule.objects.all() diff --git a/apps/authentication/migrations/0001_initial.py b/apps/authentication/migrations/0001_initial.py index 62fa6cf..a19a141 100644 --- a/apps/authentication/migrations/0001_initial.py +++ b/apps/authentication/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 5.0.4 on 2024-05-03 13:14 +# Generated by Django 5.0.4 on 2024-09-24 14:05 +import django.db.models.deletion import django.utils.timezone +from django.conf import settings from django.db import migrations, models @@ -10,6 +12,7 @@ class Migration(migrations.Migration): dependencies = [ ('auth', '0012_alter_user_first_name_max_length'), + ('home', '0001_initial'), ] operations = [ @@ -26,6 +29,8 @@ class Migration(migrations.Migration): ('mfa_enabled', models.BooleanField(help_text='whether the user has two factor enabled on their account', verbose_name='mfa enabled')), ('last_login', models.DateTimeField(default=django.utils.timezone.now, help_text='datetime of the previous login', verbose_name='last login')), ('access_token', models.CharField(help_text='token for the application to make api calls on behalf of the user.', max_length=100, verbose_name='access token')), + ('token_expires', models.DateTimeField(help_text='when to request a new access token.', verbose_name='token expires')), + ('refresh_token', models.CharField(help_text='token for the application to request a new access token.', max_length=100, verbose_name='refresh token')), ('user_type', models.CharField(choices=[('D', 'discord'), ('A', 'automated')], default='D', help_text='What type of user is this?', max_length=32, verbose_name='user type')), ('is_active', models.BooleanField(default=True, help_text='Use as a "soft delete" rather than deleting the user.', verbose_name='active status')), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), @@ -37,4 +42,14 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.CreateModel( + name='ServerMember', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('permissions', models.CharField(max_length=32)), + ('is_owner', models.BooleanField(default=False)), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.server')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), ] diff --git a/apps/authentication/migrations/0002_discorduser_token_expires.py b/apps/authentication/migrations/0002_discorduser_token_expires.py deleted file mode 100644 index 448cb83..0000000 --- a/apps/authentication/migrations/0002_discorduser_token_expires.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.0.4 on 2024-05-31 22:41 - -from django.db import migrations, models -from django.utils import timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='discorduser', - name='token_expires', - field=models.DateTimeField(default=timezone.now, help_text='when to request a new access token.', verbose_name='token expires'), - preserve_default=False, - ), - ] diff --git a/apps/authentication/migrations/0003_discorduser_refresh_token.py b/apps/authentication/migrations/0003_discorduser_refresh_token.py deleted file mode 100644 index 4fd6bfd..0000000 --- a/apps/authentication/migrations/0003_discorduser_refresh_token.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-01 16:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0002_discorduser_token_expires'), - ] - - operations = [ - migrations.AddField( - model_name='discorduser', - name='refresh_token', - field=models.CharField(default='1', help_text='token for the application to request a new access token.', max_length=100, verbose_name='refresh token'), - preserve_default=False, - ), - ] diff --git a/apps/authentication/migrations/0004_servermember.py b/apps/authentication/migrations/0004_servermember.py deleted file mode 100644 index 28563e5..0000000 --- a/apps/authentication/migrations/0004_servermember.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-19 20:20 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0003_discorduser_refresh_token'), - ('home', '0026_temp_testonly_migration'), - ] - - operations = [ - migrations.CreateModel( - name='ServerMember', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('nick', models.CharField(max_length=32)), - ('permissions', models.CharField(max_length=32)), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_server')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/apps/authentication/migrations/0005_alter_servermember_nick.py b/apps/authentication/migrations/0005_alter_servermember_nick.py deleted file mode 100644 index 2b0955c..0000000 --- a/apps/authentication/migrations/0005_alter_servermember_nick.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-23 20:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0004_servermember'), - ] - - operations = [ - migrations.AlterField( - model_name='servermember', - name='nick', - field=models.CharField(blank=True, max_length=32, null=True), - ), - ] diff --git a/apps/authentication/migrations/0006_remove_servermember_nick_servermember_is_owner.py b/apps/authentication/migrations/0006_remove_servermember_nick_servermember_is_owner.py deleted file mode 100644 index bcb5092..0000000 --- a/apps/authentication/migrations/0006_remove_servermember_nick_servermember_is_owner.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-23 20:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('authentication', '0005_alter_servermember_nick'), - ] - - operations = [ - migrations.RemoveField( - model_name='servermember', - name='nick', - ), - migrations.AddField( - model_name='servermember', - name='is_owner', - field=models.BooleanField(default=False), - ), - ] diff --git a/apps/authentication/models.py b/apps/authentication/models.py index e3c102e..982304e 100644 --- a/apps/authentication/models.py +++ b/apps/authentication/models.py @@ -168,7 +168,7 @@ class ServerMember(models.Model): id = models.AutoField(primary_key=True) user = models.ForeignKey(to=DiscordUser, on_delete=models.CASCADE) - server = models.ForeignKey(to="home.r_Server", on_delete=models.CASCADE) + server = models.ForeignKey(to="home.Server", on_delete=models.CASCADE) permissions = models.CharField(max_length=32) is_owner = models.BooleanField(default=False) diff --git a/apps/home/admin.py b/apps/home/admin.py index 08d447e..031729c 100644 --- a/apps/home/admin.py +++ b/apps/home/admin.py @@ -3,83 +3,23 @@ from django.contrib import admin from .models import ( + Server, + ContentFilter, + MessageMutator, + MessageStyle, Subscription, - SavedGuilds, - Filter, - SubChannel, - TrackedContent, - ArticleMutator, - GuildSettings, - - # temp rewrite - r_Server, - r_ContentFilter, - r_MessageMutator, - r_MessageStyle, - r_Subscription, - r_Content, - r_UniqueContentRule + Content, + UniqueContentRule ) -@admin.register(Subscription) -class SubscriptionAdmin(admin.ModelAdmin): - list_display = [ - "id", "name", "url", "guild_id", - "creation_datetime", "active" - ] - -@admin.register(SubChannel) -class SubChannelAdmin(admin.ModelAdmin): - list_display = [ - "id", "channel_id", "subscription" - ] - - -@admin.register(Filter) -class FilterAdmin(admin.ModelAdmin): - list_display = [ - "id", "name", "guild_id" - ] - - -@admin.register(TrackedContent) -class TrackedContentAdmin(admin.ModelAdmin): - list_display = [ - "guid", "title", "url", "subscription", "blocked", "creation_datetime" - ] - - -@admin.register(SavedGuilds) -class SavedGuildAdmin(admin.ModelAdmin): - list_display = [ - "id", "name", "icon" - ] - - -@admin.register(ArticleMutator) -class ArticleMutatorAdmin(admin.ModelAdmin): - list_display = [ - "id", "name", "value" - ] - -@admin.register(GuildSettings) -class GuildSettingsAdmin(admin.ModelAdmin): - list_display = [ - "id", "guild_id", "default_embed_colour", "active" - ] - - -#region rewrite - - -@admin.register(r_Server) -class r_ServerAdmin(admin.ModelAdmin): +@admin.register(Server) +class ServerAdmin(admin.ModelAdmin): list_display = ["id", "name", "icon_hash", "active"] -@admin.register(r_ContentFilter) -class r_ContentFilterAdmin(admin.ModelAdmin): +@admin.register(ContentFilter) +class ContentFilterAdmin(admin.ModelAdmin): list_display = [ "id", "server", @@ -91,8 +31,8 @@ class r_ContentFilterAdmin(admin.ModelAdmin): ] -@admin.register(r_MessageMutator) -class r_MessageMutatorAdmin(admin.ModelAdmin): +@admin.register(MessageMutator) +class MessageMutatorAdmin(admin.ModelAdmin): list_display = [ "id", "name", @@ -100,8 +40,8 @@ class r_MessageMutatorAdmin(admin.ModelAdmin): ] -@admin.register(r_MessageStyle) -class r_MessageStyleAdmin(admin.ModelAdmin): +@admin.register(MessageStyle) +class MessageStyleAdmin(admin.ModelAdmin): list_display = [ "id", "server", @@ -116,8 +56,8 @@ class r_MessageStyleAdmin(admin.ModelAdmin): ] -@admin.register(r_Subscription) -class r_Subscription(admin.ModelAdmin): +@admin.register(Subscription) +class Subscription(admin.ModelAdmin): list_display = [ "id", "server", @@ -131,8 +71,8 @@ class r_Subscription(admin.ModelAdmin): ] -@admin.register(r_Content) -class r_ContentAdmin(admin.ModelAdmin): +@admin.register(Content) +class ContentAdmin(admin.ModelAdmin): list_display = [ "id", "subscription", @@ -144,8 +84,8 @@ class r_ContentAdmin(admin.ModelAdmin): ] -@admin.register(r_UniqueContentRule) -class r_UniqueContentRule(admin.ModelAdmin): +@admin.register(UniqueContentRule) +class UniqueContentRule(admin.ModelAdmin): list_display = [ "id", "name", diff --git a/apps/home/migrations/0001_initial.py b/apps/home/migrations/0001_initial.py index 6776bf3..c4c6a8b 100644 --- a/apps/home/migrations/0001_initial.py +++ b/apps/home/migrations/0001_initial.py @@ -1,8 +1,7 @@ -# Generated by Django 5.0.4 on 2024-06-17 01:11 +# Generated by Django 5.0.4 on 2024-09-24 14:05 import django.db.models.deletion import django.utils.timezone -from django.conf import settings from django.db import migrations, models @@ -11,53 +10,70 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Filter', + name='MessageMutator', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('value', models.CharField(max_length=32)), + ], + ), + migrations.CreateModel( + name='Server', + fields=[ + ('id', models.PositiveIntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128)), + ('icon_hash', models.CharField(blank=True, max_length=128, null=True)), + ('active', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='UniqueContentRule', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('value', models.CharField(max_length=32)), + ], + ), + migrations.CreateModel( + name='MessageStyle', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('is_embed', models.BooleanField(default=True)), + ('is_hyperlinked', models.BooleanField()), + ('show_author', models.BooleanField()), + ('show_timestamp', models.BooleanField()), + ('show_images', models.BooleanField()), + ('fetch_images', models.BooleanField()), + ('description_mutator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='desc_mutated_messagestyle', to='home.messagemutator')), + ('title_mutator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='title_mutated_messagestyle', to='home.messagemutator')), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.server')), + ], + ), + migrations.CreateModel( + name='ContentFilter', fields=[ ('id', models.AutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=32)), - ('keywords', models.CharField(blank=True, max_length=128, null=True)), - ('regex', models.CharField(blank=True, max_length=128, null=True)), - ('whitelist', models.BooleanField(default=False)), - ('guild_id', models.CharField(max_length=128)), + ('match', models.CharField(max_length=256)), + ('matching_algorithm', models.PositiveIntegerField(choices=[(0, 'None'), (1, 'Any: Item contains any of these words (space separated)'), (2, 'All: Item contains all of these words (space separated)'), (3, 'Exact: Item contains this string'), (4, 'Regular expression: Item matches this regex'), (5, 'Fuzzy: Item contains a word similar to this word')])), + ('is_insensitive', models.BooleanField()), + ('is_whitelist', models.BooleanField()), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.server')), ], - options={ - 'verbose_name': 'filter', - 'verbose_name_plural': 'filters', - 'get_latest_by': 'id', - }, ), migrations.CreateModel( - name='SavedGuilds', + name='BotLogicLogs', fields=[ ('id', models.AutoField(primary_key=True, serialize=False)), - ('guild_id', models.CharField(help_text='Discord snowflake ID for the represented guild.', max_length=128, verbose_name='guild id')), - ('name', models.CharField(help_text='Name of the represented guild.', max_length=128)), - ('icon', models.CharField(help_text="Hash for the represented guild's icon.", max_length=128)), - ('permissions', models.CharField(help_text='Guild permissions for the user who added this instance.', max_length=64)), - ('owner', models.BooleanField(default=False, help_text="Does the 'added by' user own this guild?")), + ('level', models.CharField(max_length=32)), + ('message', models.CharField(max_length=256)), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False)), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.server')), ], - options={ - 'verbose_name': 'saved guild', - 'verbose_name_plural': 'saved guilds', - 'get_latest_by': '-creation_datetime', - }, - ), - migrations.CreateModel( - name='SubChannel', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('channel_id', models.CharField(help_text='Discord snowflake ID for the represented Channel.', max_length=128, verbose_name='channel id')), - ], - options={ - 'verbose_name': 'SubChannel', - 'verbose_name_plural': 'SubChannels', - 'get_latest_by': 'id', - }, ), migrations.CreateModel( name='Subscription', @@ -65,76 +81,26 @@ class Migration(migrations.Migration): ('id', models.AutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=32)), ('url', models.URLField()), - ('guild_id', models.CharField(max_length=128)), - ('creation_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False)), - ('extra_notes', models.CharField(blank=True, max_length=250, null=True)), - ('uwuify', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False)), + ('updated_at', models.DateTimeField(default=django.utils.timezone.now)), + ('extra_notes', models.CharField(blank=True, default='', max_length=250)), ('active', models.BooleanField(default=True)), + ('filters', models.ManyToManyField(blank=True, to='home.contentfilter')), + ('message_style', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='home.messagestyle')), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.server')), + ('unique_rules', models.ManyToManyField(to='home.uniquecontentrule')), ], - options={ - 'verbose_name': 'subscription', - 'verbose_name_plural': 'subscriptions', - 'get_latest_by': '-creation_datetime', - }, ), migrations.CreateModel( - name='TrackedContent', + name='Content', fields=[ ('id', models.AutoField(primary_key=True, serialize=False)), - ('guid', models.CharField(max_length=128)), - ('title', models.CharField(max_length=128)), - ('url', models.URLField(unique=True)), - ('blocked', models.BooleanField(default=False)), - ('creation_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False)), - ('guild_id', models.CharField(max_length=128)), + ('item_id', models.CharField(max_length=1024)), + ('item_guid', models.CharField(max_length=1024)), + ('item_url', models.CharField(max_length=1024)), + ('item_title', models.CharField(max_length=1024)), + ('item_content_hash', models.CharField(max_length=1024)), + ('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.subscription')), ], - options={ - 'verbose_name': 'tracked contents', - 'get_latest_by': '-creation_datetime', - }, - ), - migrations.AddConstraint( - model_name='filter', - constraint=models.UniqueConstraint(fields=('name', 'guild_id'), name='unique name & guild id pair'), - ), - migrations.AddField( - model_name='savedguilds', - name='added_by', - field=models.ForeignKey(help_text='The user who added created this instance.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='added by'), - ), - migrations.AddField( - model_name='subscription', - name='filters', - field=models.ManyToManyField(blank=True, to='home.filter'), - ), - migrations.AddField( - model_name='subchannel', - name='subscription', - field=models.ForeignKey(help_text='The linked Subscription, must be unique.', on_delete=django.db.models.deletion.CASCADE, to='home.subscription'), - ), - migrations.AddField( - model_name='trackedcontent', - name='subscription', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.subscription'), - ), - migrations.AddConstraint( - model_name='savedguilds', - constraint=models.UniqueConstraint(fields=('added_by', 'guild_id'), name='unique added_by & guild_id pair'), - ), - migrations.AddConstraint( - model_name='subscription', - constraint=models.UniqueConstraint(fields=('name', 'guild_id'), name='unique name & server pair'), - ), - migrations.AddConstraint( - model_name='subchannel', - constraint=models.UniqueConstraint(fields=('channel_id', 'subscription'), name='unique channel id and subscription pair'), - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('guid', 'guild_id'), name='unique guid & guild_id pair'), - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('url', 'guild_id'), name='unique url & guild_id pair'), ), ] diff --git a/apps/home/migrations/0002_articlemutator.py b/apps/home/migrations/0002_articlemutator.py deleted file mode 100644 index 89e2a02..0000000 --- a/apps/home/migrations/0002_articlemutator.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-17 01:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='ArticleMutator', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('value', models.CharField(max_length=32)), - ], - ), - ] diff --git a/apps/home/migrations/0002_predefined_data.py b/apps/home/migrations/0002_predefined_data.py new file mode 100644 index 0000000..849d5c8 --- /dev/null +++ b/apps/home/migrations/0002_predefined_data.py @@ -0,0 +1,47 @@ +# Generated by Django 5.0.4 on 2024-09-24 14:06 +# This migration was manually configured to create predefined data by corbz + +from django.db import migrations + +def add_mutators(apps, schema_editor): + model = apps.get_model("home", "MessageMutator") + model.objects.create(name="Uwuify", value="uwuify") + model.objects.create(name="Uwuify (NSFW)", value="uwuify_nsfw") + model.objects.create(name="Gothic Script", value="gothic") + model.objects.create(name="Emoji Substitute", value="emj_substitute") + model.objects.create(name="Zalgo", value="zalgo") + model.objects.create(name="Morse Code", value="morse_code") + model.objects.create(name="Binary", value="to_bin") + model.objects.create(name="Hexadecimal", value="to_hex") + model.objects.create(name="Remove Vowels", value="rm_vowels") + model.objects.create(name="Double Characters", value="dbl_chars") + model.objects.create(name="Small Case", value="small_caps") + model.objects.create(name="L33t Sp34k", value="leet_speak") + model.objects.create(name="Pig Latin", value="pig_latin") + model.objects.create(name="Upside Down", value="flipped") + model.objects.create(name="All Reversed", value="reverse_text") + model.objects.create(name="Reversed Words", value="backwards_words") + model.objects.create(name="Shuffle Words", value="shuffle_words") + model.objects.create(name="Random Case", value="rnd_case") + model.objects.create(name="Gibberish", value="gibberish") + model.objects.create(name="Shakespearean", value="shakespear") + +def add_rules(apps, schema_editor): + model = apps.get_model("home", "UniqueContentRule") + model.objects.create(name="GUID", value="GUID") + model.objects.create(name="ID", value="ID") + model.objects.create(name="URL", value="URL") + model.objects.create(name="Title", value="TITLE") + model.objects.create(name="Content Hash", value="CONTENT") + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0001_initial'), + ] + + operations = [ + migrations.RunPython(add_mutators), + migrations.RunPython(add_rules) + ] diff --git a/apps/home/migrations/0003_initial_mutator_data.py b/apps/home/migrations/0003_initial_mutator_data.py deleted file mode 100644 index be908c8..0000000 --- a/apps/home/migrations/0003_initial_mutator_data.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-17 01:23 - -from django.db import migrations - -def add_mutators(apps, schema_editor): - ArticleMutator = apps.get_model("home", "ArticleMutator") - ArticleMutator.objects.create(name="Uwuify", value="uwuify") - ArticleMutator.objects.create(name="Uwuify (NSFW)", value="uwuify_nsfw") - ArticleMutator.objects.create(name="Gothic Script", value="gothic") - ArticleMutator.objects.create(name="Emoji Substitute", value="emj_substitute") - ArticleMutator.objects.create(name="Zalgo", value="zalgo") - ArticleMutator.objects.create(name="Morse Code", value="morse_code") - ArticleMutator.objects.create(name="Binary", value="to_bin") - ArticleMutator.objects.create(name="Hexadecimal", value="to_hex") - ArticleMutator.objects.create(name="Remove Vowels", value="rm_vowels") - ArticleMutator.objects.create(name="Double Characters", value="dbl_chars") - ArticleMutator.objects.create(name="Small Case", value="small_caps") - ArticleMutator.objects.create(name="L33t Sp34k", value="leet_speak") - ArticleMutator.objects.create(name="Pig Latin", value="pig_latin") - ArticleMutator.objects.create(name="Upside Down", value="flipped") - ArticleMutator.objects.create(name="All Reversed", value="reverse_text") - ArticleMutator.objects.create(name="Reversed Words", value="backwards_words") - ArticleMutator.objects.create(name="Shuffle Words", value="shuffle_words") - ArticleMutator.objects.create(name="Random Case", value="rnd_case") - ArticleMutator.objects.create(name="Gibberish", value="gibberish") - ArticleMutator.objects.create(name="Shakespearean", value="shakespear") - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0002_articlemutator'), - ] - - operations = [ - migrations.RunPython(add_mutators) - ] diff --git a/apps/home/migrations/0004_remove_subscription_uwuify.py b/apps/home/migrations/0004_remove_subscription_uwuify.py deleted file mode 100644 index 05d8e92..0000000 --- a/apps/home/migrations/0004_remove_subscription_uwuify.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-17 01:31 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0003_initial_mutator_data'), - ] - - operations = [ - migrations.RemoveField( - model_name='subscription', - name='uwuify', - ), - ] diff --git a/apps/home/migrations/0005_subscription_mutators.py b/apps/home/migrations/0005_subscription_mutators.py deleted file mode 100644 index 475700e..0000000 --- a/apps/home/migrations/0005_subscription_mutators.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-17 01:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0004_remove_subscription_uwuify'), - ] - - operations = [ - migrations.AddField( - model_name='subscription', - name='mutators', - field=models.ManyToManyField(blank=True, to='home.articlemutator'), - ), - ] diff --git a/apps/home/migrations/0006_alter_trackedcontent_guid_alter_trackedcontent_title.py b/apps/home/migrations/0006_alter_trackedcontent_guid_alter_trackedcontent_title.py deleted file mode 100644 index aa29e0c..0000000 --- a/apps/home/migrations/0006_alter_trackedcontent_guid_alter_trackedcontent_title.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-18 11:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0005_subscription_mutators'), - ] - - operations = [ - migrations.AlterField( - model_name='trackedcontent', - name='guid', - field=models.CharField(max_length=256), - ), - migrations.AlterField( - model_name='trackedcontent', - name='title', - field=models.CharField(max_length=728), - ), - ] diff --git a/apps/home/migrations/0007_remove_subscription_mutators_and_more.py b/apps/home/migrations/0007_remove_subscription_mutators_and_more.py deleted file mode 100644 index 053914a..0000000 --- a/apps/home/migrations/0007_remove_subscription_mutators_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-25 08:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0006_alter_trackedcontent_guid_alter_trackedcontent_title'), - ] - - operations = [ - migrations.RemoveField( - model_name='subscription', - name='mutators', - ), - migrations.AddField( - model_name='subscription', - name='article_desc_mutators', - field=models.ManyToManyField(blank=True, related_name='desc_mutated_subscriptions', to='home.articlemutator'), - ), - migrations.AddField( - model_name='subscription', - name='article_title_mutators', - field=models.ManyToManyField(blank=True, related_name='title_mutated_subscriptions', to='home.articlemutator'), - ), - ] diff --git a/apps/home/migrations/0008_subscription_article_fetch_limit_and_more.py b/apps/home/migrations/0008_subscription_article_fetch_limit_and_more.py deleted file mode 100644 index 783ee86..0000000 --- a/apps/home/migrations/0008_subscription_article_fetch_limit_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-25 09:41 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0007_remove_subscription_mutators_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='subscription', - name='article_fetch_limit', - field=models.PositiveSmallIntegerField(default=10, validators=[django.core.validators.MaxValueValidator(1), django.core.validators.MinValueValidator(10)]), - ), - migrations.AddField( - model_name='subscription', - name='reset_article_fetch_limit', - field=models.BooleanField(default=False), - ), - ] diff --git a/apps/home/migrations/0009_subscription_embed_colour.py b/apps/home/migrations/0009_subscription_embed_colour.py deleted file mode 100644 index 14f6008..0000000 --- a/apps/home/migrations/0009_subscription_embed_colour.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-06-26 13:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0008_subscription_article_fetch_limit_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='subscription', - name='embed_colour', - field=models.CharField(default='3498db', max_length=6), - ), - ] diff --git a/apps/home/migrations/0010_alter_subscription_embed_colour_and_more.py b/apps/home/migrations/0010_alter_subscription_embed_colour_and_more.py deleted file mode 100644 index aa4f04b..0000000 --- a/apps/home/migrations/0010_alter_subscription_embed_colour_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-02 11:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0009_subscription_embed_colour'), - ] - - operations = [ - migrations.AlterField( - model_name='subscription', - name='embed_colour', - field=models.CharField(blank=True, default='3498db', max_length=6), - ), - migrations.AlterField( - model_name='trackedcontent', - name='url', - field=models.URLField(), - ), - ] diff --git a/apps/home/migrations/0011_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py b/apps/home/migrations/0011_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py deleted file mode 100644 index 72ade9d..0000000 --- a/apps/home/migrations/0011_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-02 12:36 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0010_alter_subscription_embed_colour_and_more'), - ] - - operations = [ - migrations.RemoveConstraint( - model_name='trackedcontent', - name='unique guid & guild_id pair', - ), - migrations.RemoveConstraint( - model_name='trackedcontent', - name='unique url & guild_id pair', - ), - migrations.RenameField( - model_name='trackedcontent', - old_name='guild_id', - new_name='channel_id', - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('guid', 'channel_id'), name='unique guid & guild_id pair'), - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('url', 'channel_id'), name='unique url & guild_id pair'), - ), - ] diff --git a/apps/home/migrations/0012_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py b/apps/home/migrations/0012_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py deleted file mode 100644 index 9df57fc..0000000 --- a/apps/home/migrations/0012_remove_trackedcontent_unique_guid_guild_id_pair_and_more.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-05 13:29 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0011_remove_trackedcontent_unique_guid_guild_id_pair_and_more'), - ] - - operations = [ - migrations.RemoveConstraint( - model_name='trackedcontent', - name='unique guid & guild_id pair', - ), - migrations.RemoveConstraint( - model_name='trackedcontent', - name='unique url & guild_id pair', - ), - migrations.AddField( - model_name='subscription', - name='article_fetch_image', - field=models.BooleanField(default=True, help_text='Will the resulting article have an image?'), - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('guid', 'channel_id'), name='unique guid & channel_id pair'), - ), - migrations.AddConstraint( - model_name='trackedcontent', - constraint=models.UniqueConstraint(fields=('url', 'channel_id'), name='unique url & channel_id pair'), - ), - ] diff --git a/apps/home/migrations/0013_subscription_published_theshold.py b/apps/home/migrations/0013_subscription_published_theshold.py deleted file mode 100644 index beeeb6b..0000000 --- a/apps/home/migrations/0013_subscription_published_theshold.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-10 09:08 - -import django.utils.timezone -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0012_remove_trackedcontent_unique_guid_guild_id_pair_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='subscription', - name='published_theshold', - field=models.DateField(blank=True, default=django.utils.timezone.now), - ), - ] diff --git a/apps/home/migrations/0014_rename_published_theshold_subscription_published_threshold.py b/apps/home/migrations/0014_rename_published_theshold_subscription_published_threshold.py deleted file mode 100644 index eeff806..0000000 --- a/apps/home/migrations/0014_rename_published_theshold_subscription_published_threshold.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-10 09:27 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0013_subscription_published_theshold'), - ] - - operations = [ - migrations.RenameField( - model_name='subscription', - old_name='published_theshold', - new_name='published_threshold', - ), - ] diff --git a/apps/home/migrations/0015_alter_filter_options_remove_filter_keywords_and_more.py b/apps/home/migrations/0015_alter_filter_options_remove_filter_keywords_and_more.py deleted file mode 100644 index 1bbd443..0000000 --- a/apps/home/migrations/0015_alter_filter_options_remove_filter_keywords_and_more.py +++ /dev/null @@ -1,67 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-10 13:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0014_rename_published_theshold_subscription_published_threshold'), - ] - - operations = [ - migrations.AlterModelOptions( - name='filter', - options={'ordering': ('name',)}, - ), - migrations.RemoveField( - model_name='filter', - name='keywords', - ), - migrations.RemoveField( - model_name='filter', - name='regex', - ), - migrations.RemoveField( - model_name='filter', - name='whitelist', - ), - migrations.RemoveField( - model_name='subscription', - name='article_fetch_limit', - ), - migrations.RemoveField( - model_name='subscription', - name='reset_article_fetch_limit', - ), - migrations.AddField( - model_name='filter', - name='is_insensitive', - field=models.BooleanField(default=True, verbose_name='is insensitive'), - ), - migrations.AddField( - model_name='filter', - name='is_whitelist', - field=models.BooleanField(default=False, verbose_name='is whitelist'), - ), - migrations.AddField( - model_name='filter', - name='match', - field=models.CharField(blank=True, max_length=256, verbose_name='match'), - ), - migrations.AddField( - model_name='filter', - name='matching_algorithm', - field=models.PositiveIntegerField(choices=[(0, 'None'), (1, 'Any word'), (2, 'All words'), (3, 'Exact match'), (4, 'Regular expression'), (5, 'Fuzzy word'), (6, 'Automatic')], default=1, verbose_name='matching algorithm'), - ), - migrations.AlterField( - model_name='filter', - name='guild_id', - field=models.CharField(max_length=128, verbose_name='guild id'), - ), - migrations.AlterField( - model_name='filter', - name='name', - field=models.CharField(max_length=128, verbose_name='name'), - ), - ] diff --git a/apps/home/migrations/0016_alter_filter_matching_algorithm_and_more.py b/apps/home/migrations/0016_alter_filter_matching_algorithm_and_more.py deleted file mode 100644 index 531acd7..0000000 --- a/apps/home/migrations/0016_alter_filter_matching_algorithm_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-10 19:19 - -import django.utils.timezone -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0015_alter_filter_options_remove_filter_keywords_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='filter', - name='matching_algorithm', - field=models.PositiveIntegerField(choices=[(0, 'None'), (1, 'Any: Item contains any of these words (space separated)'), (2, 'All: Item contains all of these words (space separated)'), (3, 'Exact: Item contains this string'), (4, 'Regular expression: Item matches this regex'), (5, 'Fuzzy: Item contains a word similar to this word')], default=1, verbose_name='matching algorithm'), - ), - migrations.AlterField( - model_name='subscription', - name='published_threshold', - field=models.DateTimeField(blank=True, default=django.utils.timezone.now), - ), - ] diff --git a/apps/home/migrations/0017_trackedcontent_message_id.py b/apps/home/migrations/0017_trackedcontent_message_id.py deleted file mode 100644 index 7146037..0000000 --- a/apps/home/migrations/0017_trackedcontent_message_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-11 21:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0016_alter_filter_matching_algorithm_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='trackedcontent', - name='message_id', - field=models.CharField(max_length=128), - preserve_default=False, - ), - ] diff --git a/apps/home/migrations/0018_subchannel_channel_name.py b/apps/home/migrations/0018_subchannel_channel_name.py deleted file mode 100644 index 9304dc1..0000000 --- a/apps/home/migrations/0018_subchannel_channel_name.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-20 18:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0017_trackedcontent_message_id'), - ] - - operations = [ - migrations.AddField( - model_name='subchannel', - name='channel_name', - field=models.CharField(default='placeholder-channel-name', help_text='Name of the represented Channel.', max_length=256, verbose_name='channel name'), - preserve_default=False, - ), - ] diff --git a/apps/home/migrations/0019_savedguilds_default_embed_colour.py b/apps/home/migrations/0019_savedguilds_default_embed_colour.py deleted file mode 100644 index 50c68df..0000000 --- a/apps/home/migrations/0019_savedguilds_default_embed_colour.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-23 14:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0018_subchannel_channel_name'), - ] - - operations = [ - migrations.AddField( - model_name='savedguilds', - name='default_embed_colour', - field=models.CharField(blank=True, default='3498db', max_length=6), - ), - ] diff --git a/apps/home/migrations/0020_guildsettings.py b/apps/home/migrations/0020_guildsettings.py deleted file mode 100644 index beb6ae8..0000000 --- a/apps/home/migrations/0020_guildsettings.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.0.4 on 2024-07-23 15:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0019_savedguilds_default_embed_colour'), - ] - - operations = [ - migrations.CreateModel( - name='GuildSettings', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('guild_id', models.CharField(help_text='Discord snowflake ID for the represented guild.', max_length=128, verbose_name='guild id')), - ('default_embed_colour', models.CharField(blank=True, default='3498db', max_length=6, verbose_name='default embed colour')), - ], - ), - ] diff --git a/apps/home/migrations/0021_alter_guildsettings_options_and_more.py b/apps/home/migrations/0021_alter_guildsettings_options_and_more.py deleted file mode 100644 index 1ff9025..0000000 --- a/apps/home/migrations/0021_alter_guildsettings_options_and_more.py +++ /dev/null @@ -1,122 +0,0 @@ -# Generated by Django 5.0.4 on 2024-08-14 20:41 - -import django.utils.timezone -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0020_guildsettings'), - ] - - operations = [ - migrations.AlterModelOptions( - name='guildsettings', - options={'get_latest_by': 'id', 'verbose_name': 'guild settings', 'verbose_name_plural': 'guild settings'}, - ), - migrations.RemoveField( - model_name='savedguilds', - name='default_embed_colour', - ), - migrations.AddField( - model_name='guildsettings', - name='active', - field=models.BooleanField(default=True, help_text='Subscriptions of inactive guilds will also be treated as inactive', verbose_name='Active'), - ), - migrations.AlterField( - model_name='guildsettings', - name='guild_id', - field=models.CharField(help_text='Discord snowflake ID for the represented guild.', max_length=128, unique=True, verbose_name='guild id'), - ), - migrations.AlterField( - model_name='savedguilds', - name='id', - field=models.AutoField(primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='subscription', - name='active', - field=models.BooleanField(default=True, verbose_name='Active'), - ), - migrations.AlterField( - model_name='subscription', - name='article_fetch_image', - field=models.BooleanField(default=True, help_text='Will the resulting article have an image?', verbose_name='Fetch Article Images'), - ), - migrations.AlterField( - model_name='subscription', - name='creation_datetime', - field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Created At'), - ), - migrations.AlterField( - model_name='subscription', - name='embed_colour', - field=models.CharField(blank=True, default='3498db', max_length=6, verbose_name='Embed Colour'), - ), - migrations.AlterField( - model_name='subscription', - name='extra_notes', - field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Extra Notes'), - ), - migrations.AlterField( - model_name='subscription', - name='guild_id', - field=models.CharField(max_length=128, verbose_name='Guild ID'), - ), - migrations.AlterField( - model_name='subscription', - name='name', - field=models.CharField(max_length=32, verbose_name='Name'), - ), - migrations.AlterField( - model_name='subscription', - name='published_threshold', - field=models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='Published Threshold'), - ), - migrations.AlterField( - model_name='subscription', - name='url', - field=models.URLField(verbose_name='URL'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='blocked', - field=models.BooleanField(default=False, verbose_name='Blocked'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='channel_id', - field=models.CharField(max_length=128, verbose_name='Channel ID'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='creation_datetime', - field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Created At'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='guid', - field=models.CharField(help_text='RSS provided GUID of the content', max_length=256, verbose_name='GUID'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='id', - field=models.AutoField(primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='message_id', - field=models.CharField(max_length=128, verbose_name='Message ID'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='title', - field=models.CharField(max_length=728, verbose_name='Title'), - ), - migrations.AlterField( - model_name='trackedcontent', - name='url', - field=models.URLField(verbose_name='URL'), - ), - ] diff --git a/apps/home/migrations/0022_populate_guild_settings.py b/apps/home/migrations/0022_populate_guild_settings.py deleted file mode 100644 index e00e7e4..0000000 --- a/apps/home/migrations/0022_populate_guild_settings.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.0.4 on 2024-08-14 20:46 - -from django.db import migrations - - -def create_missing_guild_settings(apps, scheme_editor): - SavedGuilds = apps.get_model("home", "SavedGuilds") - GuildSettings = apps.get_model("home", "GuildSettings") - - for saved_guild in SavedGuilds.objects.all(): - GuildSettings.objects.get_or_create(guild_id=saved_guild.guild_id) - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0021_alter_guildsettings_options_and_more'), - ] - - operations = [ - migrations.RunPython(create_missing_guild_settings), - ] diff --git a/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py b/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py deleted file mode 100644 index f7737aa..0000000 --- a/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-13 23:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0022_populate_guild_settings'), - ] - - operations = [ - migrations.CreateModel( - name='UniqueContentRule', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('value', models.CharField(max_length=32)), - ], - ), - migrations.AddField( - model_name='subscription', - name='unique_content_rules', - field=models.ManyToManyField(to='home.uniquecontentrule'), - ), - ] diff --git a/apps/home/migrations/0024_initial_uniquecontentrule_data.py b/apps/home/migrations/0024_initial_uniquecontentrule_data.py deleted file mode 100644 index 28a5532..0000000 --- a/apps/home/migrations/0024_initial_uniquecontentrule_data.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-13 23:32 - -from django.db import migrations - -def add_rules(apps, schema_editor): - Rules = apps.get_model("home", "UniqueContentRule") - Rules.objects.create(name="GUID", value="GUID") - Rules.objects.create(name="ID", value="ID") - Rules.objects.create(name="URL", value="URL") - Rules.objects.create(name="Title", value="TITLE") - Rules.objects.create(name="Content Hash", value="CONTENT") - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0023_uniquecontentrule_subscription_unique_content_rules'), - ] - - operations = [ - migrations.RunPython(add_rules) - ] diff --git a/apps/home/migrations/0025_set_uniquerules.py b/apps/home/migrations/0025_set_uniquerules.py deleted file mode 100644 index 1b2402d..0000000 --- a/apps/home/migrations/0025_set_uniquerules.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-15 13:40 - -from django.db import migrations - -def set_rules_to_ruleless_subs(apps, scheme_editor): - Subs = apps.get_model("home", "Subscription") - Rules = apps.get_model("home", "UniqueContentRule") - no_rule_subs = Subs.objects.filter(unique_content_rules=None) - - guid_rule = Rules.objects.get(value="GUID") - id_rule = Rules.objects.get(value="ID") - - for sub in no_rule_subs: - sub.unique_content_rules.set([guid_rule.pk, id_rule.pk]) - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0024_initial_uniquecontentrule_data'), - ] - - operations = [ - migrations.RunPython(set_rules_to_ruleless_subs) - ] diff --git a/apps/home/migrations/0026_temp_testonly_migration.py b/apps/home/migrations/0026_temp_testonly_migration.py deleted file mode 100644 index 68cef51..0000000 --- a/apps/home/migrations/0026_temp_testonly_migration.py +++ /dev/null @@ -1,104 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-18 20:26 - -import django.db.models.deletion -import django.utils.timezone -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0025_set_uniquerules'), - ] - - operations = [ - migrations.CreateModel( - name='r_MessageMutator', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('value', models.CharField(max_length=32)), - ], - ), - migrations.CreateModel( - name='r_Server', - fields=[ - ('id', models.PositiveIntegerField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=128)), - ('icon_hash', models.CharField(max_length=128)), - ('active', models.BooleanField(default=True)), - ], - ), - migrations.CreateModel( - name='r_UniqueContentRule', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('value', models.CharField(max_length=32)), - ], - ), - migrations.CreateModel( - name='r_MessageStyle', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('is_embed', models.BooleanField(default=True)), - ('is_hyperlinked', models.BooleanField()), - ('show_author', models.BooleanField()), - ('show_timestamp', models.BooleanField()), - ('show_images', models.BooleanField()), - ('fetch_images', models.BooleanField()), - ('description_mutator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='desc_mutated_messagestyle', to='home.r_messagemutator')), - ('title_mutator', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='title_mutated_messagestyle', to='home.r_messagemutator')), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_server')), - ], - ), - migrations.CreateModel( - name='r_ContentFilter', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=32)), - ('match', models.CharField(max_length=256)), - ('matching_algorithm', models.PositiveIntegerField(choices=[(0, 'None'), (1, 'Any: Item contains any of these words (space separated)'), (2, 'All: Item contains all of these words (space separated)'), (3, 'Exact: Item contains this string'), (4, 'Regular expression: Item matches this regex'), (5, 'Fuzzy: Item contains a word similar to this word')])), - ('is_insensitive', models.BooleanField()), - ('is_whitelist', models.BooleanField()), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_server')), - ], - ), - migrations.CreateModel( - name='BotLogicLogs', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('level', models.CharField(max_length=32)), - ('message', models.CharField(max_length=256)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False)), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_server')), - ], - ), - migrations.CreateModel( - name='r_Subscription', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=32)), - ('url', models.URLField()), - ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False)), - ('updated_at', models.DateTimeField(default=django.utils.timezone.now)), - ('extra_notes', models.CharField(blank=True, default='', max_length=250)), - ('active', models.BooleanField(default=True)), - ('filters', models.ManyToManyField(blank=True, to='home.r_contentfilter')), - ('message_style', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='home.r_messagestyle')), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_server')), - ], - ), - migrations.CreateModel( - name='r_Content', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('item_id', models.CharField(max_length=1024)), - ('item_guid', models.CharField(max_length=1024)), - ('item_url', models.CharField(max_length=1024)), - ('item_title', models.CharField(max_length=1024)), - ('item_content_hash', models.CharField(max_length=1024)), - ('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.r_subscription')), - ], - ), - ] diff --git a/apps/home/migrations/0027_alter_r_server_icon_hash.py b/apps/home/migrations/0027_alter_r_server_icon_hash.py deleted file mode 100644 index bb0e41e..0000000 --- a/apps/home/migrations/0027_alter_r_server_icon_hash.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.4 on 2024-09-23 20:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0026_temp_testonly_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='r_server', - name='icon_hash', - field=models.CharField(blank=True, max_length=128, null=True), - ), - ] diff --git a/apps/home/models.py b/apps/home/models.py index c6f8a49..542ec88 100644 --- a/apps/home/models.py +++ b/apps/home/models.py @@ -9,421 +9,25 @@ from django.core.exceptions import ValidationError log = logging.getLogger(__name__) -#region Guild Settings +# region Server -class GuildSettings(models.Model): +class Server(models.Model): """ - Represents settings for a saved Discord Guild `SavedGuild`. - These objects aren't linked through foreignkey because - `SavedGuild` is user user unique, not Discord Guild unique. + Represents a Discord Server. + Instances of this model are automatically handled, and manual intervension + should be avoided if possible. """ - id = models.AutoField(primary_key=True) - - guild_id = models.CharField( - verbose_name=_("guild id"), - max_length=128, - help_text=_("Discord snowflake ID for the represented guild."), - unique=True - ) - - default_embed_colour = models.CharField( - verbose_name=_("default embed colour"), - max_length=6, - default="3498db", - blank=True - ) - - active = models.BooleanField( - verbose_name=_("Active"), - default=True, - help_text=_("Subscriptions of inactive guilds will also be treated as inactive") - ) - - class Meta: - """ - Metadata for the GuildSettings model. - """ - - verbose_name = "guild settings" - verbose_name_plural = "guild settings" - get_latest_by = "id" - - -#region Saved Guilds - -class SavedGuilds(models.Model): - """ - Represents a saved Discord Guild (aka Server). - These are shown in the UI on the sidebar, and can be selected - to see associated Subscriptions. - """ - - id = models.AutoField(_("ID"), primary_key=True) - - # Have to use charfield instead of positiveBigIntegerField due to an Sqlite - # issue that rounds down the value - # https://github.com/sequelize/sequelize/issues/9335 - guild_id = models.CharField( - verbose_name=_("guild id"), - max_length=128, - help_text=_("Discord snowflake ID for the represented guild.") - ) - - name = models.CharField( - max_length=128, - help_text=_("Name of the represented guild.") - ) - - icon = models.CharField( - max_length=128, - help_text=_("Hash for the represented guild's icon.") - ) - - added_by = models.ForeignKey( - verbose_name=_("added by"), - to="authentication.DiscordUser", - on_delete=models.CASCADE, - help_text=_("The user who added created this instance.") - ) - - permissions = models.CharField( - max_length=64, - help_text=_("Guild permissions for the user who added this instance.") - ) - - owner = models.BooleanField( - default=False, - help_text=_("Does the 'added by' user own this guild?") - ) - - class Meta: - """ - Metadata for the SavedGuilds Model. - """ - - verbose_name = "saved guild" - verbose_name_plural = "saved guilds" - get_latest_by = "-creation_datetime" - - constraints = [ - # Prevent servers from having subscriptions with duplicate names - models.UniqueConstraint( - fields=["added_by", "guild_id"], - name="unique added_by & guild_id pair" - ) - ] - - def __str__(self) -> str: - return self.name - - @property - def settings(self): - return GuildSettings.objects.get(guild_id=self.guild_id) - - def save(self, *args, **kwargs): - GuildSettings.objects.get_or_create(guild_id=self.guild_id) - super().save(*args, **kwargs) - - -#region Sub Channels - -class SubChannel(models.Model): - """ - Represents a Discord TextChannel, saved against a Subscription. - SubChannels are used as targets to send content from Subscriptions. - """ - - id = models.AutoField(primary_key=True) - - # Have to use charfield instead of positiveBigIntegerField due to an Sqlite - # issue that rounds down the value - # https://github.com/sequelize/sequelize/issues/9335 - channel_id = models.CharField( - verbose_name=_("channel id"), - max_length=128, - help_text=_("Discord snowflake ID for the represented Channel.") - ) - - channel_name = models.CharField( - verbose_name=_("channel name"), - max_length=256, - help_text=_("Name of the represented Channel.") - ) - - subscription = models.ForeignKey( - to="home.Subscription", - on_delete=models.CASCADE, - help_text=_("The linked Subscription, must be unique.") - ) - - class Meta: - """ - Metadata for the SubChannel Model. - """ - - verbose_name = "SubChannel" - verbose_name_plural = "SubChannels" - get_latest_by = "id" - constraints = [ - # Prevent servers from having subscriptions with duplicate names - models.UniqueConstraint( - fields=["channel_id", "subscription"], - name="unique channel id and subscription pair") - ] - - def __str__(self) -> str: - return self.channel_id - - -#region Filters - -# using a brilliant matching model design from paperless-ngx src -class Filter(models.Model): - MATCH_NONE = 0 - MATCH_ANY = 1 - MATCH_ALL = 2 - MATCH_LITERAL = 3 - MATCH_REGEX = 4 - MATCH_FUZZY = 5 - MATCH_AUTO = 6 - - MATCHING_ALGORITHMS = ( - (MATCH_NONE, _("None")), - (MATCH_ANY, _("Any: Item contains any of these words (space separated)")), - (MATCH_ALL, _("All: Item contains all of these words (space separated)")), - (MATCH_LITERAL, _("Exact: Item contains this string")), - (MATCH_REGEX, _("Regular expression: Item matches this regex")), - (MATCH_FUZZY, _("Fuzzy: Item contains a word similar to this word")), - # (MATCH_AUTO, _("Automatic")), - ) - - id = models.AutoField(primary_key=True) - - name = models.CharField(_("name"), max_length=128) - - match = models.CharField(_("match"), max_length=256, blank=True) - - matching_algorithm = models.PositiveIntegerField( - _("matching algorithm"), - choices=MATCHING_ALGORITHMS, - default=MATCH_ANY, - ) - - is_insensitive = models.BooleanField(_("is insensitive"), default=True) - - is_whitelist = models.BooleanField(_("is whitelist"), default=False) - - # Have to use charfield instead of positiveBigIntegerField due to an Sqlite - # issue that rounds down the value - # https://github.com/sequelize/sequelize/issues/9335 - guild_id = models.CharField(_("guild id"), max_length=128) - - class Meta: - ordering = ("name",) - constraints = [ - models.UniqueConstraint( - fields=["name", "guild_id"], - name="unique name & guild id pair" - ) - ] - - def __str__(self): - return f"{self.guild_id} - {self.name}" - - -#region Subscription - -class Subscription(models.Model): - """ - The Subscription Model. - 'Subscription' in the context of PYRSS is an RSS Feed with various settings. - """ - - id = models.AutoField(primary_key=True) - - name = models.CharField( - _("Name"), - max_length=32, - null=False, - blank=False - ) - - url = models.URLField(_("URL")) - - # NOTE: - # Have to use charfield instead of positiveBigIntegerField due to an Sqlite - # issue that rounds down the value - # https://github.com/sequelize/sequelize/issues/9335 - guild_id = models.CharField( - _("Guild ID"), - max_length=128 - ) - - creation_datetime = models.DateTimeField( - _("Created At"), - default=timezone.now, - editable=False - ) - - extra_notes = models.CharField( - _("Extra Notes"), - max_length=250, - null=True, - blank=True, - ) - - filters = models.ManyToManyField(to="home.Filter", blank=True) - - unique_content_rules = models.ManyToManyField( - to="home.UniqueContentRule", - blank=False - ); - - article_title_mutators = models.ManyToManyField( - to="home.ArticleMutator", - related_name="title_mutated_subscriptions", - blank=True, - ) - - article_desc_mutators = models.ManyToManyField( - to="home.ArticleMutator", - related_name="desc_mutated_subscriptions", - blank=True, - ) - - embed_colour = models.CharField( - _("Embed Colour"), - max_length=6, - default="3498db", - blank=True - ) - - published_threshold = models.DateTimeField(_("Published Threshold"), default=timezone.now, blank=True) - - article_fetch_image = models.BooleanField( - _("Fetch Article Images"), - default=True, - help_text="Will the resulting article have an image?" - ) - - active = models.BooleanField(_("Active"), default=True) - - class Meta: - """ - Metadata for the Subscription Model. - """ - - verbose_name = "subscription" - verbose_name_plural = "subscriptions" - get_latest_by = "-creation_datetime" - constraints = [ - # Prevent servers from having subscriptions with duplicate names - models.UniqueConstraint(fields=["name", "guild_id"], name="unique name & server pair") - ] - - @property - def channels_count(self) -> int: - """ - Returns the number of 'SubChannel' objects assocaited - with this subscription. - """ - - return len(SubChannel.objects.filter(subscription=self)) - - def save(self, *args, **kwargs): - new_text = "New " if self._state.adding else "" - log.debug("%sSubscription Saved %s", new_text, self.id) - super().save(*args, **kwargs) - - def __str__(self) -> str: - return self.name - - -#region Tracked Content - -class TrackedContent(models.Model): - """ - Tracked Content Model - 'Tracked Content' identifies articles and tracks them being sent. - This is used to ensure duplicate articles aren't sent in feeds. - """ - - id = models.AutoField(_("ID"), primary_key=True) - - guid = models.CharField( - _("GUID"), - max_length=256, - help_text=_("RSS provided GUID of the content") - ) - - title = models.CharField(_("Title"), max_length=728) - - url = models.URLField(_("URL")) - - subscription = models.ForeignKey(to=Subscription, on_delete=models.CASCADE) - - channel_id = models.CharField(_("Channel ID"), max_length=128) - - message_id = models.CharField(_("Message ID"), max_length=128) - - blocked = models.BooleanField(_("Blocked"), default=False) - - creation_datetime = models.DateTimeField( - _("Created At"), - default=timezone.now, - editable=False - ) - - class Meta: - - verbose_name = "tracked content" - verbose_name = "tracked contents" - get_latest_by = "-creation_datetime" - constraints = [ - models.UniqueConstraint(fields=["guid", "channel_id"], name="unique guid & channel_id pair"), - models.UniqueConstraint(fields=["url", "channel_id"], name="unique url & channel_id pair") - ] - - def __str__(self) -> str: - return self.title - - -#region Article Mutator - -class ArticleMutator(models.Model): - - id = models.AutoField(primary_key=True) - name = models.CharField(max_length=64) - value = models.CharField(max_length=32) - - def __str__(self) -> str: - return self.name - - -#region UniqueContentRules - -class UniqueContentRule(models.Model): - - id = models.AutoField(primary_key=True) - name = models.CharField(max_length=64) - value = models.CharField(max_length=32) - - def __str__(self) -> str: - return self.name - - - - -#region Rewrite - - - - - - - - - - -class r_Server(models.Model): id = models.PositiveIntegerField(primary_key=True) name = models.CharField(max_length=128) icon_hash = models.CharField(max_length=128, blank=True, null=True) active = models.BooleanField(default=True) + class Meta: + verbose_name = "server" + verbose_name_plural = "servers" + get_latest_by = "name" + @property def icon_url(self): return f"https://cdn.discordapp.com/icons/{self.id}/{self.icon_hash}.webp?size=80" @@ -432,9 +36,16 @@ class r_Server(models.Model): return self.name -class r_ContentFilter(models.Model): +# region Content Filter + +class ContentFilter(models.Model): + """ + Filters for the content produced by Subscriptions. + Owned by the related server. + """ + id = models.AutoField(primary_key=True) - server = models.ForeignKey(to=r_Server, on_delete=models.CASCADE) + server = models.ForeignKey(to=Server, on_delete=models.CASCADE) MATCH_NONE = 0 MATCH_ANY = 1 @@ -459,23 +70,47 @@ class r_ContentFilter(models.Model): is_insensitive = models.BooleanField() is_whitelist = models.BooleanField() + class Meta: + verbose_name = "filter" + verbose_name_plural = "filters" + get_latest_by = "id" + def __str__(self): return self.name -# Instances of this model are predefined only -class r_MessageMutator(models.Model): +# region Message Mutator + +class MessageMutator(models.Model): + """ + Mutators to be applied via the Bot. + Instances of this model are predefined via migrations. + Manual editing should be avoided at all costs! + """ + id = models.AutoField(primary_key=True) name = models.CharField(max_length=64) value = models.CharField(max_length=32) + class Meta: + verbose_name = "message mutator" + verbose_name_plural = "message mutators" + get_latest_by = "id" + def __str__(self): return self.name -class r_MessageStyle(models.Model): +# region Message Style + +class MessageStyle(models.Model): + """ + Custom styles to be applied via the Bot. + Owned by the related server. + """ + id = models.AutoField(primary_key=True) - server = models.ForeignKey(to=r_Server, on_delete=models.CASCADE) + server = models.ForeignKey(to=Server, on_delete=models.CASCADE) is_embed = models.BooleanField(default=True) is_hyperlinked = models.BooleanField() # title only @@ -485,27 +120,61 @@ class r_MessageStyle(models.Model): fetch_images = models.BooleanField() # if not included with RSS item title_mutator = models.ForeignKey( - to=r_MessageMutator, + to=MessageMutator, related_name="title_mutated_messagestyle", on_delete=models.SET_NULL, null=True, blank=True ) description_mutator = models.ForeignKey( - to=r_MessageMutator, + to=MessageMutator, related_name="desc_mutated_messagestyle", on_delete=models.SET_NULL, null=True, blank=True ) + class Meta: + verbose_name = "message style" + verbose_name_plural = "message styles" + get_latest_by = "id" + def __str__(self): return f"{self.server.name} - {self.id}" -class r_Subscription(models.Model): +# region Unique Content Rule + +class UniqueContentRule(models.Model): + """ + Definitions for what content should be unique + Instances of this model are predefined via migrations. + Manual editing should be avoided at all costs! + """ + id = models.AutoField(primary_key=True) - server = models.ForeignKey(to=r_Server, on_delete=models.CASCADE, blank=False) + name = models.CharField(max_length=64) + value = models.CharField(max_length=32) + + class Meta: + verbose_name = "unique content rule" + verbose_name_plural = "unique content rules" + get_latest_by = "id" + + def __str__(self): + return self.name + + +# region Subscription + +class Subscription(models.Model): + """ + These represent RSSFeeds, storing relevant settings for managing them. + Owned by the related server. + """ + + id = models.AutoField(primary_key=True) + server = models.ForeignKey(to=Server, on_delete=models.CASCADE, blank=False) name = models.CharField(max_length=32, blank=False) url = models.URLField() @@ -514,16 +183,28 @@ class r_Subscription(models.Model): extra_notes = models.CharField(max_length=250, default="", blank=True) active = models.BooleanField(default=True) - filters = models.ManyToManyField(to=r_ContentFilter, blank=True) - message_style = models.ForeignKey(to=r_MessageStyle, on_delete=models.SET_NULL, null=True, blank=True) + filters = models.ManyToManyField(to=ContentFilter, blank=True) + message_style = models.ForeignKey(to=MessageStyle, on_delete=models.SET_NULL, null=True, blank=True) + unique_rules = models.ManyToManyField(to=UniqueContentRule, blank=False) + + class Meta: + verbose_name = "subscription" + verbose_name_plural = "subscriptions" + get_latest_by = "updated_at" def __str__(self): return self.name -class r_Content(models.Model): +# region Content + +class Content(models.Model): + """ + Represents a processed item created from a Subscription. + """ + id = models.AutoField(primary_key=True) - subscription = models.ForeignKey(to=r_Subscription, on_delete=models.CASCADE) + subscription = models.ForeignKey(to=Subscription, on_delete=models.CASCADE) # 'item_' prefix is to differentiate between the internal identifiers and the stored data item_id = models.CharField(max_length=1024) @@ -532,28 +213,30 @@ class r_Content(models.Model): item_title = models.CharField(max_length=1024) item_content_hash = models.CharField(max_length=1024) + class Meta: + verbose_name = "content" + verbose_name_plural = "content" + get_latest_by = "id" + def __str__(self): return f"{self.subscription.name} - {self.id}" -# Instances of this model are predefined only -class r_UniqueContentRule(models.Model): - id = models.AutoField(primary_key=True) - name = models.CharField(max_length=64) - value = models.CharField(max_length=32) - - def __str__(self): - return self.name - +# region Bot Logic Logs # Relevant logs from the bot logic class BotLogicLogs(models.Model): id = models.AutoField(primary_key=True) - server = models.ForeignKey(to=r_Server, on_delete=models.CASCADE) + server = models.ForeignKey(to=Server, on_delete=models.CASCADE) level = models.CharField(max_length=32) message = models.CharField(max_length=256) created_at = models.DateTimeField(default=timezone.now, editable=False) + class Meta: + verbose_name = "bot logic log" + verbose_name_plural = "bot logic logs" + get_latest_by = "id" + def __str__(self): return f"{self.server.name} - {self.id}" diff --git a/apps/home/views.py b/apps/home/views.py index 8763371..babbec7 100644 --- a/apps/home/views.py +++ b/apps/home/views.py @@ -9,7 +9,7 @@ from django.shortcuts import redirect from django.http import JsonResponse, HttpResponse from django.views.generic import TemplateView, View -from apps.home.models import r_Server +from apps.home.models import Server from apps.authentication.models import DiscordUser, ServerMember @@ -68,7 +68,7 @@ class GuildsView(View): await self.delete_member(user, data["id"]) return - server = await r_Server.objects.aget_or_create( + server = await Server.objects.aget_or_create( id=data["id"], name=data["name"], icon_hash=data["icon"] diff --git a/apps/static/js/api.js b/apps/static/js/api.js index b94656d..686a7b3 100644 --- a/apps/static/js/api.js +++ b/apps/static/js/api.js @@ -32,30 +32,6 @@ function makeQuerystring(filters, sort) { return sort ? querystring += `ordering=${sort}` : querystring; } -// Saved Guilds - -async function getSavedGuilds(userId) { - return await ajaxRequest(`/api/saved-guilds/?added_by=${userId}`, "GET"); -} - -async function getSavedGuild(id) { - return await ajaxRequest(`/api/saved-guilds/${id}/`, "GET"); -} - -async function newSavedGuild(formData) { - return await ajaxRequest("/api/saved-guilds/", "POST", formData); -} - -async function deleteSavedGuild(id) { - return await ajaxRequest(`/api/saved-guilds/${id}/`, "DELETE"); -} - -// Loading Guilds - -async function loadGuilds() { - return await ajaxRequest("/guilds/", "GET"); -} - // Loading Channels async function loadChannels(guildId) { return await ajaxRequest(`/channels?guild=${guildId}`, "GET"); @@ -63,30 +39,30 @@ async function loadChannels(guildId) { // Subscriptions -async function getSubscriptions(filters, sort) { - let querystring = makeQuerystring(filters, sort); - return await ajaxRequest(`/api/subscription/${querystring}`, "GET"); -} +// async function getSubscriptions(filters, sort) { +// let querystring = makeQuerystring(filters, sort); +// return await ajaxRequest(`/api/subscription/${querystring}`, "GET"); +// } -async function getSubscription(id) { - return await ajaxRequest(`/api/subscription/${id}/`, "GET"); -} +// async function getSubscription(id) { +// return await ajaxRequest(`/api/subscription/${id}/`, "GET"); +// } -async function newSubscription(formData) { - return await ajaxRequest("/api/subscription/", "POST", formData); -} +// async function newSubscription(formData) { +// return await ajaxRequest("/api/subscription/", "POST", formData); +// } -async function deleteSubscription(id) { - return await ajaxRequest(`/api/subscription/${id}/`, "DELETE"); -} +// async function deleteSubscription(id) { +// return await ajaxRequest(`/api/subscription/${id}/`, "DELETE"); +// } -async function editSubscription(id, formData) { - return await ajaxRequest(`/api/subscription/${id}/`, "PUT", formData); -} +// async function editSubscription(id, formData) { +// return await ajaxRequest(`/api/subscription/${id}/`, "PUT", formData); +// } -async function getSubscriptionOptions() { - return await ajaxRequest("/api/subscription/", "OPTIONS") -} +// async function getSubscriptionOptions() { +// return await ajaxRequest("/api/subscription/", "OPTIONS") +// } // SubChannels @@ -186,10 +162,47 @@ async function getUniqueContentRule(ruleId) { //#region rewrite + + + + +// #region Servers + async function generateServers() { return ajaxRequest("/generate-servers/", "GET"); } async function getServers() { return ajaxRequest("/api/r_servers/", "GET"); -} \ No newline at end of file +} + +// #endregion + +// #region Subscriptions + +async function getSubscriptions(filters, sort) { + let querystring = makeQuerystring(filters, sort); + return await ajaxRequest(`/api/r_subscriptions/${querystring}`, "GET"); +} + +async function getSubscription(id) { + return await ajaxRequest(`/api/r_subscriptions/${id}/`, "GET"); +} + +async function newSubscription(formData) { + return await ajaxRequest("/api/r_subscriptions/", "POST", formData); +} + +async function deleteSubscription(id) { + return await ajaxRequest(`/api/r_subscriptions/${id}/`, "DELETE"); +} + +async function editSubscription(id, formData) { + return await ajaxRequest(`/api/r_subscriptions/${id}/`, "PUT", formData); +} + +async function getSubscriptionOptions() { + return await ajaxRequest("/api/r_subscriptions/", "OPTIONS") +} + +// #endregion diff --git a/apps/static/js/home/index.js b/apps/static/js/home/index.js index 30d2b9d..77d84e0 100644 --- a/apps/static/js/home/index.js +++ b/apps/static/js/home/index.js @@ -1,11 +1,11 @@ $(document).ready(async function() { + await loadServers(); + await initSubscriptionTable(); await initFiltersTable(); await initContentTable(); $("#subscriptionsTab").click(); - - await loadServers(); }); $(document).on("selectedServerChange", function() { diff --git a/apps/static/js/home/servers.js b/apps/static/js/home/servers.js index c385df2..c28cb25 100644 --- a/apps/static/js/home/servers.js +++ b/apps/static/js/home/servers.js @@ -97,7 +97,6 @@ function selectServer(id) { selectServer = null; return; } - debugger // Change appearance of selected vs none-selected items $("#serverList .server-item").removeClass("active");