Spaces:
Paused
Paused
Download patch_missing_defs.py from bep40/zalo-bot-webhook: direct link, hf CLI and curl.
- Browser
- Download file 5.79 kB
-
https://huggingface.co/spaces/bep40/zalo-bot-webhook/resolve/main/patch_missing_defs.py
- Command line
-
hf download hf://spaces/bep40/zalo-bot-webhook/patch_missing_defs.py
-
curl -L -o patch_missing_defs.py https://huggingface.co/spaces/bep40/zalo-bot-webhook/resolve/main/patch_missing_defs.py
5.79 kB
| #!/usr/bin/env python3 | |
| """patch_missing_defs.py — insert missing `_bstate_log` into app.py. | |
| Fixes (verified from live build log): | |
| NameError: name '_bstate_log' is not defined (app.py:5574) | |
| -> every POST /webhooks returned 500 Internal Server Error, so the main | |
| bot NEVER replied and webhook tests (webhook.test) failed. | |
| Strategy (same proven pattern as patch_card_strings.py): | |
| - Fixes the LOCAL on-disk file first (running app uses the fix NOW). | |
| - If anything changed, commits the fixed file back to THIS Space's repo | |
| (SELF_REPO_ID env) -> repo converges after ONE rebuild, no loop. | |
| - Also installs a PEP 562 module-level __getattr__ that auto-provides any | |
| other missing `_bstate*` helper (fail-open) instead of NameError. | |
| - Idempotent & fail-open: never blocks the app from starting. | |
| """ | |
| import os | |
| import sys | |
| import tempfile | |
| import pathlib | |
| FILES = ["app.py"] | |
| _FIX_BLOCK = '''def _bstate_log(state): | |
| """Log a bot-state event dict. Fail-open: never raises. | |
| Called at the top of handle_webhook; if missing, the app crashes with | |
| NameError and every webhook returns 500. Mirrors the child-template log | |
| semantics: append to in-memory _logs + print a [WEBHOOK] line so the | |
| main.py journal bridge persists it to the registry dataset. | |
| """ | |
| try: | |
| if not isinstance(state, dict): | |
| state = {"data": str(state)} | |
| # Redact anything sensitive before persisting | |
| _red = {} | |
| for _k, _v in state.items(): | |
| if re.search(r"token|secret|key|hash|password", str(_k), re.I): | |
| _red[str(_k)] = "***" | |
| else: | |
| _red[str(_k)] = _v | |
| _entry = { | |
| "event": str(state.get("event", "bstate"))[:40], | |
| "sender_id": str(state.get("sender_id", state.get("sender", "system")) or "system"), | |
| "sender_name": str(state.get("sender_name", "") or ""), | |
| "chat_id": str(state.get("chat_id", state.get("chat", "SYSTEM")) or "SYSTEM"), | |
| "chat_type": str(state.get("chat_type", "") or ""), | |
| "text": json.dumps(_red, ensure_ascii=False)[:500], | |
| "is_zgr": bool(state.get("is_zgr", False)), | |
| "time": time.strftime("%Y-%m-%d %H:%M:%S"), | |
| } | |
| try: | |
| _logs.append(_entry) | |
| if len(_logs) > 200: | |
| del _logs[:100] | |
| except Exception: | |
| pass | |
| print("[WEBHOOK] event=" + str(_entry["event"]) + " sender=" + str(_entry["sender_id"]) + | |
| " chat=" + str(_entry["chat_id"]) + " is_zgr=" + str(_entry["is_zgr"]) + | |
| " text=" + str(_entry["text"])[:100], flush=True) | |
| except Exception: | |
| pass | |
| _BSTATE_STATE = {} | |
| def _bstate_get(key, default=None): | |
| """Read one key from the in-memory bot-state dict (fail-open).""" | |
| try: | |
| return _BSTATE_STATE.get(key, default) | |
| except Exception: | |
| return default | |
| def _bstate_set(key, value): | |
| """Store one key/value in the in-memory bot-state dict (fail-open).""" | |
| try: | |
| _BSTATE_STATE[key] = value | |
| except Exception: | |
| pass | |
| def __getattr__(name): | |
| """PEP 562: auto-provide any OTHER missing `_bstate_*` helper as a no-op | |
| logger so one forgotten call site can never 500 the webhook again. | |
| All other undefined names still raise AttributeError -> real bugs stay | |
| visible. | |
| """ | |
| if name.startswith("_bstate"): | |
| return _bstate_log | |
| raise AttributeError(name) | |
| ''' | |
| def main(): | |
| rc = 0 | |
| try: | |
| repo_id = os.getenv("SELF_REPO_ID", "").strip() | |
| token = os.getenv("HF_TOKEN", os.getenv("HF_API_TOKEN", "")).strip() | |
| changed = False | |
| for fname in FILES: | |
| if not os.path.exists(fname): | |
| continue | |
| with open(fname, encoding="utf-8") as f: | |
| content = f.read() | |
| if "def _bstate_log(" in content: | |
| print("[patch-defs] already fixed:", fname, flush=True) | |
| continue | |
| anchor = "def handle_webhook(" | |
| idx = content.find(anchor) | |
| if idx < 0: | |
| anchor = "async def handle_webhook(" | |
| idx = content.find(anchor) | |
| if idx < 0: | |
| print("[patch-defs] WARNING: handle_webhook anchor not found in", fname, flush=True) | |
| continue | |
| content = content[:idx] + _FIX_BLOCK + content[idx:] | |
| with open(fname, "w", encoding="utf-8") as f: | |
| f.write(content) | |
| print("[patch-defs] fixed locally:", fname, flush=True) | |
| changed = True | |
| if repo_id and token: | |
| try: | |
| from huggingface_hub import HfApi | |
| api = HfApi(token=token) | |
| with tempfile.TemporaryDirectory() as tmpdir: | |
| tmp = pathlib.Path(tmpdir) | |
| (tmp / fname).write_text(content, encoding="utf-8") | |
| api.upload_folder( | |
| repo_id=repo_id, | |
| repo_type="space", | |
| folder_path=str(tmp), | |
| token=token, | |
| commit_message="autofix: define missing _bstate_log in app.py", | |
| ) | |
| print("[patch-defs] committed", fname, "->", repo_id, flush=True) | |
| except Exception as e: | |
| print("[patch-defs] commit failed (local fix still applied):", str(e)[:200], flush=True) | |
| if not changed: | |
| print("[patch-defs] nothing to do — exiting clean", flush=True) | |
| except Exception as e: | |
| print("[patch-defs] FATAL (ignored, fail-open):", str(e)[:200], flush=True) | |
| rc = 0 | |
| return rc | |
| if __name__ == "__main__": | |
| sys.exit(main()) |