feat(issuecards): init
This commit is contained in:
parent
12b8dbdbdf
commit
d615acdcdd
18 changed files with 1709 additions and 269 deletions
140
issuecards/models/provider.py
Normal file
140
issuecards/models/provider.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from typing import Any
|
||||
|
||||
from discord import Guild
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
|
||||
from ..config import config
|
||||
|
||||
SUPPORTED_PROVIDER_SERVICES = ["github", "gitlab", "forgejo"]
|
||||
|
||||
|
||||
class UnsupportedServiceError(Exception):
|
||||
"""Custom exception for unsupported provider services."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Provider(BaseModel):
|
||||
"""Model for a provider configuration.
|
||||
|
||||
Attributes:
|
||||
id (str): The ID of the provider. This should usually be the same as the URl, just without the scheme (i.e. https://).
|
||||
url (str): The URL of the provider.
|
||||
service (str): The service type of the provider (e.g., `github`, `gitlab`, `forgejo`). Must be one of the supported services.
|
||||
token (str): An optional API token for the provider. If not provided, unauthenticated requests will be used.
|
||||
guild (Guild | None): The Discord guild associated with the provider. If `None`, the provider is considered a global provider.
|
||||
|
||||
Properties:
|
||||
api_url (str): The API URL for the provider. This is typically the base URL with an API path appended.
|
||||
favicon_url (str): The URL to the provider's favicon. This is typically the base URL with a favicon path appended.
|
||||
|
||||
Methods:
|
||||
to_config: Save the provider to Red's configuration. Saves to guilds if the `guild` class attribute is not `None`.
|
||||
|
||||
Class Methods:
|
||||
from_config: Create a Provider instance from the Red configuration. Checks both global and guild providers, prioritizing guild providers.
|
||||
fetch_all: Fetch all providers from the Red configuration. Checks both global and guild providers
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
id: str
|
||||
url: str
|
||||
service: str
|
||||
token: str | None = None
|
||||
guild: Guild | None = None
|
||||
|
||||
@field_validator("service")
|
||||
@classmethod
|
||||
def _validate_provider(cls, value: Any) -> Any:
|
||||
if value not in SUPPORTED_PROVIDER_SERVICES:
|
||||
raise UnsupportedServiceError("Provider service '%s' is not supported." % value)
|
||||
return value
|
||||
|
||||
@property
|
||||
def api_url(self) -> str:
|
||||
"""Return the API URL for the provider. This is typically the base URL with an API path appended."""
|
||||
match self.service:
|
||||
case "github":
|
||||
if self.url == "https://github.com":
|
||||
return "https://api.github.com"
|
||||
return self.url.rstrip("/") + "/api/v3"
|
||||
case "gitlab":
|
||||
return self.url.rstrip("/") + "/api/v4"
|
||||
case "forgejo":
|
||||
return self.url.rstrip("/") + "/api/v1"
|
||||
case _:
|
||||
raise UnsupportedServiceError("Unsupported provider service:' '%s'" % self.service)
|
||||
|
||||
@property
|
||||
def graphql_url(self) -> str:
|
||||
"""Return the GraphQL URL for the provider. This is typically the base URL with a GraphQL path appended."""
|
||||
match self.service:
|
||||
case "github":
|
||||
if self.url == "https://github.com":
|
||||
return "https://api.github.com/graphql"
|
||||
return self.url.rstrip("/") + "/api/graphql"
|
||||
case "gitlab":
|
||||
return self.url.rstrip("/") + "/api/graphql"
|
||||
case "forgejo":
|
||||
raise ValueError("Forgejo does not support GraphQL.")
|
||||
case _:
|
||||
raise UnsupportedServiceError("Unsupported provider service:' '%s'" % self.service)
|
||||
|
||||
@property
|
||||
def favicon_url(self) -> str:
|
||||
"""Return the URL to the provider's favicon. This is typically the base URL with a favicon path appended."""
|
||||
if self.service == "github":
|
||||
# GitHub's favicon is *actually* an ico, and Discord can't embed those
|
||||
return self.url.rstrip("/") + "/apple-touch-icon.png"
|
||||
return self.url.rstrip("/") + "/favicon.ico"
|
||||
|
||||
async def to_config(self) -> None:
|
||||
"""Save the provider to Red's configuration. Saves to guilds if the `guild` class attribute is not `None`."""
|
||||
if self.guild:
|
||||
providers = await config.guild(self.guild).providers()
|
||||
else:
|
||||
providers = await config.global_providers()
|
||||
|
||||
for provider in providers:
|
||||
if provider["url"] == self.url:
|
||||
provider["token"] = self.token
|
||||
|
||||
if not any(provider["url"] == self.url for provider in providers):
|
||||
providers.append(self.model_dump(mode="json", exclude={"guild"}))
|
||||
|
||||
if self.guild:
|
||||
await config.guild(self.guild).providers.set(providers)
|
||||
else:
|
||||
await config.global_providers.set(providers)
|
||||
|
||||
@classmethod
|
||||
async def from_config(cls, provider_id: str, guild: Guild | None = None) -> "Provider":
|
||||
"""Create a Provider instance from the Red configuration. Checks both global and guild providers, prioritizing guild providers."""
|
||||
if guild:
|
||||
guild_providers = await config.guild(guild).providers()
|
||||
for provider in guild_providers:
|
||||
if provider["id"] == provider_id:
|
||||
return cls(guild=guild, **provider)
|
||||
|
||||
providers = await config.global_providers()
|
||||
for provider in providers:
|
||||
if provider["id"] == provider_id:
|
||||
return cls(guild=None, **provider)
|
||||
raise ValueError("No provider found for ID: %s" % provider_id)
|
||||
|
||||
@classmethod
|
||||
async def fetch_all(cls, guild: Guild | None = None) -> tuple["Provider"]:
|
||||
"""Fetch all providers from the Red configuration. Checks both global and guild providers."""
|
||||
providers_list = []
|
||||
|
||||
if guild:
|
||||
guild_providers = await config.guild(guild).providers()
|
||||
for provider in guild_providers:
|
||||
providers_list.extend((cls(guild=guild, **provider),))
|
||||
|
||||
global_providers = await config.global_providers()
|
||||
for provider in global_providers:
|
||||
providers_list.extend((cls(guild=None, **provider),))
|
||||
|
||||
return tuple(providers_list)
|
Loading…
Add table
Add a link
Reference in a new issue