fix: Persist login credentials for stable device ID
- Save user_id/device_id/access_token to crypto store on first login - restore_login() on subsequent starts (no new device each restart) - Enables proper Olm session persistence across restarts CF-1147 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
41
bot.py
41
bot.py
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ LK_KEY = os.environ["LIVEKIT_API_KEY"]
|
|||||||
LK_SECRET = os.environ["LIVEKIT_API_SECRET"]
|
LK_SECRET = os.environ["LIVEKIT_API_SECRET"]
|
||||||
AGENT_NAME = os.environ.get("AGENT_NAME", "matrix-ai")
|
AGENT_NAME = os.environ.get("AGENT_NAME", "matrix-ai")
|
||||||
STORE_PATH = os.environ.get("CRYPTO_STORE_PATH", "/data/crypto_store")
|
STORE_PATH = os.environ.get("CRYPTO_STORE_PATH", "/data/crypto_store")
|
||||||
|
CREDS_FILE = os.path.join(STORE_PATH, "credentials.json")
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
@@ -47,13 +49,31 @@ class Bot:
|
|||||||
self.dispatched_rooms = set()
|
self.dispatched_rooms = set()
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
resp = await self.client.login(BOT_PASS, device_name="ai-voice-bot")
|
# Restore existing session or create new one
|
||||||
if not isinstance(resp, LoginResponse):
|
if os.path.exists(CREDS_FILE):
|
||||||
logger.error("Login failed: %s", resp)
|
with open(CREDS_FILE) as f:
|
||||||
return
|
creds = json.load(f)
|
||||||
logger.info("Logged in as %s (device %s)", resp.user_id, resp.device_id)
|
self.client.restore_login(
|
||||||
|
user_id=creds["user_id"],
|
||||||
|
device_id=creds["device_id"],
|
||||||
|
access_token=creds["access_token"],
|
||||||
|
)
|
||||||
|
self.client.load_store()
|
||||||
|
logger.info("Restored session as %s (device %s)", creds["user_id"], creds["device_id"])
|
||||||
|
else:
|
||||||
|
resp = await self.client.login(BOT_PASS, device_name="ai-voice-bot")
|
||||||
|
if not isinstance(resp, LoginResponse):
|
||||||
|
logger.error("Login failed: %s", resp)
|
||||||
|
return
|
||||||
|
# Persist credentials for next restart
|
||||||
|
with open(CREDS_FILE, "w") as f:
|
||||||
|
json.dump({
|
||||||
|
"user_id": resp.user_id,
|
||||||
|
"device_id": resp.device_id,
|
||||||
|
"access_token": resp.access_token,
|
||||||
|
}, f)
|
||||||
|
logger.info("Logged in as %s (device %s) — credentials saved", resp.user_id, resp.device_id)
|
||||||
|
|
||||||
# Trust our own device keys
|
|
||||||
if self.client.should_upload_keys:
|
if self.client.should_upload_keys:
|
||||||
await self.client.keys_upload()
|
await self.client.keys_upload()
|
||||||
|
|
||||||
@@ -74,7 +94,6 @@ class Bot:
|
|||||||
logger.info("Invited to %s", room.room_id)
|
logger.info("Invited to %s", room.room_id)
|
||||||
await self.client.join(room.room_id)
|
await self.client.join(room.room_id)
|
||||||
|
|
||||||
# LiveKit room name = Matrix room ID (Element Call convention)
|
|
||||||
lk_room_name = room.room_id
|
lk_room_name = room.room_id
|
||||||
try:
|
try:
|
||||||
await self.lkapi.agent_dispatch.create_dispatch(
|
await self.lkapi.agent_dispatch.create_dispatch(
|
||||||
@@ -90,17 +109,17 @@ class Bot:
|
|||||||
|
|
||||||
async def on_sync(self, response: SyncResponse):
|
async def on_sync(self, response: SyncResponse):
|
||||||
"""After each sync, trust all devices in our rooms."""
|
"""After each sync, trust all devices in our rooms."""
|
||||||
for user_id in self.client.device_store.users:
|
for user_id in list(self.client.device_store.users):
|
||||||
for device in self.client.device_store.active_user_devices(user_id):
|
for device in self.client.device_store.active_user_devices(user_id):
|
||||||
if not device.verified:
|
if not device.verified:
|
||||||
self.client.verify_device(device)
|
self.client.verify_device(device)
|
||||||
logger.info("Auto-trusted device %s of %s", device.device_id, user_id)
|
logger.info("Auto-trusted device %s of %s", device.device_id, user_id)
|
||||||
|
|
||||||
async def on_megolm(self, room, event: MegolmEvent):
|
async def on_megolm(self, room, event: MegolmEvent):
|
||||||
"""Handle undecryptable messages by requesting keys."""
|
"""Log undecryptable messages."""
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Can't decrypt event %s in %s from %s (session %s)",
|
"Undecryptable event %s in %s from %s",
|
||||||
event.event_id, room.room_id, event.sender, event.session_id,
|
event.event_id, room.room_id, event.sender,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def on_key_verification(self, event):
|
async def on_key_verification(self, event):
|
||||||
|
|||||||
Reference in New Issue
Block a user