Add presence heartbeat to keep bot showing as online
matrix-nio's AsyncClient.set_presence() is called every 60s in a background task so Matrix clients see the bot as online. On shutdown the bot sets itself to offline. https://claude.ai/code/session_01DuzWyMMXvLMB4VxEwJyV4X
This commit is contained in:
@@ -4,7 +4,7 @@ from contextlib import asynccontextmanager
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from app.database import close_db, get_thread_root, init_db, set_thread_root
|
from app.database import close_db, get_thread_root, init_db, set_thread_root
|
||||||
from app.matrix import THREAD_ROOT_MESSAGES, close_client, send_thread_root
|
from app.matrix import THREAD_ROOT_MESSAGES, close_client, send_thread_root, start_presence
|
||||||
from app.webhooks import router as webhook_router
|
from app.webhooks import router as webhook_router
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -19,6 +19,7 @@ async def lifespan(app: FastAPI):
|
|||||||
log.info("Starting Melora")
|
log.info("Starting Melora")
|
||||||
await init_db()
|
await init_db()
|
||||||
await _ensure_thread_roots()
|
await _ensure_thread_roots()
|
||||||
|
await start_presence()
|
||||||
log.info("Melora ready")
|
log.info("Melora ready")
|
||||||
yield
|
yield
|
||||||
log.info("Shutting down Melora")
|
log.info("Shutting down Melora")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from nio import AsyncClient, RoomSendResponse
|
from nio import AsyncClient, RoomSendResponse
|
||||||
@@ -7,6 +8,9 @@ from app.config import config
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
_client: AsyncClient | None = None
|
_client: AsyncClient | None = None
|
||||||
|
_presence_task: asyncio.Task | None = None
|
||||||
|
|
||||||
|
PRESENCE_INTERVAL_SECONDS = 60
|
||||||
|
|
||||||
THREAD_ROOT_MESSAGES: dict[str, str] = {
|
THREAD_ROOT_MESSAGES: dict[str, str] = {
|
||||||
"movies": "\U0001f3ac Movies",
|
"movies": "\U0001f3ac Movies",
|
||||||
@@ -25,12 +29,41 @@ async def get_client() -> AsyncClient:
|
|||||||
|
|
||||||
|
|
||||||
async def close_client() -> None:
|
async def close_client() -> None:
|
||||||
global _client
|
global _client, _presence_task
|
||||||
|
if _presence_task is not None:
|
||||||
|
_presence_task.cancel()
|
||||||
|
try:
|
||||||
|
await _presence_task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
_presence_task = None
|
||||||
if _client is not None:
|
if _client is not None:
|
||||||
|
await _set_presence("offline")
|
||||||
await _client.close()
|
await _client.close()
|
||||||
_client = None
|
_client = None
|
||||||
|
|
||||||
|
|
||||||
|
async def _set_presence(state: str) -> None:
|
||||||
|
client = await get_client()
|
||||||
|
try:
|
||||||
|
await client.set_presence(state)
|
||||||
|
except Exception:
|
||||||
|
log.debug("Presence update failed", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def _presence_loop() -> None:
|
||||||
|
while True:
|
||||||
|
await _set_presence("online")
|
||||||
|
await asyncio.sleep(PRESENCE_INTERVAL_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
|
async def start_presence() -> None:
|
||||||
|
global _presence_task
|
||||||
|
await _set_presence("online")
|
||||||
|
_presence_task = asyncio.create_task(_presence_loop())
|
||||||
|
log.info("Presence heartbeat started (every %ds)", PRESENCE_INTERVAL_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
async def send_thread_root(media_type: str) -> str:
|
async def send_thread_root(media_type: str) -> str:
|
||||||
client = await get_client()
|
client = await get_client()
|
||||||
label = THREAD_ROOT_MESSAGES[media_type]
|
label = THREAD_ROOT_MESSAGES[media_type]
|
||||||
|
|||||||
Reference in New Issue
Block a user