Files
azul/front/oscar/entry.py
T
Athena Funderburg 4b463a3432 init
2026-05-25 07:05:17 +00:00

108 lines
2.5 KiB
Python

import asyncio
import struct
import settings
from core.backend import Backend
from typing import Optional, Callable
from util.misc import Logger, ProtocolRunner
from .ctrl import OSCARCtrl
from .proto.snac import SNACMessage
def register(loop: asyncio.AbstractEventLoop, backend: Backend) -> None:
backend.add_runner(ProtocolRunner('0.0.0.0', 5190, ListenerOSCAR, args=['OSCAR', backend, OSCARCtrl], service = 'OSCAR'))
class ListenerOSCAR(asyncio.Protocol):
logger: Logger
backend: Backend
controller: OSCARCtrl
transport: Optional[asyncio.WriteTransport]
buffer: bytes = b''
def __init__(self,
logger_prefix: str,
backend: Backend,
controller_factory: Callable[[Logger, str, Backend], OSCARCtrl]) -> None:
super().__init__()
self.logger = Logger(logger_prefix, self)
self.backend = backend
self.controller = controller_factory(self.logger, 'direct', backend)
self.controller.close_callback = self._on_close
self.transport = None
def connection_made(self, transport: asyncio.BaseTransport) -> None:
self.controller.transport = transport
self.controller.on_connect()
self.logger.log_connect()
def connection_lost(self, exc: Optional[Exception]) -> None:
self.controller.close()
self.logger.log_disconnect()
def data_received(self, packet: bytes) -> None:
if self.backend.maintenance_mode:
self.transport.close()
return
self.buffer += packet
self.parse_buffer()
def parse_buffer(self) -> None:
while True:
if self.controller.closed:
self.buffer = b''
break
if self.buffer[0] != 0x2A:
break
# TODO(subpurple): Bufferize this?
frame, sequence, length = struct.unpack('>BHH', self.buffer[1:6])
if len(self.buffer) < length + 6:
break
if len(self.buffer) >= length + 6:
data = self.buffer[6:length + 6]
match frame:
# SIGNON
case 0x01:
self.controller.on_signon_frame(data)
# DATA (always contains a SNAC)
case 0x02:
if len(data) < 10:
return
msg = SNACMessage()
msg.unmarshal(data)
self.controller.on_data_frame(msg)
# ERROR
case 0x03:
self.controller.on_error_frame(data)
# SIGNOFF
case 0x04:
self.controller.on_signoff_frame(data)
# KEEP_ALIVE
case 0x05:
pass
case _:
self.logger.info('Recieved unknown frame:', str(frame), 'with data:', data.hex())
self.buffer = self.buffer[length + 6:]
if len(self.buffer) < 6:
break
def _on_close(self) -> None:
if self.transport is not None:
self.transport.close()