This commit is contained in:
Athena Funderburg
2026-05-25 07:05:17 +00:00
commit 4b463a3432
682 changed files with 47796 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
from core.backend import Backend, BackendSession
from core.client import Client
from core.models import User
from dataclasses import dataclass
from typing import Optional, Callable, Any
from .buffer import Buffer
foodgroups = {}
@dataclass
class SNACMessage(Buffer):
__slots__ = ('foodgroup', 'subgroup', 'flags', 'request_id', 'data')
foodgroup: int
subgroup: int
flags: int
request_id: int
data: bytes
def __init__(self, foodgroup: int = 0x0000, subgroup: int = 0x0000, flags: int = 0x0000,
request_id: int = 0x00000000, data: bytes = b'') -> None:
super().__init__()
self.foodgroup = foodgroup
self.subgroup = subgroup
self.flags = flags
self.request_id = request_id
self.data = data
def marshal(self) -> bytes:
# we use a new Buffer here because even though we are a subclass of Buffer, the methods are reserved
# for SNAC data (everything after the header)
buf = Buffer()
buf.write_u16(self.foodgroup)
buf.write_u16(self.subgroup)
buf.write_u16(self.flags)
buf.write_u32(self.request_id)
buf.write_bytes(self.data)
return buf.data
def unmarshal(self, flap_data: bytes) -> None:
buf = Buffer(flap_data)
self.foodgroup = buf.read_u16()
self.subgroup = buf.read_u16()
self.flags = buf.read_u16()
self.request_id = buf.read_u32()
self.data = buf.data
class Foodgroup:
__slots__ = ('value', 'cls')
value: int
cls: Any
def __init__(self, value) -> None:
self.value = value
self.cls = None
def __call__(self, *args) -> None:
self.cls = args[0]
foodgroups[self.value] = self.cls()
class Subgroup:
__slots__ = ('value', 'mode', 'func')
value: int
mode: str
func: Optional[Callable]
def __init__(self, value) -> None:
self.value = value
self.mode = 'decorating'
self.func = None
def __call__(self, *args) -> Any:
if self.mode == 'decorating':
self.func = args[0]
self.mode = 'calling'
return self
return self.func(*args)
def __set_name__(self, owner, name) -> None:
if not hasattr(owner, 'subgroups'):
owner.subgroups = {}
owner.subgroups[self.value] = self.func
self.func.class_name = owner.__name__
setattr(owner, name, self.func)
# OSCARClient and OSCARContext
class OSCARClient:
__slots__ = 'ctrl'
ctrl: Any # Any because of circular imports - TODO(subpurple): make this less hacky
def __init__(self, ctrl: Any) -> None:
self.ctrl = ctrl
def send_snac(self, msg: SNACMessage) -> None:
self.ctrl.send_specific_frame(0x02, msg.marshal())
def get_ip(self) -> str:
ip, *_ = self.ctrl.transport.get_extra_info('peername')
return ip
@dataclass
class OSCARContext:
__slots__ = ('backend', 'bs', 'client', 'user')
backend: Backend
bs: Optional[BackendSession]
client: Client
# These slots are equivalent to .bs.* and only exist for the sake of convenience
user: Optional[User]
def __init__(self, backend: Backend, client: Client) -> None:
self.backend = backend
self.bs = None
self.client = client
self.user = None