From af04d97de95cb4cc40e7d45b16ec2e71a76b015c Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Tue, 1 Sep 2026 19:41:41 +0000 Subject: [PATCH] =?UTF-8?q?fix(maria):=20=E2=80=9Eam=20trimis=20la=20supor?= =?UTF-8?q?t"=20doar=20cand=20puntea=20chiar=20a=20trimis,=20si=20un=20log?= =?UTF-8?q?=20care=20nu=20minte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gasite citind logul rularii de test de azi. 1. `requests` NU ridica exceptie la 4xx/5xx, iar puntea raspunde 503 cand nu e conectata la WhatsApp si 500 cand `sendMessage` cade. send_reply/send_image ignorau codul, deci escaladarea se inregistra `notified: true` si omul primea „te contacteaza cineva" pentru un mesaj care nu plecase nicaieri — exact promisiunea pentru care exista ESCALATED_RECORDED. Acum trimiterea intoarce motivul esecului ("" la reusita), iar `notified` si `notify_error` vin de acolo. 2. Puntea nu loga nimic la trimitere: o escaladare nu lasa nicio urma pe partea de WhatsApp, deci nu se poate verifica daca captura chiar a ajuns la suport. /send si /send-image logheaza acum destinatarul si inceputul mesajului. 3. Mesajele primite se logau taiate la 80 de caractere, fara semn ca sunt taiate. Un mesaj de exact 80 arata ca unul intreg — asa am ajuns azi la concluzia gresita ca gardul de „mesaj prea vag" nu functioneaza, cand de fapt mesajul era mai lung decat parea. 200 de caractere si „… (+N)". 4. La o captura pe un fir deschis se logau doua linii „caut dupa" diferite, iar prima nu era interogarea folosita. Prima zice acum „din captura, retin". Teste: 103 pass (una noua: puntea respinge cu 503 -> notified false). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY --- .../maria-whatsapp-bridge/rag/consumer.py | 67 +++++++++++-------- .../tests/test_escaladare.py | 20 ++++++ .../maria-whatsapp-bridge/whatsapp/index.js | 9 ++- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/consumer.py b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/consumer.py index 861d696..cb054b7 100644 --- a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/consumer.py +++ b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/consumer.py @@ -187,16 +187,34 @@ def ask_llm(chunks: list[str], question: str, istoric: list[dict] | None = None) return resp.json()["choices"][0]["message"]["content"] -def send_reply(to: str, text: str) -> None: - requests.post(f"{bridge_url()}/send", json={"to": to, "text": REPLY_PREFIX + text}, timeout=15) +def _trimite(url: str, corp: dict, catre: str, timeout: int) -> str: + """Trimite prin punte. Intoarce "" daca a plecat, altfel motivul esecului. + + `requests` NU ridica exceptie la 4xx/5xx, iar puntea raspunde 503 cand nu e + conectata la WhatsApp si 500 cand `sendMessage` cade. Fara verificarea + codului, escaladarea se inregistra ca „notificata" si Maria promitea „te + contacteaza cineva" pentru un mesaj care nu plecase nicaieri — exact + promisiunea pe care ESCALATED_RECORDED exista ca sa n-o facem. + """ + try: + resp = requests.post(url, json=corp, timeout=timeout) + except Exception as exc: # noqa: BLE001 + print(f"[consumer] trimitere esuata catre {catre}: {exc}", file=sys.stderr) + return str(exc) + if resp.status_code >= 400: + motiv = f"HTTP {resp.status_code}: {resp.text[:150]}" + print(f"[consumer] trimitere esuata catre {catre}: {motiv}", file=sys.stderr) + return motiv + return "" -def send_image(to: str, path: str, caption: str) -> None: - requests.post( - f"{bridge_url()}/send-image", - json={"to": to, "path": path, "caption": caption}, - timeout=60, - ) +def send_reply(to: str, text: str) -> str: + return _trimite(f"{bridge_url()}/send", {"to": to, "text": REPLY_PREFIX + text}, to, 15) + + +def send_image(to: str, path: str, caption: str) -> str: + return _trimite(f"{bridge_url()}/send-image", + {"to": to, "path": path, "caption": caption}, to, 60) def react_seen(to: str, message_id: str, from_me: bool) -> None: @@ -269,7 +287,9 @@ def prepare_query(msg: dict) -> tuple[str | None, str | None, str | None]: file=sys.stderr, ) cautare = ocr.retrieval_query(text, ocr_text) - print(f"[consumer] caut dupa: {cautare!r}", file=sys.stderr) + # „din captura", nu „caut dupa": pe un fir, interogarea finala e alta (ancora + # plus asta) si se logheaza separat — doua linii „caut dupa" induceau in eroare. + print(f"[consumer] din captura, retin: {cautare!r}", file=sys.stderr) return build_image_question(text, ocr_text), cautare, None @@ -323,15 +343,14 @@ def escalate(msg: dict, question: str, search_result: dict, fir: dict | None = N f"{(msg.get('ocr_text') or msg.get('text') or '').strip()[:1200]}" + (f"\n\n--- discutia de pana acum ---\n{discutie}" if discutie else "") ) - try: - if media.get("path") and os.path.exists(media["path"]): - send_image(jid, media["path"], rezumat[:1000]) - else: - requests.post(f"{bridge_url()}/send", json={"to": jid, "text": rezumat}, timeout=20) - record["notified"] = True - except Exception as exc: # noqa: BLE001 - record["notify_error"] = str(exc) - print(f"[consumer] escaladare: notificarea a esuat: {exc}", file=sys.stderr) + if media.get("path") and os.path.exists(media["path"]): + eroare = send_image(jid, media["path"], rezumat[:1000]) + else: + eroare = _trimite(f"{bridge_url()}/send", {"to": jid, "text": rezumat}, jid, 20) + record["notified"] = not eroare + if eroare: + record["notify_error"] = eroare + print(f"[consumer] escaladare: notificarea a esuat: {eroare}", file=sys.stderr) else: print("[consumer] escaladare: SUPPORT_JID nesetat — doar in jurnal", file=sys.stderr) @@ -362,15 +381,9 @@ def _notifica_suport(text: str, imagine: str | None = None) -> bool: jid = config.get("SUPPORT_JID") or "" if not jid: return False - try: - if imagine and os.path.exists(imagine): - send_image(jid, imagine, text[:1000]) - else: - requests.post(f"{bridge_url()}/send", json={"to": jid, "text": text}, timeout=20) - return True - except Exception as exc: # noqa: BLE001 - print(f"[consumer] mesaj catre suport netrimis: {exc}", file=sys.stderr) - return False + if imagine and os.path.exists(imagine): + return not send_image(jid, imagine, text[:1000]) + return not _trimite(f"{bridge_url()}/send", {"to": jid, "text": text}, jid, 20) def completeaza(ref: str, msg: dict, text: str) -> str | None: diff --git a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_escaladare.py b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_escaladare.py index e662ca3..8764a7a 100644 --- a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_escaladare.py +++ b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_escaladare.py @@ -124,3 +124,23 @@ def test_notificarea_esuata_nu_pierde_jurnalul(rezultat_fara_acoperire, monkeypa inregistrare = json.loads(next((config.STATE_DIR / "escalations").glob("*.json")).read_text()) assert inregistrare["notified"] is False assert "puntea nu raspunde" in inregistrare["notify_error"] + + +def test_puntea_respinge_mesajul_nu_promitem_ca_a_plecat(rezultat_fara_acoperire, monkeypatch): + """503 „not connected to WhatsApp" nu ridica exceptie in requests — dar nu e trimitere. + + Fara verificarea codului, escaladarea se scria „notificata" si omul primea + „te contacteaza cineva" pentru un mesaj care nu plecase nicaieri. + """ + monkeypatch.setitem(config._env, "SUPPORT_JID", "40799999999@s.whatsapp.net") + + class Respins: + status_code = 503 + text = '{"ok":false,"error":"not connected to WhatsApp"}' + + monkeypatch.setattr(consumer.requests, "post", lambda *a, **kw: Respins()) + consumer.escalate(_mesaj(), "intrebare", rezultat_fara_acoperire) + + inregistrare = json.loads(next((config.STATE_DIR / "escalations").glob("*.json")).read_text()) + assert inregistrare["notified"] is False + assert "503" in inregistrare["notify_error"] diff --git a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/whatsapp/index.js b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/whatsapp/index.js index 6986e64..eaec630 100644 --- a/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/whatsapp/index.js +++ b/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/whatsapp/index.js @@ -285,7 +285,12 @@ async function startConnection() { fromMe: msg.key.fromMe || false, }); - console.log(`[whatsapp] Message from ${msg.pushName || 'unknown'} in ${msg.key.remoteJid}: ${media ? '[imagine] ' : ''}${text.substring(0, 80)}`); + // Taiat, dar marcat: 80 de caractere fix aratau ca un mesaj intreg, iar + // logul e primul loc in care te uiti cand un raspuns iese prost. Pe + // 2026-09-01 asta a dus la concluzia gresita ca gardul de „mesaj prea vag" + // nu functioneaza, cand de fapt mesajul era mai lung decat parea in log. + const scurt = text.length > 200 ? `${text.substring(0, 200)}… (+${text.length - 200})` : text; + console.log(`[whatsapp] Message from ${msg.pushName || 'unknown'} in ${msg.key.remoteJid}: ${media ? '[imagine] ' : ''}${scurt}`); } }); } @@ -372,6 +377,7 @@ app.post('/send', async (req, res) => { try { const result = await sock.sendMessage(to, { text }); + console.log(`[whatsapp] -> ${to}: ${text.substring(0, 120)}${text.length > 120 ? '…' : ''}`); res.json({ ok: true, id: result.key.id }); } catch (err) { console.error('[whatsapp] Send failed:', err.message); @@ -398,6 +404,7 @@ app.post('/send-image', async (req, res) => { try { const buffer = fs.readFileSync(resolved); const result = await sock.sendMessage(to, { image: buffer, caption: caption || undefined }); + console.log(`[whatsapp] -> ${to}: [imagine ${buffer.length} octeti] ${(caption || '').substring(0, 100)}`); res.json({ ok: true, id: result.key.id }); } catch (err) { console.error('[whatsapp] Send image failed:', err.message);