feat: initial async appservice rewrite
This commit is contained in:
parent
266a0d558b
commit
b6c92790d6
2 changed files with 134 additions and 17 deletions
99
src/aioappsrv/app.py
Normal file
99
src/aioappsrv/app.py
Normal 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)
|
|
@ -1,28 +1,46 @@
|
||||||
|
from typing import Any
|
||||||
|
from enum import Enum
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class User:
|
class User:
|
||||||
avatar_url: str = ""
|
avatar_url: str = ""
|
||||||
display_name: str = ""
|
display_name: str = ""
|
||||||
|
|
||||||
|
class EventType(Enum):
|
||||||
|
MESSAGE = "m.room.message"
|
||||||
|
MEMBER = "m.room.member"
|
||||||
|
REDACTION = "m.room.redaction"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class Event:
|
class Event:
|
||||||
def __init__(self, event: dict):
|
content: dict[str, Any]
|
||||||
content = event.get("content", {})
|
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", {})
|
# class Event:
|
||||||
|
# def __init__(self, event: dict):
|
||||||
self.relates_to = rel.get("event_id")
|
# content = event.get("content", {})
|
||||||
self.reltype = rel.get("rel_type")
|
#
|
||||||
self.new_body = content.get("m.new_content", {}).get("body", "")
|
# 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", "")
|
||||||
|
|
Loading…
Reference in a new issue