Periodic background indexing via scheduler; !rooms reads cache; v1.0.8

This commit is contained in:
Eddy de Vink 2026-09-01 18:35:44 +02:00
commit 1bfc35024c
4 changed files with 54 additions and 33 deletions

View file

@ -14,7 +14,6 @@ rooms_per_message: 50
# Voorkomt dat een enorme (gefedereerde) space-boom leidt tot duizenden berichten.
max_rooms: 300
# Hoe lang (minuten) de opgehaalde lijst wordt gecached in de plugin-database.
# Binnen deze periode gebruikt !rooms de cache i.p.v. de hele boom opnieuw op te
# halen (minder traffic naar de homeserver en federatie). 0 = caching uit.
cache_ttl_minutes: 15
# Hoe vaak (minuten) de bot de ruimte-hierarchy opnieuw indexeert op de
# achtergrond. !rooms toont altijd het laatste geindexeerde resultaat.
refresh_interval_minutes: 15

View file

@ -1,6 +1,6 @@
maubot: 0.1.0
id: nl.roomsoverview
version: 1.0.7
version: 1.0.8
license: MIT
modules:
- roomsoverview

Binary file not shown.

View file

@ -1,6 +1,7 @@
import asyncio
import json
import time
from functools import partial
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import quote
@ -58,12 +59,36 @@ class Config(BaseProxyConfig):
helper.copy("max_depth")
helper.copy("rooms_per_message")
helper.copy("max_rooms")
helper.copy("cache_ttl_minutes")
helper.copy("refresh_interval_minutes")
class RoomsOverviewPlugin(Plugin):
async def start(self) -> None:
self.config.load_and_update()
space_id = str(self.config.get("space_id", "") or "")
interval_minutes = max(1, int(self.config.get("refresh_interval_minutes", 15) or 15))
if not space_id or space_id.startswith("!vervang-mij"):
return
self._refresh_lock = asyncio.Lock()
self._scheduled_tasks = []
# Direct eenmalig indexeren (prewarm), daarna periodiek.
self._scheduled_tasks.append(
self.sched.run_later(2, self._refresh_cache(space_id))
)
self._scheduled_tasks.append(
self.sched.run_periodically(
interval_minutes * 60,
partial(self._refresh_cache, space_id),
run_task_in_background=True,
)
)
self.log.info(
f"start: indexering gepland elke {interval_minutes} minuten"
)
async def stop(self) -> None:
for task in getattr(self, "_scheduled_tasks", []):
task.cancel()
@classmethod
def get_config_class(cls):
@ -155,6 +180,19 @@ class RoomsOverviewPlugin(Plugin):
break
return rooms, True
async def _refresh_cache(self, space_id: str) -> None:
"""Live ophalen en in de cache opslaan (gedeeld via lock, ook achtergrond)."""
async with self._refresh_lock:
entries, complete = await self._fetch_hierarchy(space_id)
rooms = [
r
for r in entries
if r.get("room_id") != space_id
and (r.get("room_type") or "") != "m.space"
]
await self._store_cache(space_id, rooms, complete)
self.log.info(f"indexering voltooid ({len(rooms)} rooms)")
async def _load_cache(
self, space_id: str
) -> Optional[Tuple[List[Dict[str, Any]], bool, int]]:
@ -191,35 +229,19 @@ class RoomsOverviewPlugin(Plugin):
await evt.mark_read()
ttl_minutes = max(0, int(self.config.get("cache_ttl_minutes", 15) or 15))
cached = await self._load_cache(space_id)
now = int(time.time())
if cached and ttl_minutes > 0 and now - cached[2] < ttl_minutes * 60:
rooms, complete = cached[0], cached[1]
self.log.info(
f"rooms: cache hit voor {space_id} (age {now - cached[2]}s)"
)
else:
entries, complete = await self._fetch_hierarchy(space_id)
rooms = [
r
for r in entries
if r.get("room_id") != space_id
and (r.get("room_type") or "") != "m.space"
]
await self._store_cache(space_id, rooms, complete)
if cached is None:
# Nog nooit geïndexeerd: vraag een indexering aan en wacht erop
# (lock deelt met de achtergrondtaak, dus geen dubbele fetches).
await self._refresh_cache(space_id)
cached = await self._load_cache(space_id)
rooms, complete = cached[0], cached[1] if cached else ([], True)
if not rooms:
if not complete:
await evt.reply(
"De ruimte-hierarchy kon niet worden opgehaald. Controleer de "
"space_id en of de bot lid is van (of toegang heeft tot) de space."
)
else:
await evt.reply(
"Geen rooms gevonden. Controleer of de space_id klopt en of de bot "
"lid is van (of toegang heeft tot) de space."
)
await evt.reply(
"Er zijn (nog) geen rooms gevonden. Controleer de space_id en of de bot "
"lid is van (of toegang heeft tot) de space."
)
return
def sort_key(r: Dict[str, Any]) -> str:
@ -248,5 +270,5 @@ class RoomsOverviewPlugin(Plugin):
for idx, chunk in enumerate(chunks, start=1):
header = f"**Rooms overzicht ({total} rooms getoond, deel {idx}/{len(chunks)}):**\n\n"
if idx == 1 and not complete:
header += "*De lijst is mogelijk onvolledig: de ruimte-hierarchy kon niet volledig worden opgehaald.*\n\n"
header += "*De lijst is mogelijk onvolledig: de laatste indexering was niet compleet.*\n\n"
await evt.respond(header + "\n".join(chunk), markdown=True)