feat(watcher,api,frontend): archive who writes to watched bots over mtproto

This commit is contained in:
hh committed 2026-09-18 15:05:01 +02:00
1 parent 089a31db8d
commit 4bd031f21e
30 files changed
+1863 -10

No files matched your search

+46 -2
View File
@@ -13,9 +13,9 @@ from pydantic import BaseModel
from api.mcp.format import build_transcript, load_notes, load_photos, resolve_names
from dependencies.container import container
from utils.jobs import enqueue
from utils.read import annotations, chats, media, peers, presence, social, watches
from utils.read import annotations, bots, chats, media, peers, presence, social, watches
from utils.read.accounts import self_user_id, unknown_account_hint
from utils.read.models import DEFAULT_LIMIT, Page
from utils.read.models import DEFAULT_LIMIT, MAX_LIMIT, Page
from utils.search.models import SearchFilters
from utils.search.repository import search_messages
from utils.storage import ContentAddressedStorage
@@ -58,6 +58,12 @@ Typical flows:
- Monitoring: `set_watch` creates a local rule, `list_watches` lists rules, and
`list_alerts` reads what those rules fired.
Watched bots (a separate axis, `account_id` does not apply):
- beavergram can also hold bot tokens of arbitrary bots and archive who writes
to them over MTProto. `list_watched_bots` names them, `get_bot_stats` gives
the aggregate picture and `get_bot_updates` the raw incoming stream. These
take `bot_id` (the bot's Telegram id), never `account_id`.
Writes: everything is read-only except `set_watch`, the only allowed write.
Paging: list/search tools accept `limit` and `offset`.
@@ -356,3 +362,41 @@ async def list_alerts(
await _pool(), account_id, Page(limit=limit, offset=offset), seen=seen
)
)
@mcp.tool
async def list_watched_bots() -> list[dict[str, Any]]:
"""List bots this beavergram watches, with their status and counters."""
return _dump(await bots.list_bots(await _pool()))
@mcp.tool
async def get_bot_stats(
bot_id: int, days: int = 90, senders: int = 20
) -> dict[str, Any] | None:
"""Who writes to a watched bot: totals, daily volume, hours, top senders."""
result = await bots.stats(await _pool(), bot_id, days=days, senders=senders)
return result.model_dump(mode="json") if result else None
@mcp.tool
async def get_bot_updates(
bot_id: int,
sender_id: int | None = None,
chat_id: int | None = None,
incoming_only: bool = True,
limit: int = DEFAULT_LIMIT,
offset: int = 0,
) -> list[dict[str, Any]]:
"""Read what people sent to a watched bot, newest first."""
return _dump(
await bots.list_updates(
await _pool(),
bot_id,
sender_id=sender_id,
chat_id=chat_id,
incoming_only=incoming_only,
limit=min(limit, MAX_LIMIT),
offset=offset,
)
)