From b87d97e7733c0a1010a236d0b171a512743e9133 Mon Sep 17 00:00:00 2001 From: Ekaropolus Date: Thu, 18 Sep 2025 15:11:38 -0600 Subject: [PATCH] Log in router --- .gitignore | 1 + polisplexity/settings.py | 25 +++++++++++++++++++++ pxy_bots.zip | Bin 44903 -> 44903 bytes pxy_bots/router.py | 46 +++++++++++++++++++++++++++++++++------ pxy_contracts.zip | Bin 4619 -> 0 bytes pxy_de.zip | Bin 20297 -> 0 bytes pxy_routing.zip | Bin 5580 -> 0 bytes 7 files changed, 65 insertions(+), 7 deletions(-) delete mode 100644 pxy_contracts.zip delete mode 100644 pxy_de.zip delete mode 100644 pxy_routing.zip diff --git a/.gitignore b/.gitignore index 8f17090..b4dbcab 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ pxy_bots.zip pxy_bots (2).zip pxy_bots.zip pxy_bots.zip +pxy_bots.zip diff --git a/polisplexity/settings.py b/polisplexity/settings.py index db6754d..66a1cc1 100644 --- a/polisplexity/settings.py +++ b/polisplexity/settings.py @@ -226,3 +226,28 @@ REST_FRAMEWORK = { } AGENTS_INTERNAL_BASE = "http://127.0.0.1:8000" + +LOGGING = { + # ... keep your existing handlers/formatters ... + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + }, + "loggers": { + "pxy_bots": { # package logger + "handlers": ["console"], + "level": "INFO", # or "DEBUG" + "propagate": False, + }, + # make sure __name__ in router resolves here; if your module path is different, use that + "pxy_bots.router": { + "handlers": ["console"], + "level": "INFO", + "propagate": False, + }, + }, +} + diff --git a/pxy_bots.zip b/pxy_bots.zip index 6823993ccb9b60406224633c0e92efa872456c8d..89c555bcb4f106c1135011aec1e902cb0eb8b22d 100644 GIT binary patch delta 97 zcmaEUkLme6rU`w_j|$IhoLJ4soGW@}Ge4800Z3r8woxG?m@(hj!WGQ8Rq+ENaCM_kg^Xavd}9k&FymIm4~WRs dDqbeA$kQ5W2!pwv3BurL43Pv&#n0jp1ppfaBC!Af diff --git a/pxy_bots/router.py b/pxy_bots/router.py index 800a483..08cdc35 100644 --- a/pxy_bots/router.py +++ b/pxy_bots/router.py @@ -1,6 +1,7 @@ # pxy_bots/router.py import json import logging +import time # <-- add from typing import Dict, Optional, Tuple from urllib.parse import urlparse @@ -45,26 +46,36 @@ def pick_db_route(bot_name: str, canon: Dict) -> Optional[Dict]: try: # Lazy import to avoid circulars at startup from .models import CommandRoute, Connection, TelegramBot # noqa + bot = TelegramBot.objects.filter(name=bot_name, is_active=True).first() if not bot: + logger.info("router.no_bot bot=%s", bot_name) return None trigger = ((canon.get("command") or {}).get("trigger")) or "message" cmd = ((canon.get("command") or {}).get("name")) or None cmd = (cmd or "").strip().lstrip("/").lower() or None + logger.info("router.lookup bot=%s trigger=%s cmd=%s", bot.username, trigger, cmd or "") + qs = ( CommandRoute.objects .select_related("connection") .filter(bot=bot, enabled=True, connection__is_active=True, trigger=trigger) .order_by("priority", "id") ) + # Log a small snapshot of candidates + snapshot = list(qs.values("command_name", "trigger", "priority", "path", "connection__name")[:10]) + logger.info("router.candidates n=%s sample=%s", qs.count(), snapshot) # Prefer exact command; then default (blank/null) - route = qs.filter(command_name=cmd).first() \ - or qs.filter(command_name__isnull=True).first() \ + route = ( + qs.filter(command_name=cmd).first() + or qs.filter(command_name__isnull=True).first() or qs.filter(command_name="").first() + ) if not route: + logger.info("router.no_match bot=%s trigger=%s cmd=%s", bot.username, trigger, cmd or "") return None conn: Connection = route.connection @@ -72,14 +83,22 @@ def pick_db_route(bot_name: str, canon: Dict) -> Optional[Dict]: ok, why = _is_allowed(url, conn.allowed_host_set()) if not ok: - logger.warning("router.db.reject url=%s reason=%s allowed=%s", url, why, conn.allowed_host_set()) + logger.warning( + "router.db.reject url=%s reason=%s allowed=%s route_conn=%s", + url, why, conn.allowed_host_set(), conn.name + ) return None headers = {} headers.update(conn.auth_headers()) headers.update(conn.extra_headers()) + logger.info( + "router.route_ok bot=%s trigger=%s cmd=%s url=%s conn=%s timeout=%ss", + bot.username, trigger, cmd or "", url, conn.name, conn.timeout_s + ) return {"url": url, "headers": headers, "timeout": conn.timeout_s} + except Exception as e: logger.exception("router.db.error: %s", e) return None @@ -96,28 +115,41 @@ def post_json(url: str, payload: Dict, timeout: float = 4.0, headers: Optional[D """ hdrs = {"Content-Type": "application/json", **(headers or {})} data = json.dumps(payload, ensure_ascii=False).encode("utf-8") + t0 = time.perf_counter() if _HAS_REQUESTS: try: r = requests.post(url, data=data, headers=hdrs, timeout=timeout) + dt = (time.perf_counter() - t0) * 1000 try: body = r.json() + preview = str(body)[:200].replace("\n", " ") except Exception: body = {"text": r.text[:2000]} + preview = body["text"][:200].replace("\n", " ") + logger.info("router.http requests url=%s status=%s t_ms=%.1f body~=%s", url, r.status_code, dt, preview) return r.status_code, body except Exception as e: - logger.exception("router.requests_failed url=%s", url) + dt = (time.perf_counter() - t0) * 1000 + logger.exception("router.requests_failed url=%s t_ms=%.1f err=%s", url, dt, e.__class__.__name__) return 502, {"ok": False, "error": f"requests_failed:{e.__class__.__name__}"} else: try: req = urllib.request.Request(url, data=data, headers=hdrs, method="POST") with urllib.request.urlopen(req, timeout=timeout) as resp: # nosec raw = resp.read(65536) + dt = (time.perf_counter() - t0) * 1000 try: body = json.loads(raw.decode("utf-8")) + preview = str(body)[:200].replace("\n", " ") except Exception: - body = {"text": raw.decode("utf-8", errors="replace")[:2000]} - return getattr(resp, "status", 200), body + txt = raw.decode("utf-8", errors="replace")[:2000] + body = {"text": txt} + preview = txt[:200].replace("\n", " ") + status = getattr(resp, "status", 200) + logger.info("router.http urllib url=%s status=%s t_ms=%.1f body~=%s", url, status, dt, preview) + return status, body except Exception as e: - logger.exception("router.urllib_failed url=%s", url) + dt = (time.perf_counter() - t0) * 1000 + logger.exception("router.urllib_failed url=%s t_ms=%.1f err=%s", url, dt, e.__class__.__name__) return 502, {"ok": False, "error": f"urllib_failed:{e.__class__.__name__}"} diff --git a/pxy_contracts.zip b/pxy_contracts.zip deleted file mode 100644 index b1d0ba76d5f629b1f000ee49554c4dade6d8456c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4619 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 diff --git a/pxy_routing.zip b/pxy_routing.zip deleted file mode 100644 index 4e4b3e282c5d14d413a8720ed7ef851049a505ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5580 zcmb7I2|UyNAD?5cA-TImj>$QS+T5w(a-~5uD_X?vVj=?@p^_9DQ`jpr}4i?ouTANJQPT-_IMA>U-qt> zLR#uKu0sOnH%KNB6R3{@Qk)v(UwEhU4(GgVobgT`7?eF;#@TB#;toV~IFUGT2AU4y zx({*qE}V#H4Uc@a|6Z0>{2?uA>BKH`>{hMgDC~Qe(l+nL0jmo7D-9a6&vwstK-Go( z>k?sS#}SP5UUv_uRP1wXuY{RuCG+0TXUpE(RJv#65Zb^{Nq2xD@Vra6-c)DZ1;KrZ zPh8au-j)_|i#babLdMLK8%?`gIzI0iW7@mr@?XPIy7k?PIAd|n8+l%A0fHuVmM;&u z`Q&Dgo2nFfIZPZKLw})tYr`MZwiKVb(#ndVxg35WE~^QQGUY^-sl@&-%(emSs8P_G z%Laz+No=_9WPDuAH6|xkepkJAh+f`V9bQFb#hdBI$hDcOSQZH+{kxN;oqBCKEDD*! zbaC&R(!t9U9q7^;_PU)AG4nH%UHeOO!}t}`YMC*opU}+q)%4KWo2I2R#ow9fo?YpD z8k-8UR#v}Qw%xTzwCL>g6&g`9)2!zcTzWc%CrxG(2FwZ7>l@JM5tm zF=E?4!ykwrG0V3Wp_|qhjvKy%2`ErtyIFrmz2A9@R@x}`R<^pd!9Rqqo`O5h%u&U? zZ~ZLH5)w+~!f8?n%qV>Z2!mybXoo)Edx2x`SD6jGh7dv%-pd-EKc?@x|(5TZUMKT@)v(muEde;w6RU4_16gbem0lx&wc`)_KzU?v0`($=(P7WRH=pPlfLjvHDk` zC$1>mj(M%8>b|U>SmS+sA#6|;;YZ6>a-z99zmn@s#&l?llR0-`*VE};QWpmN{Up*t zStGZOsUN7;EtWMjQjELtc6aG?5Bt}uAZ!0%@nt@k{zHfCa;*^&OOM5!?Kw}!zfNj! zT0MVu!_!6Iwl!wgf?HRrep&2DAtL?``5f!T?Su$5Hd4m>)k?S6(A4&g7TgV{Z8o zp{IKuibX4yiInMWU90LI@(Zz^w`FHWTEF(06B9EAKhMHcGFP6yTzH0sb->52E#ZQG zt3X}Vz~bkUW$utX!Ngiu6O~q5l(3^S7Sdn5ETelgS?Aqw23>2Uuyod!lOnpqZV|Ao zpg_|mYi#JP^C*KkdQ?2xHeldmHecs;?#0ByI#oQ{jCU9AaU8-Vj#ssZvAn(;8CU>s z^A^0ybpggN0%hBg_mck8%NCSzx+CjBH))iy#T(eOodx4I$sMb$ED8^XEK4Vbgw$Ha zMjn-&()VAS)Y^8S-4~K=!m(WFfy)Yi|DajSj#+X)H^=L9FQX@pR7Jj>ca~34PcvQR zbeKr?JfX8KKHw7pZl8{pJRrwuKP8bK^=?q!<*}gCjOFU_JoQzf*Xh{9{9JENe9F3` zX5hs|8ib2Ol_GjBu|chqxymN#6KEl#Qo{^J zD+-9oL57{@2mJ-E3D7%Sym_M96~|D5F$;Ay9&=PtEV7#`!7p7? zvk6lgZ@$Q7xc1Lk!U_Wrue4N8gcdPgSIFX(zmo zdPO`ive}p)rpf|gZSEL50v~U=RDR4Wq;1~HweNdTV9wQYu0OpzvzSOBAQyI zn`9-9H?PpKT|IUnwrZkl++~h%J9M6q#1Q#5^BQXaL=RR~Mj7J_3j`&=;&JB94 zZ%LWROZAVqd0_0%|1r0AqQ7)yr1zQ=34-K#!Vq%^M0h0yy%u?s=i#ITSyoIMfrxHI zmG}Wf8C5=ttixg)@klp}6K-?;1|%FH34xP(s_dZ-`X9}QG_mQ{{S1&M*3J4u-6G{R ze!~nP5CL=4%m5ZCM3`fEy;2BTpv3%j?vdv5BV6ir(c`>IIkL(;x55&!dsZ+%LXgR zTrw>_2B$%r?vUKeH}jG9B`(Y8+4#%6R)tS5!Jkanp5bh8;k^COsW$c@kBM_@$LwN! zjFHN!I&=Ap8Eqbc2Y|vXb1C!mA^N9}_}U1{EXDo!H}Tf53LyUb1%Wfmv=%oAW35?| zRQg~?lNUoT38Qbi3lGKy7d3fH(rTAY98Q(?;!G9g|MVf#PQ~SJ`E!_!RP2R!Bf_I~ z{Vb&g=vBx96{Jo|O($y*XeN^ieeFGVS=_erDsfc)5S#(V4Gdm*Ur_6RD+e-> z(anOv@>C~lUsxEO_I<^m`i4%~k*-F2|4Ea-2`fyh#fBr}PS5%X7oim@qIX7%?jcH= zkeOZ1U(^QYQ_QOBFN^CFg2M7EN;q`TQ9ANcE@=_#F#JyR-NJdlcMrYE(M6?i(cqoqw zb_i2)?K1dw(L(S?M8w-xc?%Xr_pY$V90IQ|8OQo-ony*bxz1XVpl;?KYLofjP`PSKmFVjzIZwsx+WK$^StWW)gstgHrW&7lgsPF)*7l^sz7WQ znPgg*vh8vGl$y@J)0WlR)Yyq-2Y#!TGs}At4-yHw+z?TD2<6NP7A=Y2omZnKeZtNmc6&Q-U|EVbfDhEio3pEbu_fxbkufx ze&Szzd)738^Ou`G7)0}J!{0#sdB-P3)T2P8zVQ>Gzis;8pnvT8r2B|CXal