feat: initial async appservice rewrite

This commit is contained in:
əlemi 2024-01-29 16:52:39 +01:00
parent 266a0d558b
commit b6c92790d6
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 134 additions and 17 deletions

99
src/aioappsrv/app.py Normal file
View file

@ -0,0 +1,99 @@
import asyncio
import logging
from typing import Callable, Awaitable
from aiohttp import ClientSession, web
from .matrix import Event, EventType
class AppService:
_rx: web.Application
_tx: ClientSession
_callbacks: dict[EventType, dict[str, Callable]]
as_token: str
hs_token: str
base_url: str
user_id: str
server_name: str
logger: logging.Logger
def __init__(
self,
as_token: str,
hs_token: str,
base_url: str,
user_id: str,
server_name: str,
logger: logging.Logger | None = None,
):
self._rx = web.Application()
self.as_token = as_token
self.hs_token = hs_token
self.base_url = base_url
self.user_id = user_id
self.server_name = server_name
self.logger = logger if logger is not None else logging.getLogger(__file__)
self._rx.add_routes([
web.put('/transactions/{transaction}', lambda req: self.handler(req))
])
async def handler(self, request: web.Request) -> web.Response:
self.logger.debug("handling webhook callback %s", request)
hs_token = request.query.getone("access_token")
if not hs_token:
return web.Response(status=401)
if hs_token != self.hs_token:
return web.Response(status=403)
body = await request.json()
for doc in body["events"]:
event = Event(**doc)
if event.type not in EventType:
self.logger.warn("unhandled event of type %s, ignoring", event["type"])
continue
self.logger.debug("dispatching event %s for %s", event.type)
asyncio.get_event_loop().create_task(
self._callbacks[event.type][event.room_id](event)
)
return web.Response()
def callback(self, room: str, event: EventType | str = EventType.MESSAGE) -> Callable:
def wrapper(func: Callable[[Event], Awaitable[None]]):
if isinstance(event, str):
key = EventType[event]
else:
key = event
self._callbacks[key][room] = func
return func
return wrapper
async def join_room(self):
pass
async def leave_room(self):
pass
async def send_message(self):
pass
async def redact_message(self):
pass
async def start(self, host: str = "localhost", port: int = 25511):
runner = web.AppRunner(self._rx)
await runner.setup()
site = web.TCPSite(runner, host, port)
await site.start()
# utility function in case we need to block forever
async def idle():
while True:
await asyncio.sleep(3600)

View file

@ -1,28 +1,46 @@
from typing import Any
from enum import Enum
from dataclasses import dataclass
@dataclass
class User:
avatar_url: str = ""
display_name: str = ""
class EventType(Enum):
MESSAGE = "m.room.message"
MEMBER = "m.room.member"
REDACTION = "m.room.redaction"
@dataclass
class Event:
def __init__(self, event: dict):
content = event.get("content", {})
content: dict[str, Any]
type: EventType
event_id: str
origin_server_ts: int
room_id: str
sender: str
state_key: str | None = None
redacts: str | None = None
unsigned: dict | None = None
self.attachment = content.get("url")
self.body = content.get("body", "").strip()
self.formatted_body = content.get("formatted_body", "")
self.id = event["event_id"]
self.is_direct = content.get("is_direct", False)
self.redacts = event.get("redacts", "")
self.room_id = event["room_id"]
self.sender = event["sender"]
self.state_key = event.get("state_key", "")
rel = content.get("m.relates_to", {})
self.relates_to = rel.get("event_id")
self.reltype = rel.get("rel_type")
self.new_body = content.get("m.new_content", {}).get("body", "")
# class Event:
# def __init__(self, event: dict):
# content = event.get("content", {})
#
# self.attachment = content.get("url")
# self.body = content.get("body", "").strip()
# self.formatted_body = content.get("formatted_body", "")
# self.id = event["event_id"]
# self.is_direct = content.get("is_direct", False)
# self.redacts = event.get("redacts", "")
# self.room_id = event["room_id"]
# self.sender = event["sender"]
# self.state_key = event.get("state_key", "")
#
# rel = content.get("m.relates_to", {})
#
# self.relates_to = rel.get("event_id")
# self.reltype = rel.get("rel_type")
# self.new_body = content.get("m.new_content", {}).get("body", "")