"""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())