2022-08-24 08:09:41 +03:00
|
|
|
from dataclasses import dataclass
|
|
|
|
from datetime import datetime
|
|
|
|
from typing import Optional, List, Literal
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Field:
|
|
|
|
name: str
|
|
|
|
value: str
|
|
|
|
verified_at: Optional[datetime] = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Field":
|
|
|
|
return cls(
|
|
|
|
name=data["name"],
|
|
|
|
value=data["value"],
|
|
|
|
verified_at=(
|
|
|
|
datetime.fromisoformat(data["verified_at"].rstrip("Z"))
|
|
|
|
if data.get("verified_at") is not None
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Emoji:
|
|
|
|
shortcode: str
|
|
|
|
url: str
|
|
|
|
static_url: str
|
|
|
|
visible_in_picker: bool
|
|
|
|
category: Optional[str] = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Emoji":
|
|
|
|
return cls(**data)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Account:
|
|
|
|
id: str
|
|
|
|
username: str
|
|
|
|
acct: str
|
|
|
|
url: str
|
|
|
|
display_name: str
|
|
|
|
note: str
|
|
|
|
avatar: str
|
|
|
|
avatar_static: str
|
|
|
|
header: str
|
|
|
|
header_static: str
|
|
|
|
locked: bool
|
|
|
|
emojis: List[Emoji]
|
|
|
|
discoverable: bool
|
|
|
|
created_at: datetime
|
|
|
|
last_status_at: datetime
|
|
|
|
statuses_count: int
|
|
|
|
followers_count: int
|
|
|
|
following_count: int
|
|
|
|
moved: Optional["Account"] = None
|
|
|
|
fields: Optional[List[Field]] = None
|
|
|
|
bot: Optional[bool] = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Account":
|
|
|
|
return cls(
|
|
|
|
id=data["id"],
|
|
|
|
username=data["username"],
|
|
|
|
acct=data["acct"],
|
|
|
|
url=data["url"],
|
|
|
|
display_name=data["display_name"],
|
|
|
|
note=data["note"],
|
|
|
|
avatar=data["avatar"],
|
|
|
|
avatar_static=data["avatar_static"],
|
|
|
|
header=data["header"],
|
|
|
|
header_static=data["header_static"],
|
|
|
|
locked=data["locked"],
|
|
|
|
emojis=list(map(Emoji.from_dict, data["emojis"])),
|
|
|
|
discoverable=data["discoverable"],
|
|
|
|
created_at=datetime.fromisoformat(data["created_at"].rstrip("Z")),
|
|
|
|
last_status_at=datetime.fromisoformat(data["last_status_at"].rstrip("Z")),
|
|
|
|
statuses_count=data["statuses_count"],
|
|
|
|
followers_count=data["followers_count"],
|
|
|
|
following_count=data["following_count"],
|
|
|
|
moved=(
|
|
|
|
Account.from_dict(data["moved"])
|
|
|
|
if data.get("moved") is not None
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
fields=list(map(Field.from_dict, data.get("fields", []))),
|
|
|
|
bot=bool(data.get("bot")),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-08-26 02:03:06 +03:00
|
|
|
@dataclass
|
|
|
|
class AttachmentMetaImage:
|
|
|
|
@dataclass
|
|
|
|
class Vec2F:
|
|
|
|
x: float
|
|
|
|
y: float
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class AttachmentMetaImageDimensions:
|
|
|
|
width: int
|
|
|
|
height: int
|
|
|
|
size: str
|
|
|
|
aspect: float
|
|
|
|
|
|
|
|
original: AttachmentMetaImageDimensions
|
|
|
|
small: AttachmentMetaImageDimensions
|
|
|
|
focus: Vec2F
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "AttachmentMetaImage":
|
|
|
|
return cls(
|
|
|
|
**data,
|
|
|
|
original=cls.AttachmentMetaImageDimensions(**data["original"]),
|
|
|
|
small=cls.AttachmentMetaImageDimensions(**data["small"]),
|
|
|
|
focus=cls.Vec2F(**data["focus"])
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class AttachmentMetaVideo:
|
|
|
|
@dataclass
|
|
|
|
class AttachmentMetaVideoOriginal:
|
|
|
|
width: int
|
|
|
|
height: int
|
|
|
|
duration: float
|
|
|
|
bitrate: int
|
|
|
|
frame_rate: Optional[str] # XXX Gargron wtf?
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class AttachmentMetaVideoSmall:
|
|
|
|
width: int
|
|
|
|
height: int
|
|
|
|
size: str
|
|
|
|
aspect: float
|
|
|
|
|
|
|
|
length: str
|
|
|
|
duration: float
|
|
|
|
fps: int
|
|
|
|
size: str
|
|
|
|
width: int
|
|
|
|
height: int
|
|
|
|
aspect: float
|
|
|
|
audio_encode: str
|
|
|
|
audio_bitrate: str # XXX GARGROOOOONNNNNN!!!!!!!
|
|
|
|
audio_channels: str # XXX I HATE YOU
|
|
|
|
original: AttachmentMetaVideoOriginal
|
|
|
|
small: AttachmentMetaVideoSmall
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "AttachmentMetaVideo":
|
|
|
|
return cls(
|
|
|
|
**data,
|
|
|
|
original=cls.AttachmentMetaVideoOriginal(**data["original"]),
|
|
|
|
small=cls.AttachmentMetaVideoSmall(**data["small"])
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-08-24 08:09:41 +03:00
|
|
|
@dataclass
|
|
|
|
class Attachment:
|
|
|
|
id: str
|
|
|
|
type: Literal["unknown", "image", "gifv", "video", "audio"]
|
|
|
|
url: str
|
|
|
|
preview_url: str
|
|
|
|
remote_url: Optional[str] = None
|
|
|
|
preview_remote_url: Optional[str] = None
|
|
|
|
meta: Optional[dict] = None
|
|
|
|
description: Optional[str] = None
|
|
|
|
blurhash: Optional[str] = None
|
|
|
|
text_url: Optional[str] = None # XXX: DEPRECATED
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Attachment":
|
|
|
|
return cls(**data)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Application:
|
|
|
|
name: str
|
|
|
|
website: Optional[str] = None
|
|
|
|
vapid_key: Optional[str] = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Application":
|
|
|
|
return cls(**data)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Mention:
|
|
|
|
id: str
|
|
|
|
username: str
|
|
|
|
acct: str
|
|
|
|
url: str
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Mention":
|
|
|
|
return cls(**data)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Tag:
|
|
|
|
name: str
|
|
|
|
url: str
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Tag":
|
|
|
|
return cls(**data)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Status:
|
|
|
|
id: str
|
|
|
|
uri: str
|
|
|
|
created_at: datetime
|
|
|
|
account: Account
|
|
|
|
content: str
|
|
|
|
visibility: Literal["public", "unlisted", "private", "direct"]
|
|
|
|
sensitive: bool
|
|
|
|
spoiler_text: str
|
|
|
|
media_attachments: List[Attachment]
|
|
|
|
reblogs_count: int
|
|
|
|
favourites_count: int
|
|
|
|
replies_count: int
|
|
|
|
application: Optional[Application] = None
|
|
|
|
url: Optional[str] = None
|
|
|
|
in_reply_to_id: Optional[str] = None
|
|
|
|
in_reply_to_account_id: Optional[str] = None
|
|
|
|
reblog: Optional["Status"] = None
|
|
|
|
poll: Optional[dict] = None
|
|
|
|
card: Optional[dict] = None
|
|
|
|
language: Optional[str] = None
|
|
|
|
text: Optional[str] = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_dict(cls, data: dict) -> "Status":
|
|
|
|
return cls(
|
|
|
|
id=data["id"],
|
|
|
|
uri=data["uri"],
|
|
|
|
created_at=datetime.fromisoformat(data["created_at"].rstrip("Z")),
|
|
|
|
account=Account.from_dict(data["account"]),
|
|
|
|
content=data["content"],
|
|
|
|
visibility=data["visibility"],
|
|
|
|
sensitive=data["sensitive"],
|
|
|
|
spoiler_text=data["spoiler_text"],
|
|
|
|
media_attachments=list(
|
|
|
|
map(Attachment.from_dict, data["media_attachments"])
|
|
|
|
),
|
|
|
|
application=(
|
|
|
|
Application.from_dict(data["application"])
|
|
|
|
if data.get("application") is not None
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
reblogs_count=data["reblogs_count"],
|
|
|
|
favourites_count=data["favourites_count"],
|
|
|
|
replies_count=data["replies_count"],
|
|
|
|
url=data.get("url"),
|
|
|
|
in_reply_to_id=data.get("in_reply_to_id"),
|
|
|
|
in_reply_to_account_id=data.get("in_reply_to_account_id"),
|
|
|
|
reblog=(
|
|
|
|
Status.from_dict(data["reblog"])
|
|
|
|
if data.get("reblog") is not None
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
poll=data.get("poll"),
|
|
|
|
card=data.get("card"),
|
|
|
|
language=data.get("language"),
|
|
|
|
text=data.get("text"),
|
|
|
|
)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def link(self) -> str:
|
|
|
|
return self.account.url + "/" + str(self.id)
|