feat: Integrate orchestrator with Telegram bot
- Add orchestrator imports and handlers - Detect orchestrator tasks via keywords (deploy, docker, git, etc.) - Add commands: /status, /tools, /sync, /orchestrate - Add confirmation flow (sim/nao) - Keep fallback to ai_agent for non-orchestrator tasks
This commit is contained in:
96
bot_logic.py
96
bot_logic.py
@@ -8,6 +8,7 @@ from ai_agent import query_agent
|
|||||||
import speech_recognition as sr
|
import speech_recognition as sr
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment
|
||||||
from gtts import gTTS
|
from gtts import gTTS
|
||||||
|
from orchestrator import handle_message, orchestrate, format_confirmation_message, format_completion_message
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -51,17 +52,92 @@ async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
user_msg = update.message.text
|
user_msg = update.message.text
|
||||||
await update.message.reply_chat_action(action="typing")
|
await update.message.reply_chat_action(action="typing")
|
||||||
|
|
||||||
# Busca histórico anterior
|
# =====================================================
|
||||||
history = chat_histories.get(chat_id, [])
|
# COMANDOS DO ORCHESTRATOR
|
||||||
|
# =====================================================
|
||||||
|
if user_msg.startswith('/orchestrate'):
|
||||||
|
# Força uso do orchestrator
|
||||||
|
task = user_msg.replace('/orchestrate', '').strip()
|
||||||
|
result = orchestrate(task, user_confirmed=False)
|
||||||
|
if result["status"] == "needs_confirmation":
|
||||||
|
reply = format_confirmation_message(result)
|
||||||
|
else:
|
||||||
|
reply = format_completion_message(result)
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
# Aciona o Agente de IA para processar o prompt e executar Tools se precisar
|
if user_msg.startswith('/status') and not user_msg.startswith('/statusall'):
|
||||||
|
# Status do orchestrator
|
||||||
|
reply = handle_message('/status')
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_msg.startswith('/tools'):
|
||||||
|
# Lista de ferramentas
|
||||||
|
reply = handle_message('/tools')
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_msg.startswith('/sync'):
|
||||||
|
# Sync de credenciais
|
||||||
|
reply = handle_message('/sync')
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_msg.lower() in ['sim', 'confirmar', 'confirma', 'sim!', 'confirma!', 's']:
|
||||||
|
# Verifica se há confirmação pendente
|
||||||
|
if 'pending_confirmation' in context.bot_data:
|
||||||
|
task = context.bot_data['pending_confirmation']
|
||||||
|
result = orchestrate(task, user_confirmed=True)
|
||||||
|
reply = format_completion_message(result)
|
||||||
|
del context.bot_data['pending_confirmation']
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
if user_msg.lower() in ['nao', 'não', 'cancelar', 'cancela', 'n', 'n!']:
|
||||||
|
# Cancela confirmação pendente
|
||||||
|
if 'pending_confirmation' in context.bot_data:
|
||||||
|
del context.bot_data['pending_confirmation']
|
||||||
|
await update.message.reply_text("Operacao cancelada.")
|
||||||
|
return
|
||||||
|
await update.message.reply_text("Nada pendente para cancelar.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# ORCHESTRATOR: Detecta tarefas de orquestração
|
||||||
|
# =====================================================
|
||||||
|
orchestrator_keywords = ['deploy', 'restart', 'restartar', 'reiniciar',
|
||||||
|
'git pull', 'git push', 'docker', 'container',
|
||||||
|
'mostra status', 'status dos', 'verificar',
|
||||||
|
'faz um', 'executa', 'roda', 'rodar',
|
||||||
|
'atualiza', 'atualizar', 'backup']
|
||||||
|
|
||||||
|
is_orchestrator_task = any(kw in user_msg.lower() for kw in orchestrator_keywords)
|
||||||
|
|
||||||
|
if is_orchestrator_task:
|
||||||
|
result = orchestrate(user_msg, user_confirmed=False)
|
||||||
|
if result["status"] == "needs_confirmation":
|
||||||
|
# Salva tarefa pendente
|
||||||
|
context.bot_data['pending_confirmation'] = user_msg
|
||||||
|
reply = format_confirmation_message(result)
|
||||||
|
reply += "\n\nResponda *sim* para confirmar ou *nao* para cancelar."
|
||||||
|
else:
|
||||||
|
reply = format_completion_message(result)
|
||||||
|
|
||||||
|
await update.message.reply_text(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# FALLBACK: Usa o agente normal (ai_agent.py)
|
||||||
|
# =====================================================
|
||||||
|
history = chat_histories.get(chat_id, [])
|
||||||
from config import get_config
|
from config import get_config
|
||||||
cfg = get_config()
|
cfg = get_config()
|
||||||
reply = query_agent(prompt=user_msg, override_provider=cfg.get("active_provider"), chat_history=history)
|
reply = query_agent(prompt=user_msg, override_provider=cfg.get("active_provider"), chat_history=history)
|
||||||
|
|
||||||
# Atualiza histórico
|
# Atualiza histórico
|
||||||
history.append({"user": user_msg, "bot": reply})
|
history.append({"user": user_msg, "bot": reply})
|
||||||
chat_histories[chat_id] = history[-10:] # Mantém apenas as últimas 10
|
chat_histories[chat_id] = history[-10:]
|
||||||
|
|
||||||
# Se o usuário pedir ativamente por áudio no texto
|
# Se o usuário pedir ativamente por áudio no texto
|
||||||
if "áudio" in user_msg.lower() or "audio" in user_msg.lower() or "voz" in user_msg.lower():
|
if "áudio" in user_msg.lower() or "audio" in user_msg.lower() or "voz" in user_msg.lower():
|
||||||
@@ -73,15 +149,12 @@ async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
# --- NOVO: Lógica para enviar IMAGENS se a IA localizou um arquivo ---
|
# --- NOVO: Lógica para enviar IMAGENS se a IA localizou um arquivo ---
|
||||||
import re
|
import re
|
||||||
# Procura por padrões de imagem Markdown ou caminhos absolutos de imagem
|
img_matches = re.findall(r'!\[.*?\]\((/.*?)\)', reply)
|
||||||
img_matches = re.findall(r'!\[.*?\]\((/.*?)\)', reply) # 
|
|
||||||
if not img_matches:
|
if not img_matches:
|
||||||
# Tenta achar caminhos absolutos que terminam em extensões de imagem
|
|
||||||
img_matches = re.findall(r'(/[^\s]+?\.(?:jpg|jpeg|png|gif|webp))', reply, re.IGNORECASE)
|
img_matches = re.findall(r'(/[^\s]+?\.(?:jpg|jpeg|png|gif|webp))', reply, re.IGNORECASE)
|
||||||
|
|
||||||
if img_matches:
|
if img_matches:
|
||||||
for img_path in img_matches:
|
for img_path in img_matches:
|
||||||
# Garante que o caminho use /host_root se for um arquivo da VPS
|
|
||||||
real_path = img_path
|
real_path = img_path
|
||||||
if not real_path.startswith("/host_root") and real_path.startswith("/root"):
|
if not real_path.startswith("/host_root") and real_path.startswith("/root"):
|
||||||
real_path = f"/host_root{real_path}"
|
real_path = f"/host_root{real_path}"
|
||||||
@@ -89,11 +162,10 @@ async def handle_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
if os.path.exists(real_path):
|
if os.path.exists(real_path):
|
||||||
try:
|
try:
|
||||||
await update.message.reply_chat_action(action="upload_photo")
|
await update.message.reply_chat_action(action="upload_photo")
|
||||||
await update.message.reply_photo(photo=open(real_path, 'rb'), caption="🖼️ Imagem localizada na VPS")
|
await update.message.reply_photo(photo=open(real_path, 'rb'), caption="Imagem localizada na VPS")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Erro ao enviar imagem {real_path}: {e}")
|
print(f"Erro ao enviar imagem {real_path}: {e}")
|
||||||
|
|
||||||
# Responde no chat normalmente
|
|
||||||
await update.message.reply_text(reply)
|
await update.message.reply_text(reply)
|
||||||
|
|
||||||
async def handle_voice(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def handle_voice(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
@@ -184,6 +256,10 @@ def get_telegram_app():
|
|||||||
app.add_handler(CommandHandler("start", start))
|
app.add_handler(CommandHandler("start", start))
|
||||||
app.add_handler(CommandHandler("llm", llm_command))
|
app.add_handler(CommandHandler("llm", llm_command))
|
||||||
app.add_handler(CommandHandler("limpar", clear_history))
|
app.add_handler(CommandHandler("limpar", clear_history))
|
||||||
|
app.add_handler(CommandHandler("status", lambda u, c: handle_text(u, c))) # Alias para status
|
||||||
|
app.add_handler(CommandHandler("tools", lambda u, c: handle_text(u, c))) # Alias para tools
|
||||||
|
app.add_handler(CommandHandler("sync", lambda u, c: handle_text(u, c))) # Alias para sync
|
||||||
|
app.add_handler(CommandHandler("orchestrate", lambda u, c: handle_text(u, c))) # Orchestrate
|
||||||
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text))
|
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text))
|
||||||
app.add_handler(MessageHandler(filters.VOICE, handle_voice))
|
app.add_handler(MessageHandler(filters.VOICE, handle_voice))
|
||||||
return app
|
return app
|
||||||
|
|||||||
Reference in New Issue
Block a user