mejorado bot de telegram

This commit is contained in:
2025-11-06 02:21:42 +01:00
parent b351e56614
commit 5d48cd4130
+66 -16
View File
@@ -2,12 +2,15 @@
from __future__ import annotations
import asyncio
import html
import os
import json
from contextlib import suppress
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Set
import requests
from agno.agent.agent import RunOutput
from agno.run.agent import ToolCallStartedEvent
from dotenv import load_dotenv
from agentes import DEFAULT_AGENT_NAME, create_agent
@@ -51,11 +54,16 @@ class TelegramKanBot:
self.agent = None
self.context: Dict[str, Any] = {}
self.mcp_tool_names: Set[str] = set()
async def setup_agent(self) -> None:
agent, context = await create_agent(self.logger, agent=self.agent_key)
self.agent = agent
self.context = context
server_tool_map = context.get("server_tool_map") or {}
self.mcp_tool_names = {
tool_name for tools in server_tool_map.values() for tool_name in (tools or [])
}
self.logger.info(
"🤖 Agente Kan para Telegram listo",
add_fields={
@@ -186,22 +194,26 @@ class TelegramKanBot:
},
)
typing_task = asyncio.create_task(self._typing_indicator(chat_id))
notified_tool_calls: Set[str] = set()
response: Optional[RunOutput] = None
try:
response = await self.agent.arun(text)
except Exception as error: # pragma: no cover - manejo defensivo
self.logger.exception(
error,
add_fields={
"agent_call": {"action": "agent_run", "content": text},
"agent_response": {"status": "error"},
},
run_result = await self.agent.arun(
text,
stream=True,
stream_events=True,
yield_run_response=True,
)
await self._send_message(chat_id, "Hubo un error procesando tu mensaje. Revisa los logs para más detalles.")
return
finally:
typing_task.cancel()
with suppress(asyncio.CancelledError):
await typing_task
if isinstance(run_result, RunOutput):
response = run_result
else:
async for event in run_result:
if isinstance(event, RunOutput):
response = event
elif isinstance(event, ToolCallStartedEvent):
await self._maybe_notify_tool_execution(chat_id, event, notified_tool_calls)
if response is None:
raise RuntimeError("El agente no devolvió respuesta.")
content = (response.content or "(respuesta vacía)").strip()
if not content:
@@ -225,10 +237,26 @@ class TelegramKanBot:
},
},
)
except Exception as error: # pragma: no cover - manejo defensivo
self.logger.exception(
error,
add_fields={
"agent_call": {"action": "agent_run", "content": text},
"agent_response": {"status": "error"},
},
)
await self._send_message(chat_id, "Hubo un error procesando tu mensaje. Revisa los logs para más detalles.")
return
finally:
typing_task.cancel()
with suppress(asyncio.CancelledError):
await typing_task
async def _send_message(self, chat_id: int, text: str) -> None:
async def _send_message(self, chat_id: int, text: str, *, parse_mode: Optional[str] = None) -> None:
for chunk in self._chunk_text(text):
payload = {"chat_id": chat_id, "text": chunk}
if parse_mode:
payload["parse_mode"] = parse_mode
try:
response = await asyncio.to_thread(
self.session.post,
@@ -255,6 +283,28 @@ class TelegramKanBot:
},
)
async def _maybe_notify_tool_execution(
self,
chat_id: int,
event: ToolCallStartedEvent,
notified_tool_calls: Set[str],
) -> None:
if not self.mcp_tool_names:
return
tool = getattr(event, "tool", None)
if not tool or not tool.tool_name:
return
tool_identifier = tool.tool_call_id or f"{tool.tool_name}:{len(notified_tool_calls)}"
if tool_identifier in notified_tool_calls:
return
if tool.tool_name not in self.mcp_tool_names:
return
notified_tool_calls.add(tool_identifier)
escaped_name = html.escape(tool.tool_name)
await self._send_message(chat_id, f"<i>Ejecutado {escaped_name}</i>", parse_mode="HTML")
async def _typing_indicator(self, chat_id: int) -> None:
try:
while True: