social/barkshark_social/database/objects.py
2024-04-18 22:36:06 -04:00

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