File size: 7,381 Bytes
2c93e4c
 
 
5eeb485
 
2c93e4c
 
 
 
 
5eeb485
2c93e4c
5eeb485
2c93e4c
 
 
 
 
 
 
 
5eeb485
 
2c93e4c
 
 
 
 
 
 
 
5eeb485
2c93e4c
 
 
5eeb485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c93e4c
5eeb485
 
 
 
 
 
 
 
 
 
 
 
 
 
2c93e4c
5eeb485
 
 
 
 
 
 
 
 
 
 
 
 
2c93e4c
5eeb485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c93e4c
 
 
 
 
 
 
5eeb485
2c93e4c
 
5eeb485
2c93e4c
 
5eeb485
2c93e4c
 
5eeb485
 
 
 
2c93e4c
 
5eeb485
 
2c93e4c
5eeb485
2c93e4c
 
 
 
5eeb485
2c93e4c
5eeb485
2c93e4c
 
5eeb485
 
 
 
 
 
 
 
2c93e4c
 
 
5eeb485
2c93e4c
 
5eeb485
 
 
2c93e4c
5eeb485
2c93e4c
 
 
 
 
 
5eeb485
 
 
 
2c93e4c
 
 
 
 
 
 
 
 
 
5eeb485
 
 
2c93e4c
 
5eeb485
2c93e4c
 
 
5eeb485
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""
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"}