bot: types: add types package

This package collects all kinds of useful type definitions that will
aid me in development.

Some things could be expanded in the future when they become necessary
(e.g. poll result embeds), but for now this should be enough.

Signed-off-by: Max R. Carrara <max@aequito.sh>
This commit is contained in:
Max R. Carrara 2025-03-14 00:12:09 +01:00
parent 8ffe964386
commit 52d619377e
3 changed files with 227 additions and 0 deletions

41
src/bot/types/__init__.py Normal file
View file

@ -0,0 +1,41 @@
from datetime import date, datetime, timezone
from .embed import (
Embed,
RichEmbed,
ImageEmbed,
VideoEmbed,
GifvEmbed,
ArticleEmbed,
LinkEmbed,
)
__all__ = [
"Embed",
"RichEmbed",
"ImageEmbed",
"VideoEmbed",
"GifvEmbed",
"ArticleEmbed",
"LinkEmbed",
"ISO8601Timestamp",
]
class ISO8601Timestamp:
def __init__(self, dt: date | datetime) -> None:
self.dt = dt
def __str__(self) -> str:
return self.dt.isoformat()
def __repr__(self) -> str:
return f"ISO8601Timestamp({repr(self.dt)})"
@classmethod
def today(cls):
return cls(date.today())
@classmethod
def now(cls, tz: timezone | None):
return cls(datetime.now(tz))

166
src/bot/types/embed.py Normal file
View file

@ -0,0 +1,166 @@
from typing import Annotated, Literal, TypedDict
from annotated_types import Len
import discord
from .timestamp import ISO8601Timestamp
class _EmbedFooterReq(TypedDict):
text: str
class _EmbedFooterOpt(TypedDict, total=False):
icon_url: str
proxy_icon_url: str
class EmbedFooter(_EmbedFooterReq, _EmbedFooterOpt):
pass
class _EmbedImageReq(TypedDict):
url: str
class _EmbedImageOpt(TypedDict, total=False):
proxy_url: str
height: int
width: int
class EmbedImage(_EmbedImageReq, _EmbedImageOpt):
pass
class _EmbedThumbnailReq(TypedDict):
url: str
class _EmbedThumbnailOpt(TypedDict, total=False):
proxy_url: str
height: int
width: int
class EmbedThumbnail(_EmbedThumbnailReq, _EmbedThumbnailOpt):
pass
class EmbedVideo(TypedDict, total=False):
url: str
proxy_url: str
height: int
width: int
class EmbedProvider(TypedDict, total=False):
name: str
url: str
class EmbedAuthor(TypedDict, total=False):
name: str
url: str
icon_url: str
proxy_icon_url: str
class _EmbedFieldReq(TypedDict):
name: str
value: str
class _EmbedFieldOpt(TypedDict, total=False):
inline: bool
class EmbedField(_EmbedFieldReq, _EmbedFieldOpt):
pass
class _EmbedBase(TypedDict, total=False):
title: str
description: str
url: str
timestamp: ISO8601Timestamp
color: discord.Colour | int
footer: EmbedFooter
image: EmbedImage
thumbnail: EmbedThumbnail
video: EmbedVideo
provider: EmbedProvider
author: EmbedAuthor
fields: Annotated[list[EmbedField], Len(max_length=25)]
class RichEmbedImpl(TypedDict):
type: Literal["rich"]
class RichEmbed(_EmbedBase, RichEmbedImpl):
pass
class ImageEmbedImpl(TypedDict):
type: Literal["image"]
class ImageEmbed(_EmbedBase, ImageEmbedImpl):
pass
class VideoEmbedImpl(TypedDict):
type: Literal["video"]
class VideoEmbed(_EmbedBase, VideoEmbedImpl):
pass
class GifvEmbedImpl(TypedDict):
type: Literal["givf"]
class GifvEmbed(_EmbedBase, GifvEmbedImpl):
pass
class ArticleEmbedImpl(TypedDict):
type: Literal["article"]
class ArticleEmbed(_EmbedBase, ArticleEmbedImpl):
pass
class LinkEmbedImpl(TypedDict):
type: Literal["link"]
class LinkEmbed(_EmbedBase, LinkEmbedImpl):
pass
class PollResultEmbedImpl(TypedDict):
type: Literal["poll_result"]
# TODO: There are a bunch of fields for poll results, but I *really*
# don't need these at the moment:
# https://discord.com/developers/docs/resources/message#embed-fields-by-embed-type-poll-result-embed-fields
class PollResultEmbed(_EmbedBase, PollResultEmbedImpl):
pass
Embed = (
RichEmbed
| ImageEmbed
| VideoEmbed
| GifvEmbed
| ArticleEmbed
| LinkEmbed
| PollResultEmbed
)

View file

@ -0,0 +1,20 @@
from datetime import date, datetime, timezone
class ISO8601Timestamp:
def __init__(self, dt: date | datetime) -> None:
self.dt = dt
def __str__(self) -> str:
return self.dt.isoformat()
def __repr__(self) -> str:
return f"ISO8601Timestamp({repr(self.dt)})"
@classmethod
def today(cls):
return cls(date.today())
@classmethod
def now(cls, tz: timezone | None):
return cls(datetime.now(tz))