"""True long-polling for Telegram updates. Uses timeout parameter to keep connection hung open. Inbound messages process instantly, no 30s latency. Uses TELEGRAM_BOT_TOKEN from staging config — which is the test bot in staging environment (8469114191:...), NOT production. """ import asyncio import logging import httpx from icarus.core.config.staging import TELEGRAM_BOT_TOKEN from icarus.core.telegram.handler import process_update BASE_URL = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}" OFFSET = 0 async def polling_loop(): """Long-polling loop running alongside FastAPI.""" global OFFSET # Clear any existing webhook first async with httpx.AsyncClient() as client: try: resp = await client.post(f"{BASE_URL}/deleteWebhook") result = resp.json() if result.get("ok"): logging.info("[polling] Webhook cleared, starting long-polling...") else: logging.warning("[polling] deleteWebhook: %s", result) except Exception as e: logging.warning("[polling] Failed to clear webhook: %s", e) logging.info("[polling] Long-polling loop started") while True: try: async with httpx.AsyncClient(timeout=65.0) as client: # timeout=60 keeps connection hung open (+5s buffer) resp = await client.post( f"{BASE_URL}/getUpdates", json={"offset": OFFSET, "limit": 100, "timeout": 60} ) resp.raise_for_status() data = resp.json() if data.get("ok") and data.get("result"): for update in data["result"]: OFFSET = update["update_id"] + 1 logging.info("[polling] Processing update %s", update["update_id"]) try: await process_update(update) except Exception as e: logging.exception("[polling] Error processing update: %s", e) except asyncio.CancelledError: logging.info("[polling] Loop cancelled, exiting") break except Exception as e: logging.error("[polling] Polling error: %s", e) await asyncio.sleep(5) # Backoff on error async def start_polling(): """Entry point to start background task.""" logging.info("[polling] Starting background polling task") asyncio.create_task(polling_loop())