sync servers & members with Discord

This commit is contained in:
Corban-Lee Jones 2024-09-23 22:13:36 +01:00
parent 515a165cdb
commit 181930d687
10 changed files with 156 additions and 7 deletions

View File

@ -2,7 +2,7 @@
from django.contrib import admin
from .models import DiscordUser
from .models import DiscordUser, ServerMember
@admin.register(DiscordUser)
@ -10,3 +10,8 @@ class DiscordUserAdmin(admin.ModelAdmin):
list_display = ["id", "username", "global_name", "last_login", "is_staff", "is_superuser", "is_staff"]
list_filter = ["is_staff", "is_superuser", "is_active"]
@admin.register(ServerMember)
class ServerMemberAdmin(admin.ModelAdmin):
pass

View File

@ -0,0 +1,18 @@
# 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),
),
]

View File

@ -0,0 +1,22 @@
# 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),
),
]

View File

@ -169,11 +169,11 @@ 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)
nick = models.CharField(max_length=32, null=True, blank=True)
permissions = models.CharField(max_length=32)
is_owner = models.BooleanField(default=False)
def __str__(self):
return f"{self.server.name} · {self.user}({self.nick})"
return f"{self.server.name} · {self.user}"
def has_permission(self, flag: int) -> bool:
"""Check that the member has a givern permission.

View File

@ -49,7 +49,8 @@ class DiscordLoginRedirect(View):
discord_user = authenticate(request, discord_user_data=raw_user_data)
login(request, discord_user)
return redirect("home:index")
redirect_url = request.GET.get("redirect") or "home:index"
return redirect(redirect_url)
def exchange_code(self, code: str) -> dict:
"""

View File

@ -0,0 +1,18 @@
# 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),
),
]

View File

@ -421,8 +421,7 @@ class UniqueContentRule(models.Model):
class r_Server(models.Model):
id = models.PositiveIntegerField(primary_key=True)
name = models.CharField(max_length=128)
icon_hash = models.CharField(max_length=128)
owner_id = models.PositiveBigIntegerField()
icon_hash = models.CharField(max_length=128, blank=True, null=True)
active = models.BooleanField(default=True)
@property

View File

@ -3,8 +3,10 @@
from django.urls import path
from django.contrib.auth.decorators import login_required
from .views import IndexView
from .views import IndexView, guilds
urlpatterns = [
path("", login_required(IndexView.as_view()), name="index"),
path("user-guilds", guilds, name="user-guilds")
]

View File

@ -1,7 +1,17 @@
# -*- encoding: utf-8 -*-
import httpx
from django.conf import settings
from django.utils import timezone
from asgiref.sync import sync_to_async
from django.shortcuts import redirect
from django.http import JsonResponse, HttpResponse
from django.views.generic import TemplateView
from apps.home.models import r_Server
from apps.authentication.models import DiscordUser, ServerMember
class IndexView(TemplateView):
"""
@ -9,3 +19,73 @@ class IndexView(TemplateView):
"""
template_name = "home/index.html"
@sync_to_async
def get_user_access_token(user: DiscordUser) -> str:
return user.access_token
@sync_to_async
def is_user_authenticated(user: DiscordUser) -> bool:
return user.is_authenticated
async def delete_server(server_id: int):
# member will be auto deleted via CASCADE, so no need to handle that
try:
server = await r_Server.objects.aget(id=server_id)
await server.adelete()
except r_Server.DoesNotExist:
pass
async def setup_server(user: DiscordUser, data: dict) -> dict | None:
is_owner = data["owner"]
permissions = data["permissions"]
admin_perm = 1 << 3
# Ignore servers where the user isn't an administrator or owner
if not ((int(permissions) & admin_perm) == admin_perm or is_owner):
await delete_server(data["id"])
return
server = await r_Server.objects.aget_or_create(
id=data["id"],
name=data["name"],
icon_hash=data["icon"]
)
await ServerMember.objects.aupdate_or_create(
user=user,
server=server[0],
defaults={
"permissions": permissions,
"is_owner": is_owner
}
)
return data
async def guilds(request):
if not await is_user_authenticated(request.user):
return redirect("/oauth2/login")
access_token = await get_user_access_token(request.user)
async with httpx.AsyncClient() as client:
response = await client.get(
url=f"{settings.DISCORD_API_URL}/users/@me/guilds",
headers={"Authorization": f"Bearer {access_token}"}
)
guild_data = response.json()
# guilds where the user either administrates or owns
cleaned_guild_data = []
for item in guild_data:
cleaned_data = await setup_server(request.user, item)
if cleaned_data:
cleaned_guild_data.append(cleaned_data)
return JsonResponse(guild_data, safe=False)

View File

@ -1,3 +1,4 @@
anyio==4.6.0
asgiref==3.8.1
bump2version==1.0.1
certifi==2024.2.2
@ -7,6 +8,9 @@ django-environ==0.11.2
django-filter==24.2
djangorestframework==3.15.1
gunicorn==23.0.0
h11==0.14.0
httpcore==1.0.5
httpx==0.27.2
idna==3.7
packaging==24.1
psycopg2==2.9.9