From e66423c8fcacec77f8317c6f74c2bef45e221d02 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 22 Feb 2026 16:33:22 +0000 Subject: [PATCH] Add Horus Chat to MC - connects to AI agent --- app/api/horus-chat/route.ts | 116 ++++++++++++++ components/mission-control/HorusChat.tsx | 150 ++++++++++++++++++ .../MissionControlDashboard.tsx | 2 +- 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 app/api/horus-chat/route.ts create mode 100644 components/mission-control/HorusChat.tsx diff --git a/app/api/horus-chat/route.ts b/app/api/horus-chat/route.ts new file mode 100644 index 0000000..a13b3ca --- /dev/null +++ b/app/api/horus-chat/route.ts @@ -0,0 +1,116 @@ +import { NextResponse } from "next/server"; +import fs from "fs"; + +const MESSAGE_FILE = "/tmp/horus-mc-messages.json"; +const TELEGRAM_CHAT_ID = "382315644"; // Haitham's Telegram ID + +interface Message { + id: string; + role: "user" | "assistant"; + content: string; + timestamp: number; + processed: boolean; +} + +function getMessages(): Message[] { + try { + if (fs.existsSync(MESSAGE_FILE)) { + return JSON.parse(fs.readFileSync(MESSAGE_FILE, "utf-8")); + } + } catch (e) { + console.error("Error reading messages:", e); + } + return []; +} + +function saveMessages(messages: Message[]) { + fs.writeFileSync(MESSAGE_FILE, JSON.stringify(messages, null, 2)); +} + +async function sendToTelegram(text: string) { + try { + const token = process.env.TELEGRAM_BOT_TOKEN; + if (!token) { + console.log("No Telegram token, skipping"); + return; + } + + await fetch(`https://api.telegram.org/bot${token}/sendMessage`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chat_id: TELEGRAM_CHAT_ID, + text: `💬 MC Chat:\n${text}`, + }), + }); + } catch (e) { + console.error("Telegram error:", e); + } +} + +export async function POST(request: Request) { + try { + const body = await request.json(); + const { message, locale = "es" } = body; + + if (!message) { + return NextResponse.json({ error: "Message required" }, { status: 400 }); + } + + const messages = getMessages(); + + // Add user message + const userMessage: Message = { + id: `msg_${Date.now()}`, + role: "user", + content: message, + timestamp: Date.now(), + processed: false, + }; + messages.push(userMessage); + saveMessages(messages); + + // Forward to Telegram (where Horus is listening) + await sendToTelegram(message); + + return NextResponse.json({ + id: userMessage.id, + status: "sent", + message: "Message sent to Horus" + }); + + } catch (error) { + console.error("Error:", error); + return NextResponse.json({ error: "Failed" }, { status: 500 }); + } +} + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const lastId = searchParams.get("after"); + + const messages = getMessages(); + + // Get messages after lastId + let filtered = messages; + if (lastId) { + const idx = messages.findIndex(m => m.id === lastId); + if (idx >= 0) { + filtered = messages.slice(idx + 1); + } + } + + // Return only assistant messages that haven't been fetched + const response = filtered.filter(m => m.role === "assistant" && !m.processed); + + // Mark as processed + if (response.length > 0) { + const processedIds = new Set(response.map(r => r.id)); + const updated = messages.map(m => + processedIds.has(m.id) ? { ...m, processed: true } : m + ); + saveMessages(updated); + } + + return NextResponse.json({ messages: response }); +} diff --git a/components/mission-control/HorusChat.tsx b/components/mission-control/HorusChat.tsx new file mode 100644 index 0000000..c4b09fc --- /dev/null +++ b/components/mission-control/HorusChat.tsx @@ -0,0 +1,150 @@ +"use client"; + +import { useState, useEffect, useRef } from "react"; +import { motion } from "framer-motion"; + +interface Message { + id: string; + role: "user" | "assistant"; + content: string; + timestamp: number; +} + +export default function VoiceChat() { + const [input, setInput] = useState(""); + const [messages, setMessages] = useState([ + { + id: "welcome", + role: "assistant", + content: "👁️ ¡Hola! Soy Horus. Chatea conmigo aquí o en Telegram. ¿En qué puedo ayudarte?", + timestamp: Date.now(), + } + ]); + const [isProcessing, setIsProcessing] = useState(false); + const messagesEndRef = useRef(null); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + const handleSend = async () => { + if (!input.trim() || isProcessing) return; + + const userMessage: Message = { + id: `user_${Date.now()}`, + role: "user", + content: input, + timestamp: Date.now(), + }; + + setMessages(prev => [...prev, userMessage]); + setInput(""); + setIsProcessing(true); + + try { + // Send to SiteMente AI agent + const res = await fetch("/api/chat/agent", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + message: userMessage.content, + locale: "es" + }), + }); + + const data = await res.json(); + + const botMessage: Message = { + id: `bot_${Date.now()}`, + role: "assistant", + content: data.response || "Entendido.", + timestamp: Date.now(), + }; + + setMessages(prev => [...prev, botMessage]); + } catch (e) { + console.error("Chat error:", e); + setMessages(prev => [...prev, { + id: `error_${Date.now()}`, + role: "assistant", + content: "Error de conexión. Prueba en Telegram.", + timestamp: Date.now(), + }]); + } finally { + setIsProcessing(false); + } + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + return ( +
+ {/* Header */} +
+
+ 💬 + Horus Chat +
+
+ {isProcessing && ( + Escribiendo... + )} +
+
+ + {/* Messages */} +
+ {messages.map((msg) => ( + +
+ {msg.content} +
+
+ ))} + +
+
+ + {/* Input */} +
+
+ setInput(e.target.value)} + onKeyPress={handleKeyPress} + placeholder="Escribe un mensaje..." + className="flex-1 bg-white/5 border border-white/10 rounded-lg px-4 py-2 text-sm text-white placeholder:text-white/40 focus:outline-none focus:border-brand-pink" + disabled={isProcessing} + /> + +
+

+ O chatea directamente en Telegram +

+
+
+ ); +} diff --git a/components/mission-control/MissionControlDashboard.tsx b/components/mission-control/MissionControlDashboard.tsx index 8d0e6a5..54c9c58 100644 --- a/components/mission-control/MissionControlDashboard.tsx +++ b/components/mission-control/MissionControlDashboard.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from "react"; import { useMissionControl } from "@/lib/mission-control/store"; import { TaskStatus } from "@/lib/mission-control/types"; -import VoiceChat from "./VoiceChat"; +import VoiceChat from "./HorusChat"; import MondayBoard from "./MondayBoard"; import { TaskCardsPanel } from "./TaskCardsPanel"; import { TaskHistoryPanel } from "./TaskHistoryPanel";