added public chat bale fetch agent

This commit is contained in:
Reza Yaghobi
2026-08-30 22:30:31 +03:30
parent b307977639
commit b52d24daad
3 changed files with 442 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
"""
fetch_metro_smart.py -- decides which file to grab, instead of always
sending the same button.
Route 2: since we're authenticated as a real user (not a bot token), we can
read the FULL history of this bot chat, not just react to new messages.
Every file the bot has ever sent you (from any button you've pressed before)
is sitting in that history with a caption. So instead of guessing which
button maps to which request, we search what's already there.
Usage (n8n passes the LLM-parsed keyword as the one argument):
python3 fetch_metro_smart.py "خطوط"
Prints one line of JSON to stdout: {"files": [...], "matched": true/false}
"""
import asyncio
import json
import sys
import pathlib
from typing import Any
from aiobale import Client, Dispatcher
from aiobale.enums import ChatType
# ---- upstream library workaround ---------------------------------------
# aiobale hard-types TextMessage.value as `str`, but Bale's servers attach a
# view/seen-tracking payload (a dict) to some messages from large verified
# bots -- history scans on those chats crash with a pydantic ValidationError
# before we ever see the message. This loosens that one field so parsing
# succeeds; matches() below coerces to str defensively since the value can
# now legitimately be a dict instead of text.
import aiobale.types.message_content as _mc
_mc.TextMessage.model_fields["value"].annotation = Any
_mc.TextMessage.model_rebuild(force=True)
# -------------------------------------------------------------------------
# ---- hardcoded demo config --------------------------------------------
SESSION_FILE = "/mnt/storage/bale-downloads/metro_session.bale"
PHONE_NUMBER = 989381393442
TARGET_CHAT_ID = 1381242617 # متروی تهران و حومه
TARGET_CHAT_TYPE = ChatType.BOT
DOWNLOAD_DIR = pathlib.Path("/mnt/storage/bale-downloads")
MAX_MESSAGES = 200 # how far back into this chat's history to scan
# -------------------------------------------------------------------------
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
dp = Dispatcher()
client = Client(dp, session_file=SESSION_FILE, phone_number=PHONE_NUMBER)
def matches(doc, msg, keyword: str) -> bool:
keyword = (keyword or "").lower().strip()
if not keyword:
return False # no keyword = match nothing (avoid grabbing everything by accident)
caption = doc.caption.content if doc.caption else ""
msg_text = msg.text
if not isinstance(msg_text, str):
msg_text = "" # some messages carry a non-text payload in this field -- see workaround above
haystack = " ".join(filter(None, [doc.name or "", caption or "", msg_text])).lower()
return keyword in haystack
async def main(keyword: str):
await client.start(run_in_background=True)
await asyncio.sleep(2)
saved = []
try:
async for msg in client.iter_messages(
chat_id=TARGET_CHAT_ID, chat_type=TARGET_CHAT_TYPE, limit=MAX_MESSAGES
):
doc = msg.document
if not doc:
continue
if matches(doc, msg, keyword):
dest = DOWNLOAD_DIR / (doc.name or f"{msg.message_id}.bin")
if str(dest) in saved:
continue # already matched this exact file earlier in history
await msg.download(destination=dest)
saved.append(str(dest))
finally:
await client.stop()
print(json.dumps({"files": saved, "matched": len(saved) > 0}))
if __name__ == "__main__":
kw = sys.argv[1] if len(sys.argv) > 1 else ""
asyncio.run(main(kw))