diff --git a/ai_agent.py b/ai_agent.py index 95b4606..af002e5 100644 --- a/ai_agent.py +++ b/ai_agent.py @@ -31,26 +31,23 @@ async def query_agent_async(prompt: str, override_provider=None, chat_history=No 2. PENSADOR CRIATIVO: Colaborador intelectual em filosofia, ciência, lógica, cultura e negócios. DIRETRIZES: -- Você tem ACESSO TOTAL ao Google Workspace via GWS CLI. Use `run_bash_command` para isso. -- CONTAS GWS DISPONÍVEIS: - * `gws-adm`: Conta Empresarial (admtracksteel@gmail.com) - * `gws-mr`: Conta Particular (m.reifonas@gmail.com) - * `gws-4r`: Conta Familiar (4reifonas@gmail.com) -- Se o usuário pedir para ver e-mails, arquivos do drive ou planilhas, use o alias correspondente. -- EXEMPLOS DE COMANDOS SEGUROS: - * Listar 3 e-mails: `[TOOL:run_bash_command] gws-mr gmail users messages list --params '{{"userId": "me", "maxResults": 3}}' [/TOOL]` - * Ver e-mail específico: `[TOOL:run_bash_command] gws-mr gmail users messages get --params '{{"userId": "me", "id": "ID_AQUI"}}' [/TOOL]` - * Listar Drive: `[TOOL:run_bash_command] gws-adm drive files list --params '{{"pageSize": 5}}' [/TOOL]` -- Nunca responda que não tem acesso a e-mails ou arquivos externos se puder usar o GWS. -- Responda sempre em PORTUGUÊS. +- Você tem ACESSO TOTAL ao Google Workspace via GWS CLI. +- CONTAS GWS (Pode usar apelidos): + * `ma` ou `mr` -> gws-mr (Marcos / Particular) + * `adm` ou `empresa` -> gws-adm (Empresarial) + * `4r` ou `fam` -> gws-4r (Familiar) +- E-MAILS (O usuário exige ver Títulos e Remetentes): + * SEMPRE use `list_gmail_emails` para listar mensagens. + * Se o usuário escrever "ma - veja meu ultimo e-mail", ou "veja o e-mail ma", entenda que ele se refere à conta `ma` (gws-mr). + * SEMPRE numere os itens da lista (#1, #2, #3, etc.). + * Se o usuário disser "o primeiro", "o segundo", etc., identifique o ID correspondente da listagem anterior no histórico e use-o. +- DRIVE E ARQUIVOS: Use `run_bash_command` para `find`, `list` ou `get`. +- Responda sempre em PORTUGUÊS do Brasil. - CAMINHOS DO SISTEMA: * BotVPS / Antigravity: `/root/Apps/BotVPS` (seu código fonte) * Repositórios: `/data/repositories/` -- Se não souber onde um arquivo está, use `run_bash_command` com `find`. NUNCA CHUTE DIRETÓRIOS. - MEMÓRIA CRONOS (LONGO PRAZO): - * Siga a "Amizade Intelectual": lembre-se de conversas passadas e planos. - * Use `cronos_query` no início de tarefas complexas para recuperar contexto semanal ou por assunto. - * Use `cronos_log` (folder='current_week' ou 'knowledge') para salvar progressos, decisões e ideias importantes. + * Use `cronos_query` e `cronos_log` para manter a continuidade do conhecimento. * Raiz: `/root/Antigravity_Memory/` ### FERRAMENTAS DISPONÍVEIS: @@ -58,8 +55,8 @@ DIRETRIZES: ### FORMATO DE RESPOSTA: - Use [TOOL:nome] arg [/TOOL] para ações. -- Pense passo a passo. Se for uma nova sessão ou assunto, comece buscando em Cronos. -- Finalize sempre com resumo final . +- Pense passo a passo. Seja inteligente ao lidar com referências ordinais. +- Finalize sempre com resumo final elegante . """ history_str = "" diff --git a/bridge_telegram.py b/bridge_telegram.py index 005f6dc..3513a2c 100644 --- a/bridge_telegram.py +++ b/bridge_telegram.py @@ -25,11 +25,14 @@ logging.basicConfig( ) logger = logging.getLogger(__name__) +# Dicionário global para manter o histórico (Em um sistema de produção, usar Redis ou DB) +chat_histories = {} + async def call_antigravity_api(endpoint: str, payload: dict) -> str: """Faz a chamada para a API interna do BotVPS.""" async with httpx.AsyncClient(timeout=120.0) as client: try: - logger.info(f"Enviando payload para {endpoint}: {payload}") + logger.info(f"Enviando payload para {endpoint}: {payload.get('text', payload.get('task'))}") response = await client.post(f"{API_BASE_URL}{endpoint}", json=payload) response.raise_for_status() data = response.json() @@ -46,6 +49,7 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): if not update.message or not update.message.text: return + chat_id = update.effective_chat.id user_id = update.effective_user.id # Filtro de Segurança @@ -56,9 +60,13 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): text = update.message.text logger.info(f"Mensagem recebida de {user_id}: {text}") + # Inicializa histórico se não existir + if chat_id not in chat_histories: + chat_histories[chat_id] = [] + # Diferenciação entre comandos de sistema e conhecimento geral if text.startswith(('/bash', '/vps', '/cmd')): - # Remove prefixos para enviar a tarefa limpa para o orquestrador + # Comandos de sistema geralmente não precisam de histórico de chat natural task = text.replace('/bash', '').replace('/vps', '').replace('/cmd', '').strip() if not task: @@ -68,13 +76,24 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("⚙️ *Processando tarefa no Claw System...*", parse_mode='Markdown') reply = await call_antigravity_api("/api/orchestrate", {"task": task}) else: - # Chat Natural - # Nota: O bot local mantém o histórico se enviarmos, mas aqui simplificamos para 1-to-1 - # A API /api/chat já lida com o prompt do sistema CLAW - reply = await call_antigravity_api("/api/chat", {"text": text}) + # Chat Natural com contexto + await context.bot.send_chat_action(chat_id=chat_id, action="typing") + + # Prepara payload com histórico + payload = { + "text": text, + "history": chat_histories[chat_id][-10:] # Envia os últimos 10 turnos + } + + reply = await call_antigravity_api("/api/chat", payload) + + # Atualiza histórico Local + chat_histories[chat_id].append({"user": text, "bot": reply}) + # Mantém apenas os últimos 15 para não crescer infinito no middleware + if len(chat_histories[chat_id]) > 15: + chat_histories[chat_id].pop(0) # Envia a resposta de volta para o usuário - # Se a resposta for muito longa, o Telegram pode cortar (máx 4096 chars) if len(reply) > 4000: reply = reply[:3900] + "... [Texto truncado]" diff --git a/tools.py b/tools.py index 59099e1..bdaa195 100644 --- a/tools.py +++ b/tools.py @@ -147,12 +147,58 @@ def cronos_query(arg: str) -> str: target_dir = os.path.join(MEMORY_ROOT, folder) return run_bash_command(f"grep -rniI '{query}' {target_dir} | head -n 20") +def list_gmail_emails(account: str) -> str: + """Lista os últimos 5 e-mails com Título e Remetente. Aceita apelidos: ma, mr, adm, 4r.""" + mapping = { + "ma": "gws-mr", "mr": "gws-mr", "marcos": "gws-mr", + "adm": "gws-adm", "empresa": "gws-adm", + "4r": "gws-4r", "familia": "gws-4r", "fam": "gws-4r" + } + + clean_account = account.strip().lower().replace("gws-", "") + account = mapping.get(clean_account, f"gws-{clean_account}" if not clean_account.startswith("gws") else clean_account) + + # 1. Obtém a lista de IDs + list_cmd = f"{account} gmail users messages list --params '{{\"userId\": \"me\", \"maxResults\": 5}}'" + res = run_bash_command(list_cmd) + + try: + data = json.loads(res) + messages = data.get("messages", []) + if not messages: + return "Nenhum e-mail encontrado." + + result_text = "📧 **Últimos E-mails:**\n" + for i, msg in enumerate(messages, 1): + msg_id = msg["id"] + # 2. Busca detalhes de cada e-mail (Metadata apenas para ser rápido) + details_cmd = f"{account} gmail users messages get --params '{{\"userId\": \"me\", \"id\": \"{msg_id}\", \"format\": \"metadata\", \"metadataHeaders\": [\"Subject\", \"From\"]}}'" + details_res = run_bash_command(details_cmd) + + try: + details = json.loads(details_res) + headers = details.get("payload", {}).get("headers", []) + subject = next((h["value"] for h in headers if h["name"] == "Subject"), "Sem Assunto") + sender = next((h["value"] for h in headers if h["name"] == "From"), "Desconhecido") + + result_text += f"{i}. **De:** {sender}\n **Assunto:** {subject}\n **ID:** `{msg_id}`\n\n" + except: + result_text += f"{i}. [Erro ao carregar detalhes do ID: {msg_id}]\n\n" + + return result_text + except Exception as e: + return f"Erro ao listar e-mails: {str(e)}\nResposta bruta: {res[:200]}" + # Mapeamento para o Agente entender quais tools ele possui (será usado no loop ReAct) AVAILABLE_TOOLS = { "run_bash_command": { "description": "Executa comandos Linux na VPS. Use para docker, git, mkdir, touch, etc.", "func": run_bash_command }, + "list_gmail_emails": { + "description": "Lista os 5 e-mails mais recentes de uma conta (gws-mr, gws-adm, gws-4r) com título e remetente.", + "func": list_gmail_emails + }, "get_system_health": { "description": "Verifica RAM, CPU e Disco globais da VPS.", "func": get_system_health