Spaces:
Paused
Paused
File size: 5,794 Bytes
6925123 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | #!/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()) |