1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
"""Matrix bot adapter for Sénia.
Runs as a long-lived asyncio task alongside the API server. Watches one room,
forwards messages to `handle_turn`, posts the reply back.
"""
import asyncio
import logging
import os
from collections import deque
from nio import AsyncClient, MatrixRoom, RoomMessageText
from .loop import handle_turn
from .models import Message
log = logging.getLogger(__name__)
HOMESERVER = os.environ.get("MATRIX_HOMESERVER", "https://beep.volfaria.xyz")
USER_ID = os.environ.get("MATRIX_USER_ID", "@senia:beep.volfaria.xyz")
TOKEN = os.environ["MATRIX_TOKEN"]
ROOM = os.environ.get("MATRIX_ROOM", "#senia:beep.volfaria.xyz")
SENIA_NAME = "Sénia"
HISTORY_LEN = 30
class MatrixBot:
def __init__(self):
self.client = AsyncClient(HOMESERVER, USER_ID)
self.history: deque[Message] = deque(maxlen=HISTORY_LEN)
self._room_id: str | None = None
async def start(self):
whoami = await self.client.whoami()
self.client.restore_login(
user_id=USER_ID, device_id=whoami.device_id, access_token=TOKEN
)
self._room_id = await self._resolve_room(ROOM)
await self.client.join(self._room_id)
self.client.add_event_callback(self._on_message, RoomMessageText)
log.info("Sénia matrix bot connected, watching %s", self._room_id)
await self.client.sync_forever(timeout=30000, full_state=True)
async def _resolve_room(self, room: str) -> str:
if room.startswith("!"):
return room
resp = await self.client.room_resolve_alias(room)
return resp.room_id
async def _on_message(self, room: MatrixRoom, event: RoomMessageText):
if event.sender == self.client.user_id or room.room_id != self._room_id:
return
sender_name = room.user_name(event.sender) or event.sender
self.history.append(Message(role="user", content=event.body, name=sender_name))
try:
reply = await asyncio.to_thread(handle_turn, list(self.history), sender_name)
except Exception:
log.exception("handle_turn failed")
return
self.history.append(Message(role="assistant", content=reply, name=SENIA_NAME))
await self.client.room_send(
room_id=self._room_id,
message_type="m.room.message",
content={"msgtype": "m.text", "body": reply},
)
async def run():
bot = MatrixBot()
await bot.start()
|