diff options
Diffstat (limited to '')
| -rw-r--r-- | backend/models/__init__.py | 5 | ||||
| -rw-r--r-- | backend/models/discord_role.py | 40 | ||||
| -rw-r--r-- | backend/models/discord_user.py | 34 | ||||
| -rw-r--r-- | backend/models/form.py | 13 | 
4 files changed, 87 insertions, 5 deletions
| diff --git a/backend/models/__init__.py b/backend/models/__init__.py index 8ad7f7f..a9f76e0 100644 --- a/backend/models/__init__.py +++ b/backend/models/__init__.py @@ -1,12 +1,15 @@  from .antispam import AntiSpam -from .discord_user import DiscordUser +from .discord_role import DiscordRole +from .discord_user import DiscordMember, DiscordUser  from .form import Form, FormList  from .form_response import FormResponse, ResponseList  from .question import CodeQuestion, Question  __all__ = [      "AntiSpam", +    "DiscordRole",      "DiscordUser", +    "DiscordMember",      "Form",      "FormResponse",      "CodeQuestion", diff --git a/backend/models/discord_role.py b/backend/models/discord_role.py new file mode 100644 index 0000000..c05c9de --- /dev/null +++ b/backend/models/discord_role.py @@ -0,0 +1,40 @@ +import typing + +from pydantic import BaseModel + + +class RoleTags(BaseModel): +    """Meta information about a discord role.""" + +    bot_id: typing.Optional[str] +    integration_id: typing.Optional[str] +    premium_subscriber: bool + +    def __init__(self, **data: typing.Any) -> None: +        """ +        Handle the terrible discord API. + +        Discord only returns the premium_subscriber field if it's true, +        meaning the typical validation process wouldn't work. + +        We manually parse the raw data to determine if the field exists, and give it a useful +        bool value. +        """ +        data["premium_subscriber"] = "premium_subscriber" in data.keys() +        super().__init__(**data) + + +class DiscordRole(BaseModel): +    """Schema model of Discord guild roles.""" + +    id: str +    name: str +    color: int +    hoist: bool +    icon: typing.Optional[str] +    unicode_emoji: typing.Optional[str] +    position: int +    permissions: str +    managed: bool +    mentionable: bool +    tags: typing.Optional[RoleTags] diff --git a/backend/models/discord_user.py b/backend/models/discord_user.py index 9f246ba..0eca15b 100644 --- a/backend/models/discord_user.py +++ b/backend/models/discord_user.py @@ -1,10 +1,11 @@ +import datetime  import typing as t  from pydantic import BaseModel -class DiscordUser(BaseModel): -    """Schema model of Discord user for form response.""" +class _User(BaseModel): +    """Base for discord users and members."""      # Discord default fields.      username: str @@ -20,5 +21,34 @@ class DiscordUser(BaseModel):      premium_type: t.Optional[int]      public_flags: t.Optional[int] + +class DiscordUser(_User): +    """Schema model of Discord user for form response.""" +      # Custom fields      admin: bool + + +class DiscordMember(BaseModel): +    """A discord guild member.""" + +    user: _User +    nick: t.Optional[str] +    avatar: t.Optional[str] +    roles: list[str] +    joined_at: datetime.datetime +    premium_since: t.Optional[datetime.datetime] +    deaf: bool +    mute: bool +    pending: t.Optional[bool] +    permissions: t.Optional[str] +    communication_disabled_until: t.Optional[datetime.datetime] + +    def dict(self, *args, **kwargs) -> dict[str, t.Any]: +        """Convert the model to a python dict, and encode timestamps in a serializable format.""" +        data = super().dict(*args, **kwargs) +        for field, value in data.items(): +            if isinstance(value, datetime.datetime): +                data[field] = value.isoformat() + +        return data diff --git a/backend/models/form.py b/backend/models/form.py index f19ed85..f888d6e 100644 --- a/backend/models/form.py +++ b/backend/models/form.py @@ -1,10 +1,10 @@  import typing as t  import httpx -from pydantic import constr, BaseModel, Field, root_validator, validator +from pydantic import BaseModel, Field, constr, root_validator, validator  from pydantic.error_wrappers import ErrorWrapper, ValidationError -from backend.constants import FormFeatures, WebHook +from backend.constants import DISCORD_GUILD, FormFeatures, WebHook  from .question import Question  PUBLIC_FIELDS = [ @@ -43,6 +43,8 @@ class Form(BaseModel):      submitted_text: t.Optional[str] = None      webhook: _WebHook = None      discord_role: t.Optional[str] +    response_readers: t.Optional[list[str]] +    editors: t.Optional[list[str]]      class Config:          allow_population_by_field_name = True @@ -67,6 +69,13 @@ class Form(BaseModel):          return value +    @validator("response_readers", "editors") +    def validate_role_scoping(cls, value: t.Optional[list[str]]) -> t.Optional[list[str]]: +        """Ensure special role based permissions aren't granted to the @everyone role.""" +        if value and str(DISCORD_GUILD) in value: +            raise ValueError("You can not add the everyone role as an access scope.") +        return value +      @root_validator      def validate_role(cls, values: dict[str, t.Any]) -> t.Optional[dict[str, t.Any]]:          """Validates does Discord role provided when flag provided.""" | 
