# Gerado por IA
import asyncio
import json
import urllib.request
from datetime import datetime
from telegram import Update
from telegram.ext import (
ApplicationBuilder,
ContextTypes,
CommandHandler,
MessageHandler,
filters,
)
from config import settings, _silenced_agents
from context_manager import get_context_summary, append_to_context, load_daily_context
from ai_agent import AIAgent, SYSTEM_PROMPT_TEMPLATE
from orchestrator_shared import _orchestrator_ref
import aiohttp
agent_registry: dict[str, AIAgent] = {}
# ---------- helpers HTTP ----------
def _http_get(url: str, headers: dict | None = None, timeout: int = 30) -> dict:
req = urllib.request.Request(url, headers=headers or {})
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read())
def _http_post(url: str, payload: dict, headers: dict | None = None, timeout: int = 60) -> dict:
data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(url, data=data, headers=headers or {"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read())
# ---------- util ----------
async def _send_telegram(text: str) -> None:
url = f"https://api.telegram.org/bot{settings.TELEGRAM_BOT_TOKEN}/sendMessage"
payload = {"chat_id": settings.TELEGRAM_CHAT_ID, "text": text, "parse_mode": "HTML"}
try:
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=15)) as resp:
if resp.status != 200:
body = await resp.text()
print(f"[Telegram] HTTP {resp.status}: {body[:120]}")
except Exception as exc:
print(f"[Telegram] Erro ao enviar mensagem: {exc}")
# ---------- comandos ----------
async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
msg = (
"MultiAgentsAI iniciado!\n"
f"Agentes ativos: {len(agent_registry)}\n"
f"{', '.join(a.name for a in agent_registry.values())}\n"
"Os agentes estao conversando automaticamente.\n"
"Comandos:\n"
"/status - Status do sistema\n"
"/historico - Ultimas mensagens de hoje\n"
"/agentes - Lista de agentes\n"
"/participar [mensagem] - Enviar mensagem como usuario\n"
"/pausar /continuar - Pausar/retomar agentes"
)
await _send_telegram(msg)
async def cmd_status(update: Update, context: ContextTypes.DEFAULT_TYPE):
total = len(load_daily_context())
paused = _orchestrator_ref.paused if _orchestrator_ref else False
msg = (
"Status MultiAgentsAI\n"
f"Agentes ativos: {len(agent_registry)}\n"
f"Mensagens hoje: {total}\n"
f"Delay: {settings.CONVERSATION_DELAY}s\n"
f"Pausado: {'Sim' if paused else 'Nao'}"
)
await _send_telegram(msg)
async def cmd_historico(update: Update, context: ContextTypes.DEFAULT_TYPE):
summary = get_context_summary(30)
await _send_telegram(f"Historico de hoje:\n\n{summary}")
async def cmd_agentes(update: Update, context: ContextTypes.DEFAULT_TYPE):
lines = ["Agentes registrados:"]
for a in agent_registry.values():
lines.append(f" - {a.name} ({a.model})")
await _send_telegram("\n".join(lines))
async def cmd_participar(update: Update, context: ContextTypes.DEFAULT_TYPE):
text = " ".join(context.args) if context.args else ""
if not text:
await _send_telegram("Uso: /participar [sua mensagem aqui]")
return
user_msg = f"[Usuario] {text}"
append_to_context("Usuario", user_msg)
await _send_telegram(f"Voce: {text}")
# Recarrega historico DIRETAMENTE do SQLite para garantir que a msg do usuario esteja la
from database import get_today_messages
messages = get_today_messages()
history = "\n".join(f"[{m['agent_name']}] {m['message']}" for m in messages[-settings.MAX_CONTEXT_MESSAGES:])
other_agents = [a.name for a in agent_registry.values() if a.name != "Usuario"]
for agent in agent_registry.values():
if agent.name == "Usuario":
continue
reply = await agent.generate_response(history, other_agents)
if not reply.startswith("[Erro"):
append_to_context(agent.name, reply)
await _send_telegram(f"{agent.name}: {reply}")
async def cmd_pausar(update: Update, context: ContextTypes.DEFAULT_TYPE):
if _orchestrator_ref:
_orchestrator_ref.paused = True
await _send_telegram("Agentes pausados.")
async def cmd_continuar(update: Update, context: ContextTypes.DEFAULT_TYPE):
if _orchestrator_ref:
_orchestrator_ref.paused = False
await _send_telegram("Agentes retomados.")
async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
await _send_telegram(
"Comandos disponiveis:\n"
"/start - Iniciar\n"
"/status - Status do sistema\n"
"/historico - Historico de hoje\n"
"/agentes - Lista de agentes\n"
"/participar [msg] - Enviar mensagem\n"
"/pausar - Pausar agentes\n"
"/continuar - Retomar agentes\n"
"/modelos - Lista modelos gratuitos do OpenRouter\n"
"/testar [modelo] - Testa se um modelo esta funcionando\n"
"/tema [assunto] - Define um tema para a conversa\n"
"/limparchat - Limpa o historico de hoje\n"
"/agente [nome] [msg] - Envia msg para um agente especifico\n"
"/estatisticas - Estatisticas do banco de dados\n"
"/buscar [termo] - Busca mensagens no historico\n"
"/delay [s] - Altera o delay entre mensagens\n"
"/idioma [codigo] - Altera o idioma (ex: pt_BR, en_US)\n"
"/silenciar [nome] - Silencia/desilencia um agente\n"
"/help - Esta ajuda"
)
# ===================================================================
# Novos comandos
# ===================================================================
async def cmd_modelos(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Lista todos os modelos gratuitos disponiveis no OpenRouter."""
await _send_telegram("Buscando modelos gratuitos no OpenRouter...")
try:
data = _http_get(
f"{settings.OPENROUTER_BASE_URL}/models",
headers={"Authorization": f"Bearer {settings.OPENROUTER_API_KEY}"},
timeout=30,
)
free = [m for m in data.get("data", []) if ":free" in m.get("id", "")]
free.sort(key=lambda m: m.get("context_length", 0), reverse=True)
if not free:
await _send_telegram("Nenhum modelo gratuito encontrado.")
return
lines = [f"Modelos gratuitos ({len(free)}):"]
for m in free[:20]:
ctx = m.get("context_length", "?")
lines.append(f" - {m['id']} (ctx={ctx})")
if len(free) > 20:
lines.append(f" ... e mais {len(free) - 20} modelos.")
await _send_telegram("\n".join(lines))
except Exception as exc:
await _send_telegram(f"Erro: {exc}")
async def cmd_testar(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Testa se um modelo esta funcionando: /testar openai/gpt-4o:free"""
args = context.args
if not args:
await _send_telegram("Uso: /testar [modelo]\nExemplo: /testar nvidia/nemotron-3.5-lightning:free")
return
model = " ".join(args)
await _send_telegram(f"Testando modelo: {model}...")
try:
data = _http_post(
f"{settings.OPENROUTER_BASE_URL}/chat/completions",
{"model": model, "messages": [{"role": "user", "content": "OK"}], "max_tokens": 20},
headers={
"Authorization": f"Bearer {settings.OPENROUTER_API_KEY}",
"Content-Type": "application/json",
"HTTP-Referer": "https://multiagentsai.local",
"X-Title": "MultiAgentsAI",
},
timeout=65,
)
choices = data.get("choices", [])
if choices and (choices[0].get("message") or {}).get("content", "").strip():
await _send_telegram(f"OK - {model}\n{choices[0]['message']['content'][:100]}")
else:
await _send_telegram(f"FALHOU - {model}\nResposta vazia (finish_reason={choices[0].get('finish_reason','?') if choices else '?'})")
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")[:200]
await _send_telegram(f"FALHOU - {model}\nHTTP {exc.code}: {body}")
except Exception as exc:
await _send_telegram(f"Erro: {exc}")
async def cmd_tema(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Define um tema/foco para a conversa: /tema futuro da IA"""
text = " ".join(context.args) if context.args else ""
if not text:
await _send_telegram("Uso: /tema [assunto]\nExemplo: /tema O futuro da IA na medicina")
return
append_to_context("Usuario", f"[Usuario] Vamos conversar sobre: {text}")
# Recarrega historico do SQLite diretamente
from database import get_today_messages
msgs = get_today_messages()
history = "\n".join(f"[{m['agent_name']}] {m['message']}" for m in msgs[-settings.MAX_CONTEXT_MESSAGES:])
others = [a.name for a in agent_registry.values() if a.name != "Usuario"]
await _send_telegram(f"Novo tema definido: {text}")
for agent in agent_registry.values():
if agent.name == "Usuario":
continue
reply = await agent.generate_response(history, others)
if not reply.startswith("[Erro"):
append_to_context(agent.name, reply)
await _send_telegram(f"{agent.name}: {reply}")
async def cmd_limparchat(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Limpa o historico de hoje (JSON + SQLite)."""
import os as _os
path = _os.path.join(settings.CONTEXTS_DIR, f"conversas_{datetime.utcnow().strftime('%Y-%m-%d')}.json")
removed = 0
if _os.path.exists(path):
_os.remove(path)
removed += 1
try:
import sqlite3
conn = sqlite3.connect(settings.DB_PATH)
today = datetime.utcnow().strftime("%Y-%m-%d")
cur = conn.execute("DELETE FROM conversations WHERE date = ?", (today,))
removed += cur.rowcount
conn.commit()
conn.close()
except Exception:
pass
await _send_telegram(f"Chat limpo!\n{removed} registros removidos.")
async def cmd_agente(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Envia uma mensagem para um agente especifico: /agente AI Agent 1 ola"""
args = context.args
if len(args) < 2:
await _send_telegram("Uso: /agente [nome] [mensagem]\nExemplo: /agente AI Agent 1 ola, tudo bem?")
return
agent_name = args[0]
msg_text = " ".join(args[1:])
agent = agent_registry.get(agent_name)
if not agent:
nomes = ", ".join(agent_registry.keys())
await _send_telegram(f"Agente nao encontrado.\nAgentes disponiveis: {nomes}")
return
append_to_context("Usuario", f"[Usuario -> {agent_name}] {msg_text}")
msgs = load_daily_context()
history = "\n".join(f"[{m['agent_name']}] {m['message']}" for m in msgs[-settings.MAX_CONTEXT_MESSAGES:])
others = [a.name for a in agent_registry.values() if a.name not in (agent_name, "Usuario")]
reply = await agent.generate_response(history, others)
if not reply.startswith("[Erro"):
append_to_context(agent.name, reply)
await _send_telegram(f"{agent.name}: {reply}")
else:
await _send_telegram(reply)
async def cmd_estatisticas(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Mostra estatisticas do banco de dados."""
try:
import sqlite3
conn = sqlite3.connect(settings.DB_PATH)
conn.row_factory = sqlite3.Row
total = conn.execute("SELECT COUNT(*) FROM conversations").fetchone()[0]
today = datetime.utcnow().strftime("%Y-%m-%d")
today_count = conn.execute("SELECT COUNT(*) FROM conversations WHERE date = ?", (today,)).fetchone()[0]
agents_stats = conn.execute(
"SELECT agent_name, COUNT(*) as cnt FROM conversations WHERE date = ? GROUP BY agent_name ORDER BY cnt DESC",
(today,),
).fetchall()
conn.close()
lines = [
"Estatisticas",
f"Total de mensagens (todos os dias): {total}",
f"Mensagens hoje: {today_count}",
"",
"Por agente (hoje):",
]
if agents_stats:
for r in agents_stats:
lines.append(f" - {r['agent_name']}: {r['cnt']}")
else:
lines.append(" Nenhuma mensagem hoje.")
await _send_telegram("\n".join(lines))
except Exception as exc:
await _send_telegram(f"Erro: {exc}")
async def cmd_buscar(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Busca mensagens no historico: /buscar IA"""
args = context.args
if not args:
await _send_telegram("Uso: /buscar [termo]")
return
termo = " ".join(args).lower()
try:
import sqlite3
conn = sqlite3.connect(settings.DB_PATH)
conn.row_factory = sqlite3.Row
rows = conn.execute(
"SELECT agent_name, message, created_at FROM conversations WHERE LOWER(message) LIKE ? ORDER BY id DESC LIMIT 20",
(f"%{termo}%",),
).fetchall()
conn.close()
if not rows:
await _send_telegram(f"Nenhuma mensagem encontrada com '{termo}'.")
return
lines = [f"Resultados para '{termo}' ({len(rows)}):"]
for r in rows:
snippet = r["message"][:120].replace("<", "<").replace(">", ">")
lines.append(f" [{r['agent_name']}] {snippet}")
await _send_telegram("\n".join(lines))
except Exception as exc:
await _send_telegram(f"Erro: {exc}")
async def cmd_delay(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Altera o delay entre mensagens: /delay 60"""
args = context.args
if not args:
await _send_telegram(f"Delay atual: {settings.CONVERSATION_DELAY}s\nUso: /delay [segundos]")
return
try:
segundos = int(args[0])
if segundos < 5:
await _send_telegram("O delay minimo e de 5 segundos.")
return
if _orchestrator_ref:
_orchestrator_ref._delay_override = segundos
await _send_telegram(f"Delay alterado para {segundos}s.")
except ValueError:
await _send_telegram("Valor invalido. Use: /delay 30")
async def cmd_idioma(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Altera o idioma das conversas: /idioma en_US"""
args = context.args
if not args:
await _send_telegram(f"Idioma atual: {settings.CONVERSATION_LANGUAGE}\nUso: /idioma [codigo]\nEx: /idioma pt_BR /idioma en_US")
return
codigo = args[0]
# Atualiza em tempo de execucao
from ai_agent import SYSTEM_PROMPT_TEMPLATE
settings.CONVERSATION_LANGUAGE = codigo
for agent in agent_registry.values():
agent.system_prompt = SYSTEM_PROMPT_TEMPLATE.format(name=agent.name, language=codigo)
await _send_telegram(f"Idioma alterado para {codigo}.")
async def cmd_silenciar(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Silencia ou desilencia um agente: /silenciar AI Agent 2"""
args = context.args
if not args:
nomes = ", ".join(agent_registry.keys())
silenciados = ", ".join(_silenced_agents) or "nenhum"
await _send_telegram(
f"Agentes silenciados: {silenciados}\n"
f"Uso: /silenciar [nome]\n"
f"Agentes disponiveis: {nomes}"
)
return
nome = " ".join(args)
if nome not in agent_registry:
nomes = ", ".join(agent_registry.keys())
await _send_telegram(f"Agente '{nome}' nao encontrado.\nDisponiveis: {nomes}")
return
if nome in _silenced_agents:
_silenced_agents.discard(nome)
await _send_telegram(f"{nome} desilenciado — voltara a participar das conversas.")
else:
_silenced_agents.add(nome)
await _send_telegram(f"{nome} silenciado — nao participara mais das conversas automaticas.")
# ---------- inicializacao ----------
def build_bot() -> tuple:
from telegram.ext import ApplicationBuilder
app = ApplicationBuilder().token(settings.TELEGRAM_BOT_TOKEN).build()
app.add_handler(CommandHandler("start", cmd_start))
app.add_handler(CommandHandler("status", cmd_status))
app.add_handler(CommandHandler("historico", cmd_historico))
app.add_handler(CommandHandler("agentes", cmd_agentes))
app.add_handler(CommandHandler("participar", cmd_participar))
app.add_handler(CommandHandler("pausar", cmd_pausar))
app.add_handler(CommandHandler("continuar", cmd_continuar))
app.add_handler(CommandHandler("modelos", cmd_modelos))
app.add_handler(CommandHandler("testar", cmd_testar))
app.add_handler(CommandHandler("tema", cmd_tema))
app.add_handler(CommandHandler("limparchat", cmd_limparchat))
app.add_handler(CommandHandler("agente", cmd_agente))
app.add_handler(CommandHandler("estatisticas", cmd_estatisticas))
app.add_handler(CommandHandler("buscar", cmd_buscar))
app.add_handler(CommandHandler("delay", cmd_delay))
app.add_handler(CommandHandler("idioma", cmd_idioma))
app.add_handler(CommandHandler("silenciar", cmd_silenciar))
app.add_handler(CommandHandler("help", cmd_help))
return app