import os import asyncio from datetime import datetime, timedelta, timezone from contextlib import asynccontextmanager from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware import httpx from ai_engine import engine from telegram_bot import bot from modules.agents import MODULES, run_module from jarvis_links import get_all_links, get_random_template from database import log_activity, log_chat, log_module_output, start_keepalive from scheduler import scheduler SPACE_URL = os.environ.get("SPACE_URL", "https://bakulkrupuk2025-openclaw-neo-bot.hf.space") SUPABASE_URL_ENV = os.environ.get("SUPABASE_URL", "") SUPABASE_KEY_ENV = os.environ.get("SUPABASE_KEY", "") or os.environ.get("SUPABASE_ANON_KEY", "") SUPABASE_HEADERS_P = { "apikey": SUPABASE_KEY_ENV, "Authorization": f"Bearer {SUPABASE_KEY_ENV}", "Content-Type": "application/json", "Prefer": "return=representation" } # Spaces yang harus tetap hidup SPACES_TO_PING = [ f"{SPACE_URL}/health", "https://bakulkrupuk2025-jarvis-postiz.hf.space/healthz", ] # ============================================================ # KEEPALIVE FUNCTIONS # ============================================================ async def keep_space_alive(): """Ping diri sendiri tiap 4 menit biar tidak sleep""" await asyncio.sleep(30) while True: try: async with httpx.AsyncClient(timeout=10) as client: r = await client.get(f"{SPACE_URL}/health") print(f"💓 Self-ping OK: {r.status_code}") except Exception as e: print(f"⚠️ Self-ping failed: {e}") await asyncio.sleep(240) # 4 menit async def ping_all_spaces(): """Ping semua space terkait tiap 5 menit""" await asyncio.sleep(60) while True: for url in SPACES_TO_PING: try: async with httpx.AsyncClient(timeout=10) as client: r = await client.get(url) print(f"📡 Ping {url}: {r.status_code}") except Exception as e: print(f"⚠️ Ping failed {url}: {e}") await asyncio.sleep(5) await asyncio.sleep(300) # 5 menit # ============================================================ # LIFESPAN # ============================================================ @asynccontextmanager async def lifespan(app: FastAPI): asyncio.create_task(bot.start_polling()) asyncio.create_task(start_keepalive(SPACE_URL)) asyncio.create_task(scheduler.start()) asyncio.create_task(keep_space_alive()) asyncio.create_task(ping_all_spaces()) print("🚀 Jarvis Empire started!") yield bot.stop() scheduler.stop() app = FastAPI(title="JARVIS EMPIRE", lifespan=lifespan) app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) connections = [] DASHBOARD_HTML = """ JARVIS EMPIRE
ONLINE
BOT ACTIVE
DB CONNECTING
AUTO ON
⚡ PROVIDERS
AI AKTIF
💰 MODULES
9
AUTONOMOUS
🔗 AFFILIATES
2
PROGRAMS
📌 PINTEREST
QUEUE
🤖 JARVIS BRAIN
JARVIS
⚡ Sistem online. Selamat datang, Master Haryono. Semua modul aktif. Pinterest queue terhubung. Keepalive berjalan.
⚡ MODULES
📡 OUTPUT
Jalankan modul untuk melihat output
// Output modul muncul di sini...
💰 AFFILIATE LINKS
📌 PINTEREST QUEUE
Pending
Ready
Posted
📋 ACTIVITY LOG
--:--BOOTSystem initialized
⏰ SCHEDULER
🔥 TrendHijacker6 jam
✍️ Writer Hostinger8 jam
✍️ Writer Shopee10 jam
👻 GhostNetwork12 jam
📌 Pinterest Push6 jam
💓 Keepalive4 menit
""" # ============================================================ # ROUTES # ============================================================ @app.get("/", response_class=HTMLResponse) async def dashboard(): return DASHBOARD_HTML @app.get("/health") async def health_check(): return { "status": "alive", "service": "JARVIS EMPIRE", "timestamp": datetime.utcnow().isoformat() } @app.get("/api/status") async def get_status(): stats = engine.get_stats() from database import SUPABASE_URL return { "status": "online", "available_providers": len(stats["available"]), "providers": stats["available"], "active_provider": stats["active_provider"], "modules": list(MODULES.keys()), "db_connected": bool(SUPABASE_URL), "scheduler": "running", "timestamp": datetime.now().isoformat() } @app.post("/api/chat") async def chat(data: dict): message = data.get("message", "") if not message: return {"response": "Pesan kosong.", "provider": "none"} result = await engine.think(message) asyncio.create_task(log_chat("user", message)) asyncio.create_task(log_chat("jarvis", result["response"], result["provider"])) asyncio.create_task(log_activity("CHAT", message[:50])) return {"response": result["response"], "provider": result["provider"]} @app.post("/api/module") async def run_module_api(data: dict): module_name = data.get("module", "") if module_name not in MODULES: return {"error": "Not found", "output": "Module tidak ditemukan.", "module": module_name, "provider": "none"} kwargs = {k: v for k, v in data.items() if k != "module"} try: result = await MODULES[module_name].run(**kwargs) asyncio.create_task(log_module_output( module_name, result.get("topic", ""), result.get("output", ""), result.get("provider", "") )) asyncio.create_task(log_activity("MODULE", module_name)) return {"module": result.get("module", module_name), "output": result.get("output", ""), "provider": result.get("provider", "unknown")} except Exception as e: return {"error": str(e), "output": f"Error: {str(e)}", "module": module_name, "provider": "none"} @app.get("/api/template/{platform}") async def get_template(platform: str): template = get_random_template(platform) return {"template": template, "platform": platform} # ============================================================ # PINTEREST ENDPOINTS # ============================================================ PINTEREST_TOPICS = { "hostinger": [ "best web hosting for beginners 2025", "how to start a blog with Hostinger", "Hostinger review honest comparison", "cheap web hosting small business", "WordPress hosting tutorial step by step", "how to make money blogging affiliate", ], "general": [ "passive income ideas from home 2025", "side hustle ideas no investment", "make money online for beginners", "work from home productivity tips", "affiliate marketing beginner guide", "digital marketing tips small business", ] } @app.post("/api/pinterest/generate") async def pinterest_generate(request: Request): """Generate konten Pinterest via Gemini dan push ke Supabase queue""" import random try: body = await request.json() except: body = {} count = min(int(body.get("count", 3)), 5) niche_filter = body.get("niche", "both") results = [] for i in range(count): niche = random.choice(["hostinger", "general"]) if niche_filter == "both" else niche_filter topic = random.choice(PINTEREST_TOPICS.get(niche, PINTEREST_TOPICS["general"])) affiliate_link = "https://hostinger.com/?REFERRALCODE=FCIHARY08HBD" if niche == "hostinger" else "" try: # Generate via AI engine prompt = f"""Create Pinterest pin content for: "{topic}" Niche: {niche} {f'Include affiliate link: {affiliate_link}' if affiliate_link else ''} Reply ONLY in JSON (no markdown): {{"title":"max 100 chars catchy title","description":"200-500 chars engaging description with keywords","image_prompt":"detailed prompt for 1000x1500 Pinterest image","tags":["tag1","tag2","tag3","tag4","tag5"]}}""" ai_result = await engine.think(prompt) raw = ai_result.get("response", "") import json, re clean = re.sub(r'```json\n?|```\n?', '', raw).strip() parsed = json.loads(clean) scheduled_at = (datetime.now(timezone.utc) + timedelta(hours=(i+1)*2)).isoformat() payload = { "title": parsed["title"][:100], "description": parsed["description"][:500], "affiliate_link": affiliate_link or None, "niche": niche, "tags": parsed.get("tags", []), "image_prompt": parsed.get("image_prompt", f"Pinterest pin about {topic}"), "status": "pending", "scheduled_at": scheduled_at } async with httpx.AsyncClient(timeout=15) as client: r = await client.post( f"{SUPABASE_URL_ENV}/rest/v1/pinterest_content_queue", headers=SUPABASE_HEADERS_P, json=payload ) results.append({"success": True, "topic": topic, "niche": niche, "title": parsed["title"]}) asyncio.create_task(log_activity("PINTEREST", f"Queued: {parsed['title'][:40]}")) except Exception as e: results.append({"success": False, "topic": topic, "error": str(e)}) return { "status": "ok", "generated": sum(1 for r in results if r["success"]), "total_requested": count, "results": results } @app.get("/api/pinterest/status") async def pinterest_status(): """Status antrian Pinterest""" try: async with httpx.AsyncClient(timeout=15) as client: r = await client.get( f"{SUPABASE_URL_ENV}/rest/v1/pinterest_content_queue", headers=SUPABASE_HEADERS_P, params={"select": "status,niche,title,scheduled_at", "order": "created_at.desc", "limit": "20"} ) data = r.json() summary = {} for item in data: s = item.get("status", "unknown") summary[s] = summary.get(s, 0) + 1 return {"status": "ok", "summary": summary, "total": len(data), "recent": data[:5]} except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) @app.post("/api/n8n/webhook") async def n8n_webhook(request: Request): """Terima notifikasi dari n8n setelah pin posted""" try: body = await request.json() asyncio.create_task(log_activity("N8N", f"Pin posted: {body.get('title', '')[:40]}")) return {"status": "received"} except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) # ============================================================ # WEBSOCKET # ============================================================ @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() connections.append(websocket) try: while True: await websocket.receive_text() except WebSocketDisconnect: if websocket in connections: connections.remove(websocket) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7860)