""" webhook_server.py ══════════════════════════════════════════════════════════════ Webhook Telegram — avec client HTTP custom pour contourner les restrictions réseau de Hugging Face Spaces ══════════════════════════════════════════════════════════════ """ import asyncio import os import json import logging import httpx from fastapi import FastAPI, Request, HTTPException from superviseur import travailler_ensemble, formater_pour_telegram # ───────────────────────────────────────────── # CONFIG # ───────────────────────────────────────────── TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN", "") TELEGRAM_API = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}" WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "") logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI() # ───────────────────────────────────────────── # ENVOI TELEGRAM via httpx direct (pas python-telegram-bot) # ───────────────────────────────────────────── async def send(chat_id: int, text: str): """Envoie un message Telegram via httpx avec timeout long.""" chunks = [text[i:i+4000] for i in range(0, len(text), 4000)] async with httpx.AsyncClient(timeout=60) as client: for chunk in chunks: try: await client.post( f"{TELEGRAM_API}/sendMessage", json={ "chat_id": chat_id, "text": chunk, "parse_mode": "Markdown", } ) except Exception: try: # Retry sans Markdown si erreur de formatage await client.post( f"{TELEGRAM_API}/sendMessage", json={"chat_id": chat_id, "text": chunk} ) except Exception as e: logger.error(f"Impossible d'envoyer le message : {e}") async def send_and_get_id(chat_id: int, text: str) -> int | None: """Envoie un message et retourne son message_id.""" async with httpx.AsyncClient(timeout=60) as client: try: resp = await client.post( f"{TELEGRAM_API}/sendMessage", json={ "chat_id": chat_id, "text": text, "parse_mode": "Markdown", } ) data = resp.json() if data.get("ok"): return data["result"]["message_id"] except Exception as e: logger.warning(f"send_and_get_id: {e}") return None async def edit_message(chat_id: int, message_id: int, text: str): """Met à jour un message existant.""" async with httpx.AsyncClient(timeout=60) as client: try: await client.post( f"{TELEGRAM_API}/editMessageText", json={ "chat_id": chat_id, "message_id": message_id, "text": text, "parse_mode": "Markdown", } ) except Exception as e: logger.warning(f"edit_message: {e}") async def delete_message(chat_id: int, message_id: int): """Supprime un message.""" async with httpx.AsyncClient(timeout=30) as client: try: await client.post( f"{TELEGRAM_API}/deleteMessage", json={"chat_id": chat_id, "message_id": message_id} ) except Exception as e: logger.warning(f"delete_message: {e}") # ───────────────────────────────────────────── # GESTION DES MESSAGES # ───────────────────────────────────────────── AIDE = ( "👋 *Bienvenue ! Je suis votre équipe IA.*\n\n" "🐍 *Hermes* \\+ 🦅 *Openclaw* travaillent ensemble pour vous\\.\n\n" "📌 *Comment utiliser :*\n" "Envoyez simplement votre tâche, par exemple :\n\n" "• _Fais\\-moi une analyse SWOT de Netflix_\n" "• _Rédige un plan de cours sur la blockchain_\n" "• _Propose une stratégie marketing pour une app mobile_\n\n" "Les agents analyseront, se répartiront le travail et vous livreront un résultat complet\\." ) async def handle_update(update: dict): """Traite un message Telegram entrant.""" message = update.get("message") or update.get("edited_message") if not message or "text" not in message: return chat_id = message["chat"]["id"] text = message["text"].strip() # Commandes système if text in ("/start", "/aide", "/help"): await send(chat_id, AIDE) return # Toute autre tâche → collaboration inter-agents tache = text status_id = [None] # liste pour mutabilité dans la closure async def on_progress(msg: str): try: if status_id[0] is None: mid = await send_and_get_id(chat_id, msg) status_id[0] = mid else: await edit_message(chat_id, status_id[0], msg) except Exception as e: logger.warning(f"on_progress: {e}") try: await on_progress("🚀 *Vos agents prennent en charge la tâche...*") resultat = await travailler_ensemble(tache, on_progress=on_progress) # Supprimer le message de statut if status_id[0]: await delete_message(chat_id, status_id[0]) # Envoyer résumé + livrable messages = formater_pour_telegram(tache, resultat) for msg in messages: await send(chat_id, msg) except Exception as e: logger.exception("Erreur collaboration agents") if status_id[0]: await delete_message(chat_id, status_id[0]) await send(chat_id, f"❌ Erreur : {str(e)[:200]}") # ───────────────────────────────────────────── # ENDPOINT WEBHOOK # ───────────────────────────────────────────── @app.post("/webhook") async def webhook(request: Request): if WEBHOOK_SECRET: if request.headers.get("X-Telegram-Bot-Api-Secret-Token", "") != WEBHOOK_SECRET: raise HTTPException(status_code=403, detail="Forbidden") data = await request.json() asyncio.create_task(handle_update(data)) return {"ok": True} @app.get("/") async def root(): return {"status": "running", "equipe": ["Hermes", "Openclaw"]} @app.get("/health") async def health(): return {"status": "ok"}