#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ recept_resolver.py — vyřizuje "čekající" žádosti o recept přes Telegram. Jediný proces, který mluví s Telegramem (kvůli getUpdates). Ve smyčce: 1) pošle otázky pro nové pending záznamy (které ještě otázku nemají), 2) long-polluje odpovědi; odpověď (reply na otázku) → podle obsahu: • RČ (9–10 číslic) → definitivní volba pacienta → založí požadavek • číslo kandidáta → vybere kandidáta → založí požadavek • „ne“ → přeskočí (nezakládá) Po založení označí původní e-mail kategorií ClaudeZpracovalRecept. Telegram přenos je v recept_telegram.py (token/chat dodá uživatel). Bez odpovědi záznam zůstává 'ceka' (čeká se libovolně dlouho). Spuštění: python recept_resolver.py """ import re import sys import time from pathlib import Path try: sys.stdout.reconfigure(encoding="utf-8") except Exception: pass HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE.parent / "EmailAgent")) sys.path.insert(0, str(HERE.parent)) import recept_pending as PEND # noqa: E402 import recept_dialog as DLG # noqa: E402 import recept_telegram as TG # noqa: E402 import graph_mail # noqa: E402 (značení mailu) import mcp_medevio as MED # noqa: E402 (zaloz_pozadavek_recept) from Knihovny.mysql_db import connect_mysql # noqa: E402 MAILBOX = "ordinace@buzalkova.cz" PROCESSED_CATEGORY = "ClaudeZpracovalRecept" SINCE_FILE = HERE / "_resolver_since.txt" # poslední zpracované message_id def log(msg: str) -> None: print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True) def _norm_rc(s: str) -> str: return re.sub(r"\D", "", s or "") def najdi_pacienta_dle_rc(rc: str): """RČ → (uuid, jmeno, prijmeni) z medevio_pacient, nebo None.""" rc = _norm_rc(rc) if not rc: return None try: conn = connect_mysql() conn.ping(reconnect=True) cur = conn.cursor() cur.execute( "SELECT patient_id, name, surname FROM medevio_pacient " "WHERE REPLACE(identification_number,'/','') = %s LIMIT 1", [rc], ) row = cur.fetchone() return row if row else None except Exception as e: log(f"[uuid lookup chyba] {type(e).__name__}: {e}") return None def _load_since(): try: return int(SINCE_FILE.read_text(encoding="utf-8").strip()) except Exception: return None def _save_since(s: int) -> None: SINCE_FILE.write_text(str(s), encoding="utf-8") def posli_nove_otazky() -> None: """Pošle otázku do Telegramu pro každý pending záznam bez otázky.""" for rec in PEND.cekajici_bez_otazky(): try: mid = TG.posli_otazku(DLG.format_otazka(rec)) PEND.aktualizuj(rec["id"], otazka_message_id=mid) log(f"otázka odeslána (pending {rec['id'][:8]}, msg {mid})") except Exception as e: log(f"otázku nelze odeslat ({type(e).__name__}: {e}) — zkusím příště") break # nejspíš výpadek / nenakonfigurováno → nech na příští kolo def _zaloz_pro_rc(rec: dict, rc: str) -> None: info = najdi_pacienta_dle_rc(rc) if not info: TG.posli_zpravu(f"⚠ RČ {rc} není v Medeviu — nezakládám. Zkus jiné RČ.") return uuid_, jmeno, prijmeni = info[0], info[1], info[2] try: res = MED.zaloz_pozadavek_recept( uuid_, rec.get("leky_str", ""), rec.get("pozn_str", "") ) except Exception as e: TG.posli_zpravu(f"❌ Chyba při zakládání: {type(e).__name__}: {e}") return PEND.aktualizuj(rec["id"], stav="zalozeno", vysledek={"request_id": res["request_id"], "rc": rc, "pacient": f"{prijmeni} {jmeno}"}) try: graph_mail.add_category(MAILBOX, rec["email_message_id"], PROCESSED_CATEGORY) except Exception as e: log(f"[mail označení] {type(e).__name__}: {e}") TG.posli_zpravu(f"✅ Založeno: {prijmeni} {jmeno} (RČ {rc}) — " f"{rec.get('leky_str', '')}") log(f"založeno {res['request_id']} pro {prijmeni} {jmeno}") def zpracuj_odpoved(u: dict) -> None: """Zpracuje jednu příchozí Telegram zprávu.""" rid = u.get("reply_to_message_id") rec = PEND.najdi_dle_otazky(rid) if rid else None if rec is None: cekaji = PEND.cekajici() if len(cekaji) == 1: rec = cekaji[0] # jediný čekající → ber to na něj else: if cekaji: TG.posli_zpravu("Odpověz prosím jako reply na konkrétní dotaz " "(čeká jich víc).") return if rec.get("stav") != "ceka": return d = DLG.parse_odpoved(u.get("text", "")) if d["akce"] == "preskoc": PEND.aktualizuj(rec["id"], stav="preskoceno") TG.posli_zpravu("OK, nezakládám.") elif d["akce"] == "rc": _zaloz_pro_rc(rec, d["rc"]) elif d["akce"] == "kandidat": kand = rec.get("kandidati") or [] i = d["index"] - 1 if 0 <= i < len(kand): _zaloz_pro_rc(rec, kand[i].get("rc", "")) else: TG.posli_zpravu(f"Kandidát {d['index']} neexistuje (mám {len(kand)}).") else: TG.posli_zpravu("Nerozumím. Pošli RČ pacienta, číslo kandidáta, nebo „ne“.") def smycka(poll_s: int = 5) -> None: try: TG.priprav() # naprimuj entitu Vlada (jinak fresh session spadne) except Exception as e: log(f"[priprav] {type(e).__name__}: {e}") since = _load_since() if since is None: # první start — vezmi aktuální stav jako základ, starou historii ignoruj since = TG.baseline_since() _save_since(since) log(f"baseline since_id={since}") log("Resolver běží (Ctrl+C ukončí).") while True: try: posli_nove_otazky() nove, since = TG.nacti_odpovedi(since) for u in nove: zpracuj_odpoved(u) _save_since(since) except KeyboardInterrupt: log("Konec.") break except Exception as e: log(f"[smyčka] {type(e).__name__}: {e}") time.sleep(poll_s) if __name__ == "__main__": smycka()