server setup via api
This commit is contained in:
parent
345f70d30f
commit
ecfd4a37f3
3
apps/api/errors.py
Normal file
3
apps/api/errors.py
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
class NotAMemberError(Exception):
|
||||
pass
|
@ -264,8 +264,11 @@ class TrackedContentSerializer_POST(DynamicModelSerializer):
|
||||
|
||||
# rewrite
|
||||
|
||||
class DiscordServerIdSerializer(serializers.Serializer):
|
||||
server_id = serializers.IntegerField()
|
||||
|
||||
class r_ServerSerialiszer(DynamicModelSerializer):
|
||||
|
||||
class r_ServerSerializer(DynamicModelSerializer):
|
||||
class Meta:
|
||||
model = r_Server
|
||||
fields = ("id", "name", "icon_hash", "active")
|
||||
@ -309,6 +312,11 @@ class r_MessageStyleSerializer(DynamicModelSerializer):
|
||||
|
||||
|
||||
class r_SubscriptionSerializer(DynamicModelSerializer):
|
||||
filters = serializers.PrimaryKeyRelatedField(
|
||||
queryset=r_ContentFilter.objects.all(),
|
||||
many=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = r_Subscription
|
||||
fields = (
|
||||
@ -324,6 +332,28 @@ class r_SubscriptionSerializer(DynamicModelSerializer):
|
||||
"message_style"
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
server = data.get("server") or self.context.get("server")
|
||||
if not server:
|
||||
return data
|
||||
|
||||
# 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)
|
||||
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."}
|
||||
)
|
||||
|
||||
# Prevent using message styles from a different server
|
||||
message_style = data.get("message_style")
|
||||
if message_style and message_style.server != server:
|
||||
raise serializers.ValidationError(
|
||||
{"message_style": "Message style must belong to the specified server."}
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class r_ContentSerializer(DynamicModelSerializer):
|
||||
class Meta:
|
||||
|
@ -23,6 +23,7 @@ from .views import (
|
||||
UniqueContentRule_DetailView,
|
||||
|
||||
#rewrite
|
||||
CreateDiscordServerView,
|
||||
r_Server_ListView,
|
||||
r_Server_DetailView,
|
||||
r_ContentFilter_ListView,
|
||||
@ -86,40 +87,43 @@ urlpatterns = [
|
||||
path("<int:pk>/", UniqueContentRule_DetailView.as_view(), name="unique-content-rule-detail")
|
||||
])),
|
||||
|
||||
|
||||
#region rewrite
|
||||
|
||||
path("discord-servers/", CreateDiscordServerView.as_view()),
|
||||
|
||||
path("r_servers/", include([
|
||||
path("", r_Server_ListView.as_view()),
|
||||
path("<int:pk>/", r_Server_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_content-filters/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ])),
|
||||
path("r_content-filters/", include([
|
||||
path("", r_ContentFilter_ListView.as_view()),
|
||||
path("<int:pk>/", r_ContentFilter_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_message-mutators/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ])),
|
||||
path("r_message-mutators/", include([
|
||||
path("", r_MessageMutator_ListView.as_view()),
|
||||
path("<int:pk>/", r_MessageMutator_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_message-styles/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ])),
|
||||
path("r_message-styles/", include([
|
||||
path("", r_MessageStyle_ListView.as_view()),
|
||||
path("<int:pk>/", r_MessageStyle_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_subscriptions/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ])),
|
||||
path("r_subscriptions/", include([
|
||||
path("", r_Subscription_ListView.as_view()),
|
||||
path("<int:pk>/", r_Subscription_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_content/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ])),
|
||||
path("r_content/", include([
|
||||
path("", r_Content_ListView.as_view()),
|
||||
path("<int:pk>/", r_Content_DetailView.as_view())
|
||||
])),
|
||||
|
||||
# path("r_unique-content-rules/", include([
|
||||
# path(""),
|
||||
# path("<int:pk>/")
|
||||
# ]))
|
||||
path("r_unique-content-rules/", include([
|
||||
path("", r_UniqueContentRule_ListView.as_view()),
|
||||
path("<int:pk>/", r_UniqueContentRule_DetailView.as_view())
|
||||
]))
|
||||
]
|
||||
|
@ -1,7 +1,9 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Subquery
|
||||
from django.db.utils import IntegrityError
|
||||
from django_filters import rest_framework as rest_filters
|
||||
@ -30,7 +32,7 @@ from apps.home.models import (
|
||||
r_Content,
|
||||
r_UniqueContentRule
|
||||
)
|
||||
from apps.authentication.models import DiscordUser
|
||||
from apps.authentication.models import DiscordUser, ServerMember
|
||||
from .metadata import ExpandedMetadata
|
||||
from .serializers import (
|
||||
SubChannelSerializer,
|
||||
@ -45,7 +47,8 @@ from .serializers import (
|
||||
UniqueContentRuleSerializer,
|
||||
|
||||
#rewrite
|
||||
r_ServerSerialiszer,
|
||||
DiscordServerIdSerializer,
|
||||
r_ServerSerializer,
|
||||
r_ContentFilterSerializer,
|
||||
r_MessageMutatorSerializer,
|
||||
r_MessageStyleSerializer,
|
||||
@ -53,6 +56,7 @@ from .serializers import (
|
||||
r_ContentSerializer,
|
||||
r_UniqueContentRuleSerializer
|
||||
)
|
||||
from .errors import NotAMemberError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -708,18 +712,81 @@ class DeletableDetailView(generics.RetrieveDestroyAPIView):
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
|
||||
class CreateDiscordServerView(generics.CreateAPIView):
|
||||
serializer_class = DiscordServerIdSerializer
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response()
|
||||
|
||||
server_id = serializer.validated_data["server_id"]
|
||||
response = requests.get(
|
||||
url=f"{settings.DISCORD_API_URL}/guilds/{server_id}",
|
||||
headers={"Authorization": f"Bot {settings.BOT_TOKEN}"}
|
||||
)
|
||||
raw = response.json()
|
||||
if not response.status_code == 200:
|
||||
return Response(
|
||||
status=response.status_code,
|
||||
data=raw
|
||||
)
|
||||
|
||||
server = r_Server.objects.filter(id=server_id)
|
||||
|
||||
if server.exists():
|
||||
return self.create_member_for_server(server.first(), request.user)
|
||||
else:
|
||||
return self.create_server(raw, request.user)
|
||||
|
||||
|
||||
def create_member_for_server(self, server: r_Server, user: DiscordUser) -> Response:
|
||||
response = requests.get(
|
||||
url=f"{settings.DISCORD_API_URL}/users/@me/guilds/{server.id}/member", # TODO: continue here
|
||||
headers={"Authorization": f"Bearer {user.access_token}"} # the scope of the token doesnt cover membership, so
|
||||
) # this needs to be updated against the bot from the
|
||||
raw = response.json() # discord developers dashboard.
|
||||
if response.status_code != 200:
|
||||
return Response(
|
||||
status=response.status_code,
|
||||
data=raw
|
||||
)
|
||||
|
||||
# TODO: might need to a case where this member already exists
|
||||
ServerMember.objects.get_or_create(
|
||||
server=server,
|
||||
user=user,
|
||||
nick=raw.get("nick"),
|
||||
permissions=raw["permissions"]
|
||||
)
|
||||
|
||||
return Response(
|
||||
status=200,
|
||||
data={"message": "success"}
|
||||
)
|
||||
|
||||
def create_server(self, raw: dict, user: DiscordUser) -> Response:
|
||||
server = r_Server.objects.create(
|
||||
id=raw["id"],
|
||||
name=raw["name"],
|
||||
icon_hash=raw["icon"]
|
||||
)
|
||||
|
||||
return self.create_member_for_server(server, user)
|
||||
|
||||
|
||||
class r_Server_ListView(ListCreateView): # maybe change to ListView only later, and create through secure backend means?
|
||||
filterset_fields = []
|
||||
search_fields = []
|
||||
ordering_fields = []
|
||||
serializer_class = r_ServerSerialiszer
|
||||
serializer_class = r_ServerSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return r_Server.objects.all()
|
||||
|
||||
|
||||
class r_Server_DetailView(ChangableDetailView): # maybe change to ListView only later, and create through secure backend means?
|
||||
serializer_class = r_ServerSerialiszer
|
||||
serializer_class = r_ServerSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return r_Server.objects.all()
|
||||
@ -742,7 +809,7 @@ class r_ContentFilter_DetailView(ChangableDetailView):
|
||||
return r_ContentFilter.objects.all()
|
||||
|
||||
|
||||
class r_MessageMutator_ListView(): # instances of this one are pre-defined ONLY
|
||||
class r_MessageMutator_ListView(ListView): # instances of this one are pre-defined ONLY
|
||||
filterset_fields = []
|
||||
search_fields = []
|
||||
ordering_fields = []
|
||||
|
26
apps/authentication/migrations/0004_servermember.py
Normal file
26
apps/authentication/migrations/0004_servermember.py
Normal file
@ -0,0 +1,26 @@
|
||||
# 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)),
|
||||
],
|
||||
),
|
||||
]
|
@ -159,3 +159,25 @@ class DiscordUser(PermissionsMixin):
|
||||
self.refresh_token=raw["refresh_token"]
|
||||
|
||||
self.save(force_update=True)
|
||||
|
||||
|
||||
class ServerMember(models.Model):
|
||||
"""
|
||||
A link table, connecting users to servers, and storing their server-specific data.
|
||||
"""
|
||||
|
||||
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)
|
||||
nick = models.CharField(max_length=32, null=True, blank=True)
|
||||
permissions = models.CharField(max_length=32)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.server.name} · {self.user}({self.nick})"
|
||||
|
||||
def has_permission(self, flag: int) -> bool:
|
||||
"""Check that the member has a givern permission.
|
||||
https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags
|
||||
"""
|
||||
permissions_int = int(self.permissions)
|
||||
return (permissions_int & flag) == flag
|
||||
|
@ -517,20 +517,6 @@ class r_Subscription(models.Model):
|
||||
filters = models.ManyToManyField(to=r_ContentFilter, blank=True)
|
||||
message_style = models.ForeignKey(to=r_MessageStyle, on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
def clean(self):
|
||||
if self.message_style and self.message_style.server != self.server:
|
||||
raise ValidationError("Cannot use any Message Style of another server.")
|
||||
|
||||
if any(fltr.server != self.server for fltr in self.filters.all()):
|
||||
raise ValidationError("Cannot use any Content Filter of another server.")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.pk:
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user