feat(backend): get_feed mcp tool reads the feed of deleted and edited messages

This commit is contained in:
hh committed 2026-10-19 02:59:07 +00:00
1 parent 83c57ac96b
commit 395d58464d
1 file changed
+39 -2
+39 -2
View File
@@ -1,3 +1,4 @@
import re
from collections.abc import Sequence from collections.abc import Sequence
from datetime import datetime from datetime import datetime
from typing import Any from typing import Any
@@ -23,6 +24,7 @@ from utils.files import resolve_mime
from utils.read import ( from utils.read import (
annotations, annotations,
chats, chats,
feed,
media, media,
peers, peers,
people, people,
@@ -32,6 +34,7 @@ from utils.read import (
watches, watches,
) )
from utils.read.accounts import self_user_id, unknown_account_hint from utils.read.accounts import self_user_id, unknown_account_hint
from utils.read.feed import CURSOR_PATTERN, FeedChatKind, FeedFilter, FeedKind
from utils.read.models import DEFAULT_LIMIT, Page from utils.read.models import DEFAULT_LIMIT, Page
from utils.read.period import DEFAULT_TZ, MAX_DAYS, known_timezone from utils.read.period import DEFAULT_TZ, MAX_DAYS, known_timezone
from utils.read.read_receipts import read_info from utils.read.read_receipts import read_info
@@ -73,8 +76,10 @@ Typical flows:
transcripts; supports chat/sender/date filters and regex). transcripts; supports chat/sender/date filters and regex).
- People: `get_person` gives one person in full: profile changes, shared groups, - People: `get_person` gives one person in full: profile changes, shared groups,
joins and leaves, deleted message count, the private chat. joins and leaves, deleted message count, the private chat.
- Forensics: `get_deleted_messages` and `get_message_versions` recover content - Forensics: `get_feed` lists what was deleted or edited in Telegram across
removed or edited in Telegram but kept in the archive. all chats, newest first, with the text before and after each edit; a bulk
deletion is one item with `message_count`. `get_deleted_messages` and
`get_message_versions` recover the content of one chat or one message.
- Files: `download_media` gets any message attachment (photo, video, voice, - Files: `download_media` gets any message attachment (photo, video, voice,
document, ...). It pulls the file from Telegram if the archive lacks it and document, ...). It pulls the file from Telegram if the archive lacks it and
returns a public link valid 24h that needs no auth (`curl -O <link>`), plus returns a public link valid 24h that needs no auth (`curl -O <link>`), plus
@@ -255,6 +260,38 @@ async def get_deleted_messages(
) )
@mcp.tool
async def get_feed(
account_id: int,
kind: FeedKind = FeedKind.ALL,
since: datetime | None = None,
chat_id: int | None = None,
chat_kind: FeedChatKind | None = None,
limit: int = DEFAULT_LIMIT,
cursor: str | None = None,
) -> dict[str, Any]:
"""Deleted and edited messages across chats, newest first.
`kind` picks deleted, edited or all; `since` keeps items at or after that
time; `chat_id` or `chat_kind` (dm, group, channel) narrow it down. Edited
items carry `before` and `after` texts. A deleted item with
`message_count` above 1 is a bulk deletion in one chat; read its messages
with `get_deleted_messages` for that chat. Pass `next_cursor` back as
`cursor` for the next page.
"""
if cursor is not None and re.fullmatch(CURSOR_PATTERN, cursor) is None:
msg = "cursor must be a next_cursor returned by get_feed"
raise ToolError(msg)
page = await feed.list_feed(
await _pool(),
account_id,
FeedFilter(kind=kind, chat_id=chat_id, chat_kind=chat_kind, since=since),
limit=limit,
cursor=cursor,
)
return page.model_dump(mode="json")
@mcp.tool @mcp.tool
async def get_message_versions( async def get_message_versions(
account_id: int, chat_id: int, message_id: int account_id: int, chat_id: int, message_id: int