184 lines
5.1 KiB
Python

# -*- encoding: utf-8 -*-
import logging
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin
from .managers import DiscordUserOAuth2Manager
log = logging.getLogger(__name__)
class DiscordUser(PermissionsMixin):
"""
Represents a User authenticated with Discord OAuth2.
"""
# Discord Attributes
id = models.PositiveBigIntegerField(
primary_key=True,
help_text=_("the user's id")
)
username = models.CharField(
_("username"),
max_length=32,
unique=True,
help_text=_("the user's username, not unique across the platform")
)
global_name = models.CharField(
_("nickname"),
max_length=32,
help_text=_("the user's display name, if it is set. For bots, this is the application name")
)
avatar = models.CharField(
_("avatar"),
max_length=64,
help_text=_("the user's avatar hash")
)
public_flags = models.IntegerField(
_("public flags"),
help_text=_("the public flags on a user's account")
)
flags = models.IntegerField(
_("flags"),
help_text=_("the flags on a user's account")
)
locale = models.CharField(
_("locale"),
max_length=16,
help_text=_("the user's chosen language option")
)
mfa_enabled = models.BooleanField(
_("mfa enabled"),
help_text=_("whether the user has two factor enabled on their account")
)
last_login = models.DateTimeField(
_("last login"),
default=timezone.now,
help_text=_("datetime of the previous login")
)
# Access Token
access_token = models.CharField(
_("access token"),
max_length=100,
help_text=_("token for the application to make api calls on behalf of the user.")
)
token_expires = models.DateTimeField(
_("token expires"),
help_text=_("when to request a new access token.")
)
refresh_token = models.CharField(
_("refresh token"),
max_length=100,
help_text=_("token for the application to request a new access token.")
)
# Custom Attributes
class USER_TYPES(models.TextChoices):
DISCORD_USER = "D", _("discord")
AUTOMATED_USER = "A", _("automated")
user_type = models.CharField(
_("user type"),
choices=USER_TYPES,
default=USER_TYPES.DISCORD_USER,
max_length=32,
help_text=_("What type of user is this?")
)
is_active = models.BooleanField(
_("active status"),
default=True,
help_text=_('Use as a "soft delete" rather than deleting the user.')
)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site.")
)
is_superuser = models.BooleanField(
_("superuser status"),
default=False,
help_text=_("Designates whether the user has unrestricted site control.")
)
# Object Manager
objects = DiscordUserOAuth2Manager()
REQUIRED_FIELDS = []
USERNAME_FIELD = "username"
def __str__(self):
return self.global_name or self.username
@property
def avatar_url(self):
# Default avatar
if len(self.avatar) <= 1:
return f"https://cdn.discordapp.com/embed/avatars/{self.avatar}.png"
# Custom avatar
return f"https://cdn.discordapp.com/avatars/{self.id}/{self.avatar}.webp?size=128"
@property
def password(self):
return "password"
@property
def is_authenticated(self):
return True
@property
def is_anonymous(self):
return False
def get_username(self):
return self.username
def update(self, raw: dict):
log.debug("updating user: %s", self.username)
self.username=raw["username"]
self.global_name=raw["global_name"]
self.avatar=raw["avatar"]
self.public_flags=raw["public_flags"]
self.flags=raw["flags"]
self.locale=raw["locale"]
self.mfa_enabled=raw["mfa_enabled"]
self.access_token=raw["access_token"]
self.token_expires=raw["token_expires"]
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.Server", on_delete=models.CASCADE)
permissions = models.CharField(max_length=32)
is_owner = models.BooleanField(default=False)
def __str__(self):
return f"{self.server.name} · {self.user}"
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