""" Deploys two Cloudflare Workers: 1. Keep-alive cron worker — pings the HF Space every 4 minutes 2. Telegram proxy worker — forwards Telegram API calls """ import os, json, requests, time ACCOUNT_ID = os.environ.get("CLOUDFLARE_ACCOUNT_ID") CF_TOKEN = os.environ.get("CLOUDFLARE_WORKERS_TOKEN") SPACE_URL = os.environ.get("SPACE_URL", "https://your-space.hf.space") BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") GATEWAY_TOKEN = os.environ.get("GATEWAY_TOKEN", "changeme") AUTH_HEADERS = {"Authorization": f"Bearer {CF_TOKEN}"} # ── 1. Keep-alive Worker Script ─────────────────────────────────────────────── KEEPALIVE_SCRIPT = f""" addEventListener('scheduled', event => {{ event.waitUntil(handleScheduled()); }}); async function handleScheduled() {{ const url = '{SPACE_URL}/health'; try {{ const res = await fetch(url, {{ method: 'GET' }}); console.log('[keep-alive]', new Date().toISOString(), res.status); }} catch (e) {{ console.error('[keep-alive] failed:', e.message); }} }} """ # ── 2. Telegram Proxy Worker Script ─────────────────────────────────────────── TELEGRAM_PROXY_SCRIPT = """ addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { const url = new URL(request.url); const target = 'https://api.telegram.org' + url.pathname + url.search; const newReq = new Request(target, { method: request.method, headers: request.headers, body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined }); const response = await fetch(newReq); // Pass response back with CORS headers const newHeaders = new Headers(response.headers); newHeaders.set('Access-Control-Allow-Origin', '*'); return new Response(response.body, { status: response.status, headers: newHeaders }); } """ def get_subdomain(): """Get the workers.dev subdomain for this account.""" resp = requests.get( f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workers/subdomain", headers=AUTH_HEADERS ) data = resp.json() if data.get("success"): return data["result"]["subdomain"] return None def deploy_worker(name, script): """ Deploy a Worker script using the correct multipart format. Cloudflare requires TWO parts: - 'metadata' : JSON describing the script - 'script' : The actual JS content """ url = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workers/scripts/{name}" # ── CRITICAL: metadata part is REQUIRED ────────────────────────────────── metadata = { "body_part" : "script", # tells CF which part contains the code "bindings" : [], # no KV / D1 / R2 bindings needed "compatibility_date": "2024-01-01" } resp = requests.put( url, headers=AUTH_HEADERS, # NO Content-Type here — requests sets it for multipart files={ "metadata": ( # part name must be "metadata" "metadata.json", json.dumps(metadata), "application/json" ), "script": ( # part name must match body_part value above "worker.js", script, "application/javascript" ) } ) result = resp.json() if result.get("success"): print(f"✅ Worker '{name}' deployed successfully") return True else: print(f"❌ Failed to deploy '{name}': {result.get('errors')}") return False def set_cron(name, cron_expression): """Attach a cron trigger to an existing worker.""" url = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workers/scripts/{name}/schedules" resp = requests.put( url, headers={**AUTH_HEADERS, "Content-Type": "application/json"}, json=[{"cron": cron_expression}] ) result = resp.json() if result.get("success"): print(f" ⏰ Cron trigger set: {cron_expression}") return True else: print(f" ⚠️ Cron setup failed: {result.get('errors')}") return False def enable_workers_dev_route(name): """Enable the workers.dev route so the worker has a public URL.""" url = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workers/scripts/{name}/subdomain" resp = requests.post( url, headers={**AUTH_HEADERS, "Content-Type": "application/json"}, json={"enabled": True} ) result = resp.json() if result.get("success"): print(f" 🌐 workers.dev route enabled") return True else: # Not fatal — worker still works, just no public URL print(f" ⚠️ Could not enable workers.dev route: {result.get('errors')}") return False def set_telegram_webhook(proxy_url): """Point Telegram webhook at our HF Space — via Cloudflare proxy.""" if not BOT_TOKEN: print(" ⚠️ TELEGRAM_BOT_TOKEN not set — skipping webhook") return if not proxy_url: print(" ⚠️ No proxy_url provided — skipping webhook") return webhook_url = f"{SPACE_URL}/telegram" # ✅ Route through Cloudflare proxy instead of calling api.telegram.org directly # Your proxy worker forwards: proxy_url/bot/setWebhook → api.telegram.org/bot/setWebhook resp = requests.get( f"{proxy_url}/bot{BOT_TOKEN}/setWebhook", # ← goes to Cloudflare Worker params={"url": webhook_url}, timeout=30 ) try: data = resp.json() if data.get("ok"): print(f" 📡 Telegram webhook set → {webhook_url}") else: print(f" ⚠️ Webhook set failed: {data}") except Exception as e: print(f" ❌ Failed to parse webhook response: {e}") print(f" Raw response: {resp.text}") if __name__ == "__main__": if not ACCOUNT_ID or not CF_TOKEN: print("⚠️ CLOUDFLARE_ACCOUNT_ID or CLOUDFLARE_WORKERS_TOKEN not set — skipping") exit(0) print("☁️ Deploying Cloudflare Workers...") # ── Deploy keep-alive worker ────────────────────────────────────────────── if deploy_worker("llm-space-keepalive", KEEPALIVE_SCRIPT): set_cron("llm-space-keepalive", "*/4 * * * *") # ── Deploy Telegram proxy worker ────────────────────────────────────────── if deploy_worker("llm-space-telegram-proxy", TELEGRAM_PROXY_SCRIPT): enable_workers_dev_route("llm-space-telegram-proxy") # Build the proxy public URL subdomain = get_subdomain() if subdomain: proxy_url = f"https://llm-space-telegram-proxy.{subdomain}.workers.dev" print(f"\n ✅ Telegram Proxy URL: {proxy_url}") print(f" 👉 Add this to HF Space Secrets:") print(f" CLOUDFLARE_TELEGRAM_PROXY_URL = {proxy_url}") time.sleep(3) # ⏳ Wait for Cloudflare route to propagate set_telegram_webhook(proxy_url) else: print(" ⚠️ Could not determine workers.dev subdomain") else: print(" ❌ Telegram proxy worker deployment failed — skipping webhook setup") print("\n☁️ Cloudflare setup complete!")