""" bot_runner.py - Dedicated Lightweight Telegram Bot Runner for Hermes Agent (Nous Portal Free Model) """ import os import sys import subprocess import threading import time import psutil import requests from dotenv import load_dotenv CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) load_dotenv(os.path.join(CURRENT_DIR, ".env")) BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "").strip() ALLOWED_USER_ID_RAW = os.getenv("ALLOWED_USER_ID", "0").strip() try: ALLOWED_USER_ID = int(ALLOWED_USER_ID_RAW) except ValueError: ALLOWED_USER_ID = 0 PROVIDER = os.getenv("HERMES_PROVIDER", "nous").strip() MODEL_NAME = os.getenv("HERMES_MODEL", "stepfun/step-3.7-flash:free").strip() print(f"[STARTUP] Bot Token: {BOT_TOKEN[:10]}... | Allowed ID: {ALLOWED_USER_ID} | Provider: {PROVIDER} | Model: {MODEL_NAME}", flush=True) # Hermes Agent Loader AIAgent = None try: from run_agent import AIAgent print("[STARTUP] run_agent.AIAgent loaded successfully!", flush=True) except Exception as e: print(f"[STARTUP WARN] Could not import AIAgent: {e}", flush=True) agent_instance = None def init_hermes_agent(model_name=MODEL_NAME): global agent_instance if not AIAgent: return None try: agent_instance = AIAgent( model=model_name, provider="nous", quiet_mode=True ) print(f"[STARTUP] Hermes Agent ({model_name}) via Nous Portal initialized successfully!", flush=True) return agent_instance except Exception as e: print(f"[STARTUP WARN] AIAgent Nous init ({model_name}): {e}", flush=True) return None init_hermes_agent(MODEL_NAME) START_TIME = time.time() session = requests.Session() def send_tg_message(chat_id: int, text: str, parse_mode: str = "Markdown"): if not BOT_TOKEN: return url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" payload = {"chat_id": chat_id, "text": text} if parse_mode: payload["parse_mode"] = parse_mode try: r = session.post(url, json=payload, timeout=20) res_json = r.json() if not res_json.get("ok"): payload.pop("parse_mode", None) session.post(url, json=payload, timeout=20) except Exception as e: print(f"[ERROR] Send TG message failed: {e}", flush=True) def send_typing(chat_id: int): if not BOT_TOKEN: return try: session.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendChatAction", json={"chat_id": chat_id, "action": "typing"}, timeout=5) except Exception: pass def handle_update(update: dict): global MODEL_NAME, agent_instance msg = update.get("message") if not msg: return sender = msg.get("from", {}) sender_id = sender.get("id") chat_id = msg.get("chat", {}).get("id") text = (msg.get("text") or "").strip() if sender_id != ALLOWED_USER_ID: print(f"[BLOCKED] Unauthorized message from User ID {sender_id} (@{sender.get('username')})", flush=True) return print(f"[RECV] From {sender_id}: {text}", flush=True) send_typing(chat_id) # Command: /start or /help if text in ["/start", "/help"]: welcome = ( "🛡️ *HERMES AGENT NOUS PORTAL CONTROLLER* 🛡️\n\n" "Chào bạn! Bot đã kết nối trực tiếp tài khoản **Nous Portal** qua OAuth.\n\n" "📌 *Danh sách lệnh:*\n" f"• Model mặc định: `{MODEL_NAME}` (Free 100%)\n" "• `/model` : Xem hoặc đổi sang các model Free khác của Nous.\n" "• `/status` : Xem Uptime, CPU, RAM và trạng thái hệ thống.\n" "• `/sh ` : Chạy lệnh shell/CLI trên máy chủ.\n" "• Nhắn tin bất kỳ để chat và nhờ Hermes Agent giải quyết task." ) send_tg_message(chat_id, welcome) return # Command: /status if text == "/status": uptime_sec = int(time.time() - START_TIME) uptime_str = f"{uptime_sec // 3600}h {(uptime_sec % 3600) // 60}m {uptime_sec % 60}s" mem = psutil.virtual_memory() stat_msg = ( f"📊 *HỆ THỐNG TELEMETRY*\n" f"━━━━━━━━━━━━━━━━━━━\n" f"⏱ *Uptime:* `{uptime_str}`\n" f"💻 *CPU Load:* `{psutil.cpu_percent()}%`\n" f"🧠 *RAM:* `{round(mem.used / 1024**2, 1)}MB / {round(mem.total / 1024**2, 1)}MB`\n" f"🌐 *Provider:* `Nous Portal (OAuth Active)`\n" f"🤖 *Active Model:* `{MODEL_NAME}`\n" f"🔑 *Authorized User ID:* `{ALLOWED_USER_ID}`\n" ) send_tg_message(chat_id, stat_msg) return # Command: /model if text == "/model": model_msg = ( f"🤖 *MODEL NOUS HIỆN TẠI:* `{MODEL_NAME}`\n\n" f"🎁 *Các model FREE trên Nous Portal:*\n" f"• `/model stepfun/step-3.7-flash:free` (Khuyên dùng - Cực nhanh & thông minh)\n" f"• `/model meituan/longcat-2.0:free` (Context dài)\n" f"• `/model upstage/solar-mini4:free` (Nhẹ & chuẩn xác)\n" f"• `/model inclusionai/ling-3.0-flash-fin:free`\n\n" f"💡 *Cách đổi:* Gõ `/model `" ) send_tg_message(chat_id, model_msg) return if text.startswith("/model "): new_model = text.split(" ", 1)[1].strip() new_agent = init_hermes_agent(new_model) if new_agent: MODEL_NAME = new_model send_tg_message(chat_id, f"✅ Đã chuyển sang Model Nous: `{MODEL_NAME}` thành công!") else: send_tg_message(chat_id, f"⚠️ Không thể khởi tạo model `{new_model}`. Vui lòng kiểm tra lại tên model.") return # Command: /sh or /cmd if text.startswith("/sh ") or text.startswith("/cmd "): cmd = text.split(" ", 1)[1].strip() try: proc = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=45) out = proc.stdout.strip() if proc.stdout else "" err = proc.stderr.strip() if proc.stderr else "" res = out if out else err if not res: res = "✅ (Exit code 0, no output)" if len(res) > 3800: res = res[:3800] + "\n... [Cắt bớt do quá dài]" send_tg_message(chat_id, f"```\n{res}\n```") except subprocess.TimeoutExpired: send_tg_message(chat_id, "⏱ Lệnh bị quá thời gian cho phép (Timeout 45s).") except Exception as e: send_tg_message(chat_id, f"❌ Lỗi CLI: {str(e)}") return # Chat with Hermes Agent via Nous Portal if not agent_instance: init_hermes_agent(MODEL_NAME) if not agent_instance: send_tg_message(chat_id, "⚠️ Chưa khởi tạo được Hermes Agent qua Nous Portal.") return try: reply = agent_instance.chat(text) send_tg_message(chat_id, reply) except Exception as e: send_tg_message(chat_id, f"❌ Lỗi Hermes Agent: {str(e)}") def main(): if not BOT_TOKEN: print("[ERROR] No TELEGRAM_BOT_TOKEN found in .env!", flush=True) return print(f"🚀 Telegram Long Polling Bot active for User ID: {ALLOWED_USER_ID} on Nous Portal Free Model", flush=True) last_update_id = 0 if ALLOWED_USER_ID != 0: send_tg_message(ALLOWED_USER_ID, f"🟢 *Hermes Agent* đã chuyển sang **Model Free của Nous Portal** (`{MODEL_NAME}`) hoàn toàn miễn phí!") while True: try: url = f"https://api.telegram.org/bot{BOT_TOKEN}/getUpdates" params = {"timeout": 20, "offset": last_update_id + 1} resp = session.get(url, params=params, timeout=30) data = resp.json() if data.get("ok"): updates = data.get("result", []) for u in updates: last_update_id = u["update_id"] try: handle_update(u) except Exception as e: print(f"[ERROR] handle_update: {e}", flush=True) else: time.sleep(2) except requests.exceptions.Timeout: continue except Exception as e: print(f"[NETWORK NOTE] {e}", flush=True) time.sleep(3) if __name__ == "__main__": main()