fix(maria): „am trimis la suport" doar cand puntea chiar a trimis, si un log care nu minte

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
Claude Agent
2026-09-01 19:41:41 +00:00
parent 47110d71bf
commit af04d97de9
3 changed files with 68 additions and 28 deletions

View File

@@ -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:

View File

@@ -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"]

View File

@@ -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);