Spaces:
Sleeping
Sleeping
| """ | |
| Minimal HF Space for testing ONLY (no bot logic). | |
| Deploy this as app.py in a new, throwaway HF Space (SDK: Gradio). | |
| requirements.txt for this Space: | |
| gradio | |
| httpx | |
| """ | |
| import asyncio | |
| import json | |
| import os | |
| import threading | |
| from datetime import datetime, timezone | |
| import gradio as gr | |
| import httpx | |
| # --- CONFIG: edit these two lines -------------------------------------- | |
| NORTHFLANK_BASE_URL = os.environ.get("NORTHFLANK_TEST_URL", "https://your-service.northflank.app") | |
| TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") # optional | |
| INTERVAL_SECONDS = 300 | |
| # ------------------------------------------------------------------------- | |
| LOG_FILE = "connectivity_log.jsonl" | |
| results_lock = threading.Lock() | |
| latest_results = [] # kept in memory for the UI | |
| async def check(name: str, url: str, timeout: float = 10.0) -> dict: | |
| entry = {"check": name, "url": url, "time_utc": datetime.now(timezone.utc).isoformat()} | |
| try: | |
| async with httpx.AsyncClient(timeout=timeout) as client: | |
| resp = await client.get(url) | |
| entry["status"] = "ok" if resp.status_code == 200 else "http_error" | |
| entry["http_status"] = resp.status_code | |
| except Exception as e: | |
| entry["status"] = "connect_error" | |
| entry["error"] = f"{type(e).__name__}: {e}" | |
| return entry | |
| async def run_once() -> list[dict]: | |
| checks = [ | |
| check("hf_to_northflank_ping", f"{NORTHFLANK_BASE_URL}/ping"), | |
| check("hf_to_northflank_telegram_relay", f"{NORTHFLANK_BASE_URL}/check-telegram"), | |
| ] | |
| if TELEGRAM_BOT_TOKEN: | |
| checks.append( | |
| check("hf_to_telegram_direct", f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/getMe") | |
| ) | |
| return await asyncio.gather(*checks) | |
| def append_log(entries: list[dict]) -> None: | |
| with open(LOG_FILE, "a", encoding="utf-8") as f: | |
| for entry in entries: | |
| f.write(json.dumps(entry, ensure_ascii=False) + "\n") | |
| def background_loop(): | |
| async def loop(): | |
| while True: | |
| results = await run_once() | |
| append_log(results) | |
| with results_lock: | |
| latest_results.clear() | |
| latest_results.extend(results) | |
| await asyncio.sleep(INTERVAL_SECONDS) | |
| asyncio.run(loop()) | |
| def get_status_text() -> str: | |
| with results_lock: | |
| if not latest_results: | |
| return "لسه ما بدأ أول فحص... انتظر بضع ثواني وحدّث الصفحة." | |
| lines = [] | |
| for r in latest_results: | |
| line = f"**{r['check']}**: `{r['status']}`" | |
| if "http_status" in r: | |
| line += f" (HTTP {r['http_status']})" | |
| if "error" in r: | |
| line += f" — {r['error']}" | |
| line += f" \n_{r['time_utc']}_" | |
| lines.append(line) | |
| return "\n\n".join(lines) | |
| def get_log_tail() -> str: | |
| if not os.path.exists(LOG_FILE): | |
| return "لا يوجد سجل بعد." | |
| with open(LOG_FILE, "r", encoding="utf-8") as f: | |
| lines = f.readlines() | |
| return "".join(lines[-30:]) or "لا يوجد سجل بعد." | |
| threading.Thread(target=background_loop, daemon=True).start() | |
| with gr.Blocks(title="Connectivity Test: HF <-> Northflank <-> Telegram") as demo: | |
| gr.Markdown("## اختبار الاتصال الدوري: HF ↔ Northflank ↔ تيليجرام") | |
| gr.Markdown(f"يفحص كل {INTERVAL_SECONDS} ثانية. اضغط تحديث لرؤية آخر نتيجة.") | |
| status_box = gr.Markdown() | |
| refresh_btn = gr.Button("تحديث النتائج") | |
| log_box = gr.Textbox(label="آخر 30 سطر من السجل", lines=15) | |
| refresh_btn.click(fn=get_status_text, outputs=status_box) | |
| refresh_btn.click(fn=get_log_tail, outputs=log_box) | |
| demo.load(fn=get_status_text, outputs=status_box) | |
| demo.load(fn=get_log_tail, outputs=log_box) | |
| demo.launch() | |