Files
omni-parse/omni/tgsess.py
Vlad Gothbreach 42a864419b
Some checks failed
ci / test (3.10) (push) Has been cancelled
ci / test (3.11) (push) Has been cancelled
ci / test (3.12) (push) Has been cancelled
abuse: полный пересчёт индекса только по явному вызову (env не тормозит scan)
2026-10-27 01:21:01 +03:00

175 lines
6.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Telegram через свою сессию (Telethon): глубина и закрытые каналы.
Зачем, если есть веб-превью t.me/s: превью отдаёт ~20 последних постов и ничего
не отдаёт для каналов с выключенным превью. Сессия тянет 200+ постов и читает
каналы/группы, куда аккаунт уже вступил.
Конфиг только через env (секретов в репозитории нет):
OMNI_TG_SESSION путь к .session без расширения
OMNI_TG_API_ID api_id (my.telegram.org)
OMNI_TG_API_HASH api_hash
OMNI_TG_LIMIT сколько постов тянуть на канал (по умолчанию 100)
Если env не задан — модуль молча выключен, парсер работает на веб-превью.
"""
import asyncio
import os
import re
from datetime import datetime, timezone
SERVICE_RE = re.compile(
r"^(channel (created|photo|name|description|username|deleted)|pinned|message pinned|"
r"channel photo (updated|removed)|joined|left|invite link)", re.I)
def enabled():
try:
from . import config
config.tg_cfg()
except Exception:
pass
return bool(os.environ.get("OMNI_TG_SESSION") and os.environ.get("OMNI_TG_API_ID")
and os.environ.get("OMNI_TG_API_HASH"))
def _key(handle):
return handle.strip().strip("/").split("/")[-1].lstrip("@")
async def _pull(handles, limit, session, api_id, api_hash, timeout=40):
from telethon import TelegramClient
client = TelegramClient(session, api_id, api_hash, connection_retries=1, timeout=timeout / 2)
out = []
await asyncio.wait_for(client.connect(), timeout)
try:
for h in handles:
hh = _key(h)
rec = {"handle": hh, "posts": []}
try:
ent = await asyncio.wait_for(client.get_entity(hh), timeout)
msgs = await asyncio.wait_for(client.get_messages(ent, limit=limit), timeout)
rec["title"] = getattr(ent, "title", "") or hh
rec["id"] = getattr(ent, "id", None)
for m in msgs:
text = (getattr(m, "message", "") or "").strip()
if not text or SERVICE_RE.match(text):
continue
rec["posts"].append({
"id": getattr(m, "id", None),
"url": "https://t.me/%s/%s" % (hh, getattr(m, "id", "")),
"title": text.splitlines()[0][:300],
"text": text[:6000],
"views": getattr(m, "views", None),
"date": (m.date.astimezone(timezone.utc).isoformat()
if isinstance(getattr(m, "date", None), datetime) else ""),
"channel": hh,
})
except Exception as e:
rec["error"] = type(e).__name__
out.append(rec)
finally:
try:
await asyncio.wait_for(client.disconnect(), 10)
except Exception:
pass
return out
def fetch(handles, limit=None):
"""Список словарей {handle, title, id, posts[]} по каналам. Пусто, если нет env."""
if not enabled():
return []
try:
from . import config
config.tg_cfg()
except Exception:
pass
session = os.environ["OMNI_TG_SESSION"]
api_id = int(os.environ["OMNI_TG_API_ID"])
api_hash = os.environ["OMNI_TG_API_HASH"]
lim = int(limit or os.environ.get("OMNI_TG_LIMIT") or 100)
return asyncio.run(_pull(list(handles), lim, session, api_id, api_hash))
async def _search(query, limit, session, api_id, api_hash, timeout=60):
"""Глобальный поиск каналов/групп по запросу (contacts.SearchRequest)."""
from telethon import TelegramClient
from telethon.tl.functions.contacts import SearchRequest
client = TelegramClient(session, api_id, api_hash, connection_retries=1, timeout=timeout / 2)
await asyncio.wait_for(client.connect(), timeout)
try:
res = await asyncio.wait_for(client(SearchRequest(q=str(query), limit=int(limit))), timeout)
out = []
for ch in getattr(res, "chats", []) or []:
uname = getattr(ch, "username", None)
if not uname:
continue
out.append({"username": uname, "title": getattr(ch, "title", "") or "",
"id": getattr(ch, "id", None),
"broadcast": bool(getattr(ch, "broadcast", False)),
"megagroup": bool(getattr(ch, "megagroup", False)),
"participants": getattr(ch, "participants_count", None) or 0,
"query": str(query)})
return out
finally:
try:
await asyncio.wait_for(client.disconnect(), 10)
except Exception:
pass
def search(query, limit=40):
"""Каналы по ключевому слову. Пусто, если сессия не настроена."""
if not enabled():
return []
try:
from . import config
config.tg_cfg()
except Exception:
pass
session = os.environ["OMNI_TG_SESSION"]
api_id = int(os.environ["OMNI_TG_API_ID"])
api_hash = os.environ["OMNI_TG_API_HASH"]
return asyncio.run(_search(query, limit, session, api_id, api_hash))
_ME = {}
async def _me(session, api_id, api_hash, timeout=40):
from telethon import TelegramClient
client = TelegramClient(session, api_id, api_hash, connection_retries=1, timeout=timeout / 2)
await asyncio.wait_for(client.connect(), timeout)
try:
u = await asyncio.wait_for(client.get_me(), timeout)
if not u:
return {}
return {"id": getattr(u, "id", None), "username": getattr(u, "username", None),
"first_name": getattr(u, "first_name", None), "premium": bool(getattr(u, "premium", False)),
"phone_tail": (str(getattr(u, "phone", "") or "")[-4:] or None)}
finally:
try:
await asyncio.wait_for(client.disconnect(), 10)
except Exception:
pass
def me():
"""Аккаунт сессии: {id, username, first_name, premium}. Кэш на процесс."""
if _ME:
return _ME
if not enabled():
return {}
try:
from . import config
config.tg_cfg()
except Exception:
pass
session = os.environ["OMNI_TG_SESSION"]
api_id = int(os.environ["OMNI_TG_API_ID"])
api_hash = os.environ["OMNI_TG_API_HASH"]
_ME.update(asyncio.run(_me(session, api_id, api_hash)) or {})
return _ME