215 lines
3.9 KiB
Python
215 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import typing
|
|
|
|
from aputils import MessageDate, Signer
|
|
from blib import HttpDate
|
|
from bsql import Row
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any, Self
|
|
|
|
from .. import TRANS
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from .connection import Connection
|
|
|
|
|
|
@dataclass
|
|
class ObjectMixin:
|
|
id: int
|
|
|
|
@classmethod
|
|
def table(cls: type[Self]) -> str:
|
|
return cls.__name__.lower()
|
|
|
|
|
|
@classmethod
|
|
def new(cls: type[Self], *args: Any, **kwargs: Any) -> Self:
|
|
return cls(0, *args, **kwargs)
|
|
|
|
|
|
@classmethod
|
|
def from_row(cls: type[Self], row: Row | dict[str, Any]) -> Self:
|
|
data = []
|
|
|
|
for key in cls.__dataclass_fields__:
|
|
if key.startswith("_"):
|
|
continue
|
|
|
|
try:
|
|
value = row[key]
|
|
|
|
if isinstance(value, datetime):
|
|
value = MessageDate.parse(value)
|
|
|
|
data.append(value)
|
|
|
|
except KeyError:
|
|
print(f"missing key for {type(cls).__name__}: {key}")
|
|
|
|
return cls(*data)
|
|
|
|
|
|
@classmethod
|
|
def from_possible_row(cls: type[Self], row: Row | dict[str, Any] | None) -> Self | None:
|
|
if row is None:
|
|
return None
|
|
|
|
return cls.from_row(row)
|
|
|
|
|
|
def to_dict(self, with_id: bool = True) -> dict[str, Any]:
|
|
data = {}
|
|
|
|
for key in self.__dataclass_fields__:
|
|
if key.startswith("_"):
|
|
continue
|
|
|
|
if key == "id" and not with_id:
|
|
continue
|
|
|
|
data[key] = object.__getattribute__(self, key)
|
|
|
|
return data
|
|
|
|
|
|
@dataclass(slots = True)
|
|
class Cookie(ObjectMixin):
|
|
code: str
|
|
userid: int
|
|
user_agent: str
|
|
last_access: datetime
|
|
created: datetime
|
|
|
|
|
|
@dataclass(slots = True)
|
|
class Follow(ObjectMixin):
|
|
sourceid: int
|
|
source_instanceid: int
|
|
targetid: int
|
|
target_instanceid: int
|
|
followid: str
|
|
accepted: bool
|
|
created: datetime
|
|
|
|
|
|
@classmethod
|
|
def new_from_users(cls: type[Self],
|
|
connection: Connection,
|
|
source: User,
|
|
target: User,
|
|
followid: str,
|
|
accept: bool) -> Self:
|
|
|
|
return cls.new(
|
|
source.id,
|
|
source.get_instance(connection).id,
|
|
target.id,
|
|
target.get_instance(connection).id,
|
|
followid,
|
|
accept,
|
|
HttpDate.new_utc()
|
|
)
|
|
|
|
|
|
@dataclass(slots = True)
|
|
class Instance(ObjectMixin):
|
|
domain: str
|
|
web_domain: str
|
|
shared_inbox: str
|
|
name: str | None
|
|
description: str | None
|
|
software: str
|
|
mod_action: str | None
|
|
mod_action_reason: str | None
|
|
mod_action_date: datetime | None
|
|
note: str | None
|
|
created: datetime
|
|
updated: datetime
|
|
|
|
|
|
def __admin_repr__(self, _: Any) -> str:
|
|
return self.domain
|
|
|
|
|
|
@dataclass(slots = True)
|
|
class User(ObjectMixin):
|
|
username: str
|
|
domain: str
|
|
display_name: str
|
|
actor: str
|
|
inbox: str
|
|
page_url: str
|
|
email: str | None
|
|
password: str | None
|
|
permission: int
|
|
locked: bool
|
|
is_bot: bool
|
|
private_key: str | None
|
|
public_key: str
|
|
bio: str
|
|
info: dict[str, str]
|
|
activated: datetime | None
|
|
mod_action: str | None
|
|
mod_action_reason: str | None
|
|
mod_action_date: datetime | None
|
|
note: str | None
|
|
created: datetime
|
|
updated: datetime
|
|
_instance: Instance | None = None
|
|
|
|
|
|
def __admin_repr__(self, _: Any) -> str:
|
|
return self.username
|
|
|
|
|
|
@property
|
|
def handle(self) -> str:
|
|
return f"{self.username}@{self.domain}"
|
|
|
|
|
|
@property
|
|
def instance(self) -> Instance:
|
|
if self._instance is None:
|
|
raise ValueError("Instance is None")
|
|
|
|
return self._instance
|
|
|
|
|
|
@property
|
|
def signer(self) -> Signer:
|
|
if self.private_key:
|
|
return Signer(self.private_key, self.actor + "#main-key")
|
|
|
|
return Signer(self.public_key, self.actor + "#main-key")
|
|
|
|
|
|
# auth middleware stuff
|
|
@property
|
|
def authenticated(self) -> bool:
|
|
return True
|
|
|
|
|
|
@property
|
|
def identity(self) -> str:
|
|
return "ovo"
|
|
|
|
|
|
def get_instance(self, conn: Connection) -> Instance:
|
|
if not self._instance:
|
|
if (instance := conn.get_instance(self.domain)) is None:
|
|
raise ValueError(TRANS.fetch(
|
|
"error", "no-instance",
|
|
domain = self.domain,
|
|
username = self.username
|
|
))
|
|
|
|
self._instance = instance
|
|
|
|
return self._instance
|
|
|
|
|
|
def set_instance(self, instance: Instance) -> None:
|
|
self._instance = instance
|