| """ |
| 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 |
|
|
| |
| |
| |
|
|
| 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() |
|
|
| |
| |
| |
|
|
| 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: |
| |
| 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}") |
|
|
|
|
| |
| |
| |
|
|
| 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() |
|
|
| |
| if text in ("/start", "/aide", "/help"): |
| await send(chat_id, AIDE) |
| return |
|
|
| |
| tache = text |
| status_id = [None] |
|
|
| 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) |
|
|
| |
| if status_id[0]: |
| await delete_message(chat_id, status_id[0]) |
|
|
| |
| 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]}") |
|
|
|
|
| |
| |
| |
|
|
| @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"} |