notebookvb
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"""
|
||||
telegram_notify.py
|
||||
------------------
|
||||
Notifikace a obousměrná komunikace přes Telegram Bot API
|
||||
(bot ClaudeBot @Vlado_Claude_Bot).
|
||||
|
||||
Token a výchozí chat_id se načítají z `Medevio/.env`:
|
||||
TELEGRAM_BOT_TOKEN=123456789:AAE...
|
||||
TELEGRAM_CHAT_ID=6639316354
|
||||
|
||||
Použití ze skriptu:
|
||||
from Knihovny.telegram_notify import posli_telegram, zeptej_se_telegram
|
||||
|
||||
posli_telegram("Pipeline 08 hotová, 142 záznamů")
|
||||
|
||||
odpoved = zeptej_se_telegram("Mám reimportovat i archiv? (ano/ne)")
|
||||
if odpoved and odpoved.strip().lower() == "ano":
|
||||
...
|
||||
|
||||
Použití z příkazové řádky:
|
||||
python -m Knihovny.telegram_notify "Hotovo"
|
||||
python -m Knihovny.telegram_notify --ask "Pokracovat? (ano/ne)"
|
||||
|
||||
POZN.: getUpdates smí v jednu chvíli pollovat jen JEDEN proces. Pokud běží
|
||||
víc skriptů naráz, které čekají na odpověď, kradou si navzájem zprávy —
|
||||
v praxi se ptá vždy jen jeden agent.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
# =========================
|
||||
# Načtení .env (Medevio/.env)
|
||||
# =========================
|
||||
def _load_env():
|
||||
env_path = Path(__file__).resolve().parent.parent / "Medevio" / ".env"
|
||||
if env_path.exists():
|
||||
for line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if "=" in line and not line.startswith("#"):
|
||||
k, v = line.split("=", 1)
|
||||
os.environ.setdefault(k.strip(), v.strip())
|
||||
|
||||
|
||||
_load_env()
|
||||
|
||||
|
||||
API_BASE = "https://api.telegram.org/bot{token}/{method}"
|
||||
|
||||
|
||||
def _token() -> str:
|
||||
token = os.environ.get("TELEGRAM_BOT_TOKEN")
|
||||
if not token:
|
||||
raise RuntimeError("Chybí TELEGRAM_BOT_TOKEN v Medevio/.env")
|
||||
return token
|
||||
|
||||
|
||||
def _resolve_chat_id(chat_id: str | None) -> str:
|
||||
chat_id = chat_id or os.environ.get("TELEGRAM_CHAT_ID")
|
||||
if not chat_id:
|
||||
raise RuntimeError("Chybí TELEGRAM_CHAT_ID (zadej argumentem nebo v Medevio/.env)")
|
||||
return str(chat_id)
|
||||
|
||||
|
||||
def _call(method: str, *, http_timeout: int = 15, **params):
|
||||
"""Zavolá Telegram Bot API metodu a vrátí pole `result`."""
|
||||
url = API_BASE.format(token=_token(), method=method)
|
||||
r = requests.post(url, json=params, timeout=http_timeout)
|
||||
data = r.json()
|
||||
if not data.get("ok"):
|
||||
raise RuntimeError(f"Telegram {method} selhal [{r.status_code}]: {data}")
|
||||
return data["result"]
|
||||
|
||||
|
||||
def posli_telegram(
|
||||
text: str,
|
||||
*,
|
||||
chat_id: str | None = None,
|
||||
parse_mode: str | None = None,
|
||||
disable_notification: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Pošle zprávu přes Telegram bota.
|
||||
|
||||
:param text: text zprávy (max 4096 znaků)
|
||||
:param chat_id: cílový chat; výchozí z TELEGRAM_CHAT_ID
|
||||
:param parse_mode: None | "Markdown" | "MarkdownV2" | "HTML"
|
||||
:param disable_notification: True = tichá zpráva (bez upozornění)
|
||||
:return: odeslaná zpráva (dict z Telegram API)
|
||||
"""
|
||||
params = {
|
||||
"chat_id": _resolve_chat_id(chat_id),
|
||||
"text": text,
|
||||
"disable_notification": disable_notification,
|
||||
}
|
||||
if parse_mode:
|
||||
params["parse_mode"] = parse_mode
|
||||
return _call("sendMessage", **params)
|
||||
|
||||
|
||||
def zeptej_se_telegram(
|
||||
otazka: str,
|
||||
*,
|
||||
chat_id: str | None = None,
|
||||
timeout: int = 300,
|
||||
poll_timeout: int = 30,
|
||||
parse_mode: str | None = None,
|
||||
) -> str | None:
|
||||
"""
|
||||
Pošle otázku a BLOKUJÍCÍ čeká na textovou odpověď uživatele.
|
||||
|
||||
Zahodí starší zprávy a bere jen tu, která přijde PO odeslání otázky.
|
||||
|
||||
:param otazka: text otázky
|
||||
:param chat_id: cílový chat; výchozí z TELEGRAM_CHAT_ID
|
||||
:param timeout: celkové čekání na odpověď v sekundách (pak vrátí None)
|
||||
:param poll_timeout: délka jednoho long-poll cyklu v sekundách
|
||||
:param parse_mode: formátování otázky (None | "HTML" | "Markdown")
|
||||
:return: text odpovědi, nebo None když nikdo neodpoví do timeoutu
|
||||
"""
|
||||
cid = _resolve_chat_id(chat_id)
|
||||
|
||||
# Zjisti poslední update_id, ať bereme jen NOVÉ zprávy po otázce.
|
||||
existujici = _call("getUpdates", http_timeout=15)
|
||||
offset = (existujici[-1]["update_id"] + 1) if existujici else 0
|
||||
|
||||
posli_telegram(otazka, chat_id=cid, parse_mode=parse_mode)
|
||||
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
zbyva = int(deadline - time.monotonic())
|
||||
if zbyva <= 0:
|
||||
break
|
||||
lp = max(1, min(poll_timeout, zbyva))
|
||||
updates = _call("getUpdates", http_timeout=lp + 10, offset=offset, timeout=lp)
|
||||
for u in updates:
|
||||
offset = u["update_id"] + 1
|
||||
msg = u.get("message") or {}
|
||||
if str(msg.get("chat", {}).get("id")) != cid:
|
||||
continue # zpráva z jiného chatu — ignoruj
|
||||
text = msg.get("text")
|
||||
if text:
|
||||
return text
|
||||
return None
|
||||
|
||||
|
||||
def _safe_print(text: str):
|
||||
"""Výpis odolný vůči kódování Windows konzole (cp1252)."""
|
||||
try:
|
||||
print(text)
|
||||
except UnicodeEncodeError:
|
||||
print(text.encode("ascii", "replace").decode("ascii"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ať projdou i diakritika/emoji na Windows konzoli.
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
args = sys.argv[1:]
|
||||
|
||||
if not args:
|
||||
print('Použití:')
|
||||
print(' python -m Knihovny.telegram_notify "text zprávy"')
|
||||
print(' python -m Knihovny.telegram_notify --ask "otázka?"')
|
||||
sys.exit(1)
|
||||
|
||||
if args[0] == "--ask":
|
||||
otazka = " ".join(args[1:]) or "?"
|
||||
odpoved = zeptej_se_telegram(otazka, timeout=240)
|
||||
if odpoved is None:
|
||||
_safe_print("(bez odpovědi — vypršel timeout)")
|
||||
sys.exit(2)
|
||||
_safe_print(odpoved)
|
||||
else:
|
||||
posli_telegram(" ".join(args))
|
||||
_safe_print("Odesláno OK")
|
||||
Reference in New Issue
Block a user