Spaces:
Paused
Paused
Download Telegram_Job_Bot/dashboard_pro.html from telegramjobupload/README: direct link, hf CLI and curl.
- Browser
- Download file 38 kB
-
https://huggingface.co/spaces/telegramjobupload/README/resolve/main/Telegram_Job_Bot/dashboard_pro.html
- Command line
-
hf download hf://spaces/telegramjobupload/README/Telegram_Job_Bot/dashboard_pro.html
-
curl -L -o dashboard_pro.html https://huggingface.co/spaces/telegramjobupload/README/resolve/main/Telegram_Job_Bot/dashboard_pro.html
38 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Elite Job Bot | Command Center</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
| <style> | |
| * { font-family: 'Outfit', sans-serif; } | |
| body { background: #060918; color: #f1f5f9; min-height: 100vh; overflow-x: hidden; } | |
| .orb { position: fixed; border-radius: 50%; filter: blur(80px); opacity: 0.15; pointer-events: none; animation: float 8s ease-in-out infinite; } | |
| .orb1 { width: 400px; height: 400px; background: #3b82f6; top: -100px; left: -100px; animation-delay: 0s; } | |
| .orb2 { width: 350px; height: 350px; background: #8b5cf6; bottom: -80px; right: -80px; animation-delay: 3s; } | |
| .orb3 { width: 250px; height: 250px; background: #06b6d4; top: 40%; left: 40%; animation-delay: 5s; } | |
| @keyframes float { 0%,100%{transform:translateY(0) scale(1)} 50%{transform:translateY(-30px) scale(1.05)} } | |
| .glass { background: rgba(255,255,255,0.04); backdrop-filter: blur(20px); border: 1px solid rgba(255,255,255,0.08); } | |
| .glass-strong { background: rgba(255,255,255,0.07); backdrop-filter: blur(30px); border: 1px solid rgba(255,255,255,0.12); } | |
| .stat-card { background: linear-gradient(135deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02)); border: 1px solid rgba(255,255,255,0.08); transition: all 0.3s ease; } | |
| .stat-card:hover { transform: translateY(-4px); border-color: rgba(99,102,241,0.4); box-shadow: 0 20px 40px rgba(0,0,0,0.3); } | |
| .glow-blue { text-shadow: 0 0 30px rgba(59,130,246,0.6); } | |
| .glow-green { text-shadow: 0 0 20px rgba(34,197,94,0.5); } | |
| .feed-card { transition: all 0.2s ease; } | |
| .channel-chip { transition: all 0.2s ease; cursor: default; } | |
| .ring-track { stroke: rgba(255,255,255,0.08); } | |
| .ring-progress { stroke: url(#ringGrad); stroke-dasharray: 339.3; stroke-dashoffset: {{stroke_offset}}; stroke-linecap: round; transition: stroke-dashoffset 1s ease; transform: rotate(-90deg); transform-origin: center; } | |
| .pulse-dot { animation: pulse-glow 2s infinite; } | |
| @keyframes pulse-glow { 0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,0.4)} 50%{box-shadow:0 0 0 8px rgba(34,197,94,0)} } | |
| .ai-online { background: linear-gradient(135deg, rgba(34,197,94,0.15), rgba(16,185,129,0.05)); border-color: rgba(34,197,94,0.3); } | |
| /* NEW PRO STYLES */ | |
| .pro-gradient { | |
| background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .pro-card { | |
| background: linear-gradient(180deg, rgba(30,41,59,0.7) 0%, rgba(15,23,42,0.8) 100%); | |
| border: 1px solid rgba(255,255,255,0.1); | |
| box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5); | |
| backdrop-filter: blur(20px); | |
| } | |
| .pro-btn { | |
| position: relative; overflow: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .pro-btn::after { | |
| content: ''; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; | |
| background: radial-gradient(circle, rgba(255,255,255,0.2) 0%, transparent 60%); | |
| opacity: 0; transition: opacity 0.3s ease; transform: scale(0.5); | |
| } | |
| .pro-btn:hover::after { opacity: 1; transform: scale(1); } | |
| .pro-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(0,0,0,0.3); } | |
| .ai-offline { background: linear-gradient(135deg, rgba(239,68,68,0.15), rgba(220,38,38,0.05)); border-color: rgba(239,68,68,0.3); } | |
| #countdown { color: #94a3b8; } | |
| .glass-btn { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } | |
| .glass-btn:hover { transform: translateY(-2px) scale(1.05); filter: brightness(1.2); } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Background Orbs --> | |
| <div class="orb orb1"></div> | |
| <div class="orb orb2"></div> | |
| <div class="orb orb3"></div> | |
| <div class="relative z-10 max-w-6xl mx-auto px-4 py-8"> | |
| <!-- ENHANCED PRO TOP BAR --> | |
| <header class="pro-card rounded-3xl p-8 mb-8 flex flex-col md:flex-row justify-between items-center relative overflow-hidden"> | |
| <div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-20 mix-blend-overlay"></div> | |
| <div class="absolute -top-24 -left-24 w-64 h-64 bg-blue-500 rounded-full blur-[100px] opacity-20 pointer-events-none"></div> | |
| <div class="relative z-10 flex flex-col md:flex-row items-center gap-6"> | |
| <div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-600 to-indigo-800 flex items-center justify-center shadow-[0_0_40px_rgba(37,99,235,0.5)] border border-blue-400/30"> | |
| <span class="text-5xl animate-pulse">β‘</span> | |
| </div> | |
| <div class="text-center md:text-left"> | |
| <h1 class="text-4xl md:text-5xl font-black tracking-tighter text-white drop-shadow-2xl flex items-center gap-3"> | |
| Job Bot <span class="pro-gradient">PRO</span> | |
| </h1> | |
| <p class="text-blue-200 mt-2 font-semibold tracking-wider text-sm flex items-center justify-center md:justify-start gap-2"> | |
| <span class="w-2 h-2 rounded-full bg-blue-400 animate-pulse"></span> Autonomous Telegram Engine | |
| </p> | |
| </div> | |
| </div> | |
| <div class="relative z-10 mt-6 md:mt-0 flex flex-col items-center md:items-end gap-4"> | |
| <div class="flex gap-4"> | |
| <a href="https://t.me/" target="_blank" class="pro-btn bg-gradient-to-r from-blue-600 to-blue-500 border border-blue-400/50 text-white px-6 py-2.5 rounded-xl text-sm font-bold flex items-center gap-2 shadow-[0_0_20px_rgba(37,99,235,0.4)]"> | |
| βοΈ Open Telegram | |
| </a> | |
| </div> | |
| <div class="flex items-center gap-6"> | |
| <div class="text-center"> | |
| <p class="text-[10px] text-gray-500 mb-1 uppercase tracking-widest">Active API</p> | |
| <p class="font-black text-blue-400">#{{active_key_num}}/{{gemini_key_count}}</p> | |
| </div> | |
| <div class="flex items-center gap-2 bg-green-900/40 border border-green-500/40 px-5 py-2 rounded-full backdrop-blur-md"> | |
| <div class="w-2.5 h-2.5 rounded-full bg-green-500 shadow-[0_0_10px_#22c55e] animate-pulse pulse-dot"></div> | |
| <span class="text-xs font-bold text-green-400 tracking-widest uppercase">System Online</span> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Stats Row --> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="pro-card rounded-3xl p-6 text-center transform transition-all hover:scale-105 hover:-translate-y-2 relative overflow-hidden group"> | |
| <div class="text-4xl mb-3 drop-shadow-[0_0_15px_rgba(255,255,255,0.8)] group-hover:scale-110 transition-transform">π</div> | |
| <div class="text-4xl font-black text-white mb-1">{{stats.get('applied', 0)}}</div> | |
| <div class="text-xs text-gray-400 uppercase tracking-widest">Applied Today</div> | |
| </div> | |
| <div class="pro-card rounded-3xl p-6 text-center transform transition-all hover:scale-105 hover:-translate-y-2 relative overflow-hidden group"> | |
| <div class="text-4xl mb-3 drop-shadow-[0_0_15px_rgba(249,115,22,0.8)] group-hover:scale-110 transition-transform">π₯</div> | |
| <div class="text-4xl font-black text-orange-400 mb-1">{{stats.get('current_streak', 0)}}</div> | |
| <div class="text-xs text-gray-400 uppercase tracking-widest">Day Streak</div> | |
| </div> | |
| <div class="pro-card rounded-3xl p-6 text-center transform transition-all hover:scale-105 hover:-translate-y-2 relative overflow-hidden group"> | |
| <div class="text-4xl mb-3 drop-shadow-[0_0_15px_rgba(236,72,153,0.8)] group-hover:scale-110 transition-transform">π―</div> | |
| <div class="text-4xl font-black text-blue-400 mb-1">{{len(applied)}}</div> | |
| <div class="text-xs text-gray-400 uppercase tracking-widest">Total Processed</div> | |
| </div> | |
| <div class="pro-card rounded-3xl p-6 text-center transform transition-all hover:scale-105 hover:-translate-y-2 relative overflow-hidden group"> | |
| <div class="text-3xl mb-2">βοΈ</div> | |
| <div class="text-4xl font-black text-gray-400 mb-1">{{stats.get('skipped', 0)}}</div> | |
| <div class="text-xs text-gray-400 uppercase tracking-widest">Skipped Today</div> | |
| </div> | |
| </div> | |
| <!-- Middle Row: AI Status + Success Ring + Config --> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> | |
| <!-- AI Engines --> | |
| <div class="glass rounded-2xl p-5"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">π§ AI Engine Status</h2> | |
| <div class="space-y-3"> | |
| <div class="{{'ai-online' if gemini_status == 'ONLINE' else 'ai-offline'}} rounded-xl p-3 border flex items-center justify-between"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">β¨</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">Google Gemini</p> | |
| <p class="text-xs text-gray-400">gemini-2.5-flash</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold {{'text-green-400' if gemini_status == 'ONLINE' else 'text-red-400'}}">{{gemini_status}}</span> | |
| </div> | |
| <div class="{{'ai-online' if groq_status == 'ONLINE' else 'ai-offline'}} rounded-xl p-3 border flex items-center justify-between"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">β‘</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">Groq Llama 4</p> | |
| <p class="text-xs text-gray-400">llama-4-scout (Backup)</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold {{'text-green-400' if groq_status == 'ONLINE' else 'text-red-400'}}">{{groq_status}}</span> | |
| </div> | |
| <div class="rounded-xl p-3 border border-white/10 bg-white/3 flex items-center justify-between"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">π</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">Resume</p> | |
| <p class="text-xs text-gray-400">resume.pdf</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold {{'text-green-400' if os.path.exists(RESUME_FILE) else 'text-red-400'}}">{{'LOADED' if os.path.exists(RESUME_FILE) else 'MISSING'}}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Active Automations (Expanded) --> | |
| <div class="glass rounded-2xl p-5 relative overflow-hidden group"> | |
| <div class="absolute inset-0 bg-gradient-to-br from-indigo-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">π‘οΈ Active Subsystems</h2> | |
| <div class="space-y-3 relative z-10"> | |
| <div class="rounded-xl p-3 bg-white/5 border border-white/10 flex items-center justify-between hover:bg-white/10 transition-all cursor-default"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">π</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">Anti-Ban Engine</p> | |
| <p class="text-xs text-gray-400">Jitter & Mimicry</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold text-green-400 drop-shadow-[0_0_8px_rgba(74,222,128,0.5)]">ACTIVE</span> | |
| </div> | |
| <div class="rounded-xl p-3 bg-white/5 border border-white/10 flex items-center justify-between hover:bg-white/10 transition-all cursor-default"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">π³οΈ</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">DOM Piercing</p> | |
| <p class="text-xs text-gray-400">Shadow & iFrame extraction</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold text-green-400 drop-shadow-[0_0_8px_rgba(74,222,128,0.5)]">ACTIVE</span> | |
| </div> | |
| <div class="rounded-xl p-3 bg-white/5 border border-white/10 flex items-center justify-between hover:bg-white/10 transition-all cursor-default"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">π§</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">IMAP Auto-OTP</p> | |
| <p class="text-xs text-gray-400">Verification Bypass</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold {{'text-green-400 drop-shadow-[0_0_8px_rgba(74,222,128,0.5)]' if imap_status == 'CONNECTED' else 'text-gray-500'}}">{{imap_status}}</span> | |
| </div> | |
| <div class="rounded-xl p-3 bg-white/5 border border-white/10 flex items-center justify-between hover:bg-white/10 transition-all cursor-default"> | |
| <div class="flex items-center gap-3"> | |
| <span class="text-xl">π»</span> | |
| <div> | |
| <p class="text-sm font-semibold text-white">Ghost Mode</p> | |
| <p class="text-xs text-gray-400">Live Telemetry</p> | |
| </div> | |
| </div> | |
| <span class="text-xs font-bold {{'text-fuchsia-400 drop-shadow-[0_0_8px_rgba(232,121,249,0.5)]' if ghost_mode_active else 'text-gray-500'}}">{{'STREAMING' if ghost_mode_active else 'STANDBY'}}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Success Rate Ring --> | |
| <div class="glass rounded-2xl p-5 flex flex-col items-center justify-center text-center"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">π Success Rate</h2> | |
| <div class="relative w-40 h-40"> | |
| <svg class="w-full h-full" viewBox="0 0 120 120"> | |
| <defs> | |
| <linearGradient id="ringGrad" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" style="stop-color:#3b82f6"/> | |
| <stop offset="100%" style="stop-color:#8b5cf6"/> | |
| </linearGradient> | |
| </defs> | |
| <circle class="ring-track" cx="60" cy="60" r="54" fill="none" stroke-width="10"/> | |
| <circle class="ring-progress" cx="60" cy="60" r="54" fill="none" stroke-width="10"/> | |
| </svg> | |
| <div class="absolute inset-0 flex flex-col items-center justify-center"> | |
| <span class="text-4xl font-black text-white">{{success_pct}}%</span> | |
| <span class="text-xs text-gray-400">Applied</span> | |
| </div> | |
| </div> | |
| <p class="text-xs text-gray-500 mt-3">{{total_applied}} applied Β· {{total_skipped}} skipped today</p> | |
| </div> | |
| <!-- Config Panel --> | |
| <div class="glass rounded-2xl p-5"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">βοΈ Configuration</h2> | |
| <ul class="space-y-3 text-sm"> | |
| <li class="flex justify-between pb-2 border-b border-white/5"> | |
| <span class="text-gray-400">Primary Channel</span> | |
| <span class="font-mono text-blue-300 text-xs">@{{TARGET_CHANNEL}}</span> | |
| </li> | |
| <li class="flex justify-between pb-2 border-b border-white/5"> | |
| <span class="text-gray-400">Total Channels</span> | |
| <span class="text-green-400 font-bold">{{len(TARGET_CHANNELS)}} Active</span> | |
| </li> | |
| <li class="flex justify-between pb-2 border-b border-white/5"> | |
| <span class="text-gray-400">Gemini Keys</span> | |
| <span class="text-blue-400 font-bold">{{gemini_key_count}} Keys Β· #{{active_key_num}} Active</span> | |
| </li> | |
| <li class="flex justify-between pb-2 border-b border-white/5"> | |
| <span class="text-gray-400">Q&A Brain Size</span> | |
| <span class="text-fuchsia-400 font-bold">{{qa_size}} Semantic Memories</span> | |
| </li> | |
| <li class="flex flex-col pb-2 border-b border-white/5"> | |
| <span class="text-gray-400 mb-1">Telegram Chat ID</span> | |
| <span class="font-mono text-xs text-gray-500 truncate">{{active_chat_id if active_chat_id else "Send /start to bot"}}</span> | |
| </li> | |
| <li class="flex justify-between"> | |
| <span class="text-gray-400">Bot Status</span> | |
| <span class="text-green-400 font-bold">{{'βΈ PAUSED' if BOT_PAUSED else 'βΆ RUNNING'}}</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <!-- Control Panel --> | |
| <div class="glass rounded-2xl p-5 mb-6"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">ποΈ Bot Controls</h2> | |
| <div class="flex flex-wrap gap-3 items-center"> | |
| <button id="pauseBtn" onclick="controlBot('pause')" class="glass-btn px-4 py-2 rounded-xl font-bold text-sm" style="background:linear-gradient(135deg,#f59e0b,#d97706);color:#fff;box-shadow:0 4px 15px rgba(245,158,11,0.4)"> | |
| βΈοΈ Pause | |
| </button> | |
| <button id="resumeBtn" onclick="controlBot('resume')" class="glass-btn px-4 py-2 rounded-xl font-bold text-sm" style="background:linear-gradient(135deg,#22c55e,#16a34a);color:#fff;box-shadow:0 4px 15px rgba(34,197,94,0.4)"> | |
| βΆοΈ Resume | |
| </button> | |
| <button onclick="window.location.reload()" class="glass-btn px-3 py-1.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;box-shadow:0 4px 15px rgba(59,130,246,0.4)"> | |
| π Refresh | |
| </button> | |
| <button onclick="forceScan()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#8b5cf6,#6d28d9);color:#fff;box-shadow:0 4px 15px rgba(139,92,246,0.4)"> | |
| π‘ Force Scan | |
| </button> | |
| <button onclick="rotateKey()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#ec4899,#be185d);color:#fff;box-shadow:0 4px 15px rgba(236,72,153,0.4)"> | |
| π Rotate Key | |
| </button> | |
| <button onclick="regenResume()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#6366f1,#4338ca);color:#fff;box-shadow:0 4px 15px rgba(99,102,241,0.4)"> | |
| π Regen Resume | |
| </button> | |
| <button onclick="testHealth()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#06b6d4,#0891b2);color:#fff;box-shadow:0 4px 15px rgba(6,182,212,0.4)"> | |
| π©Ί Test AI Health | |
| </button> | |
| <button onclick="scanInbox()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#a855f7,#7e22ce);color:#fff;box-shadow:0 4px 15px rgba(168,85,247,0.4)"> | |
| π§ Scan Inbox (Interviews) | |
| </button> | |
| <button onclick="if(confirm('Are you sure you want to delete all application logs and stats?')) resetHistory()" class="pro-btn px-4 py-2.5 rounded-xl font-bold text-xs" style="background:linear-gradient(135deg,#ef4444,#b91c1c);color:#fff;box-shadow:0 4px 15px rgba(239,68,68,0.4)"> | |
| ποΈ Reset Logs | |
| </button> | |
| <div id="ctrlStatus" class="text-sm text-gray-400 ml-2"></div> | |
| <div class="ml-auto text-xs px-3 py-1.5 rounded-full font-semibold" id="botStateBadge" style="background:{{'rgba(34,197,94,0.15)' if not BOT_PAUSED else 'rgba(245,158,11,0.15)'}};border:1px solid {{'rgba(34,197,94,0.4)' if not BOT_PAUSED else 'rgba(245,158,11,0.4)'}};color:{{'#4ade80' if not BOT_PAUSED else '#fbbf24'}}"> | |
| {{'βΆοΈ RUNNING' if not BOT_PAUSED else 'βΈοΈ PAUSED'}} | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6 pt-4 border-t border-white/10"> | |
| <!-- Quick Apply --> | |
| <div> | |
| <h3 class="text-xs text-gray-400 font-bold mb-2 uppercase">π― Manual Quick Apply</h3> | |
| <div class="flex gap-2"> | |
| <input type="text" id="manualUrl" placeholder="Paste Job URL (e.g. Workday, Lever)..." class="flex-1 bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-blue-500"> | |
| <button onclick="manualApply()" class="pro-btn bg-gradient-to-r from-blue-600 to-indigo-600 text-white px-6 py-2 rounded-xl text-sm font-bold shadow-[0_4px_15px_rgba(37,99,235,0.4)]">Apply</button> | |
| </div> | |
| </div> | |
| <!-- Update Links --> | |
| <div> | |
| <h3 class="text-xs text-gray-400 font-bold mb-2 uppercase">π Quick Update Links</h3> | |
| <div class="flex flex-col gap-2"> | |
| <div class="flex gap-2"> | |
| <input type="text" id="linkedinUrl" placeholder="Paste LinkedIn URL..." class="flex-1 bg-black/40 border border-white/10 rounded-lg px-3 py-1.5 text-xs text-white placeholder-gray-500 focus:outline-none focus:border-blue-500"> | |
| <button onclick="updateProfile('linkedin')" class="pro-btn bg-white/10 hover:bg-white/20 text-white px-5 py-2 rounded-xl text-xs font-bold border border-white/10">Save</button> | |
| </div> | |
| <div class="flex gap-2"> | |
| <input type="text" id="githubUrl" placeholder="Paste GitHub URL..." class="flex-1 bg-black/40 border border-white/10 rounded-lg px-3 py-1.5 text-xs text-white placeholder-gray-500 focus:outline-none focus:border-blue-500"> | |
| <button onclick="updateProfile('github')" class="pro-btn bg-white/10 hover:bg-white/20 text-white px-5 py-2 rounded-xl text-xs font-bold border border-white/10">Save</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6 pt-4 border-t border-white/10"> | |
| <!-- Advanced Config & Exports --> | |
| <div> | |
| <h3 class="text-xs text-gray-400 font-bold mb-2 uppercase">βοΈ Advanced Config & Resume</h3> | |
| <div class="flex flex-col gap-2"> | |
| <div class="flex gap-2"> | |
| <input type="text" id="newChannel" placeholder="Add Telegram Channel (e.g. @JobAlerts)..." class="flex-1 bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-blue-500"> | |
| <button onclick="addChannel()" class="pro-btn bg-gradient-to-r from-purple-600 to-pink-600 text-white px-6 py-2 rounded-xl text-sm font-bold shadow-[0_4px_15px_rgba(147,51,234,0.4)]">Add</button> | |
| </div> | |
| <div class="relative border-2 border-dashed border-white/20 rounded-lg p-3 text-center hover:border-blue-500/50 transition-all bg-white/5 group overflow-hidden"> | |
| <input type="file" id="resumeUpload" accept=".pdf" onchange="uploadResume()" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"> | |
| <div class="relative z-0 group-hover:scale-105 transition-all"> | |
| <span class="text-xl block mb-1">π</span> | |
| <p class="text-xs text-gray-300 font-bold">Drag & Drop new Resume PDF</p> | |
| <p class="text-[10px] text-gray-500 mt-1">Or click to browse (Instantly Updates System)</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-xs text-gray-400 font-bold mb-2 uppercase">π₯ Data Exports & Links</h3> | |
| <div class="flex flex-wrap gap-2"> | |
| <button onclick="window.open('{{notion_url}}', '_blank')" class="pro-btn bg-gradient-to-r from-blue-600 to-indigo-600 text-white px-5 py-2.5 rounded-xl text-xs font-bold transition-all shadow-[0_4px_15px_rgba(79,70,229,0.4)]">βοΈ Open Notion CRM</button> | |
| <button onclick="window.location.href='api/download_log'" class="pro-btn bg-gradient-to-r from-teal-600 to-emerald-600 text-white px-5 py-2.5 rounded-xl text-xs font-bold transition-all shadow-[0_4px_15px_rgba(20,184,166,0.3)]">CSV Log</button> | |
| <button onclick="window.location.href='api/download_profile'" class="pro-btn bg-gradient-to-r from-gray-700 to-gray-600 text-white px-5 py-2.5 rounded-xl text-xs font-bold transition-all shadow-[0_4px_15px_rgba(75,85,99,0.3)]">profile.json</button> | |
| <button onclick="window.location.href='api/download_qa'" class="pro-btn bg-gradient-to-r from-purple-600 to-fuchsia-600 text-white px-5 py-2.5 rounded-xl text-xs font-bold transition-all shadow-[0_4px_15px_rgba(168,85,247,0.3)]">Q&A Brain</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Channels Being Monitored --> | |
| <div class="glass rounded-2xl p-5 mb-6"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest mb-4">π‘ Channels Being Monitored ({{len(TARGET_CHANNELS)}} Active)</h2> | |
| <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-2"> | |
| {{channels_html|safe}} | |
| </div> | |
| </div> | |
| <!-- Live Activity Feed --> | |
| <div class="glass rounded-2xl p-5 mb-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-sm font-bold text-gray-300 uppercase tracking-widest">β‘ Live Activity Feed</h2> | |
| <span class="text-xs px-3 py-1 rounded-full font-semibold" style="background:rgba(139,92,246,0.2);border:1px solid rgba(139,92,246,0.4);color:#c4b5fd">Auto-updating</span> | |
| </div> | |
| <div>{{recent_jobs_html|safe}}</div> | |
| </div> | |
| <!-- π‘ Job Radar Section --> | |
| <div class="glass rounded-2xl p-5 mb-6" id="radarSection"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <div> | |
| <h2 class="text-sm font-bold uppercase tracking-widest" style="color:#f59e0b">π‘ Job Radar β Multi-Platform</h2> | |
| <p class="text-xs text-gray-400 mt-0.5">Tamil Nadu focus β’ Remote β’ 2025 jobs only β’ Sends links to your Telegram</p> | |
| </div> | |
| <button onclick="triggerRadar()" id="radarBtn" | |
| class="text-xs font-bold px-4 py-2 rounded-xl transition-all hover:scale-105" | |
| style="background:linear-gradient(135deg,#f59e0b,#d97706);color:#000;box-shadow:0 0 20px rgba(245,158,11,0.4)"> | |
| π Scan Now | |
| </button> | |
| </div> | |
| <div id="radarStatus" class="text-xs text-gray-400 mb-4">Last scan: Loading...</div> | |
| <div id="radarJobList" class="space-y-2"> | |
| <div class="text-center text-gray-500 text-xs py-4">β³ Loading radar results...</div> | |
| </div> | |
| </div> | |
| <footer class="text-center text-xs text-gray-600 pb-4"> | |
| Elite Job Bot v3.0 β’ Hybrid AI Engine (Gemini + Groq) β’ Refreshing every 60s | |
| </footer> | |
| </div> | |
| <script> | |
| let secs = 60; | |
| const el = document.getElementById('countdown'); | |
| setInterval(() => { | |
| secs--; | |
| el.textContent = secs + 's'; | |
| if (secs <= 0) window.location.reload(); | |
| }, 1000); | |
| // Load radar results on page load | |
| async function loadRadar() { | |
| try { | |
| const res = await fetch('api/radar'); | |
| const data = await res.json(); | |
| document.getElementById('radarStatus').textContent = | |
| `Last scan: ${data.last_scan || 'Never'} β’ ${data.total || 0} new jobs found`; | |
| const container = document.getElementById('radarJobList'); | |
| if (!data.jobs || data.jobs.length === 0) { | |
| container.innerHTML = '<div class="text-center text-gray-500 text-xs py-4">No new jobs found yet. Click "Scan Now" to start!</div>'; | |
| return; | |
| } | |
| // Group by source | |
| const grouped = {}; | |
| data.jobs.forEach(j => { | |
| if (!grouped[j.source]) grouped[j.source] = []; | |
| grouped[j.source].push(j); | |
| }); | |
| const timeAgo = (dateStr) => { | |
| if (!dateStr || dateStr.length < 4) return dateStr; | |
| try { | |
| const date = new Date(dateStr); | |
| if (isNaN(date)) return dateStr; | |
| const seconds = Math.floor((new Date() - date) / 1000); | |
| let interval = seconds / 31536000; | |
| if (interval > 1) return Math.floor(interval) + " years ago"; | |
| interval = seconds / 2592000; | |
| if (interval > 1) return Math.floor(interval) + " months ago"; | |
| interval = seconds / 86400; | |
| if (interval > 1) return Math.floor(interval) + " days ago"; | |
| interval = seconds / 3600; | |
| if (interval > 1) return Math.floor(interval) + " hours ago"; | |
| interval = seconds / 60; | |
| if (interval > 1) return Math.floor(interval) + " mins ago"; | |
| return "Just now"; | |
| } catch(e) { return dateStr; } | |
| }; | |
| let html = ''; | |
| for (const [src, jobs] of Object.entries(grouped)) { | |
| html += `<div class="mb-4">`; | |
| html += `<h3 class="text-xs font-bold mb-2 uppercase tracking-widest" style="color:#f59e0b">${src}</h3>`; | |
| html += `<div class="grid grid-cols-1 md:grid-cols-2 gap-2">`; | |
| jobs.forEach(j => { | |
| const displayDate = timeAgo(j.date_posted); | |
| const descSnippet = j.description ? `<div class="text-gray-500 text-[9px] mt-1.5 line-clamp-2 italic leading-tight border-l-2 border-white/10 pl-2">${j.description}</div>` : ''; | |
| html += ` | |
| <a href="${j.link}" target="_blank" class="block p-3 rounded-xl border transition-all hover:scale-[1.02] hover:border-yellow-500/50 relative overflow-hidden" | |
| style="background:rgba(245,158,11,0.05);border-color:rgba(245,158,11,0.15)"> | |
| <div class="font-bold text-xs text-yellow-300 truncate pr-6">${j.title}</div> | |
| <div class="text-gray-400 text-[10px] mt-1 font-semibold truncate">π’ ${j.company}</div> | |
| <div class="text-gray-400 text-[10px] mt-0.5 truncate">π ${j.location} β’ ποΈ ${displayDate}</div> | |
| ${descSnippet} | |
| <div class="mt-2 text-[10px] font-bold uppercase tracking-wide" style="color:#f59e0b">π Click to Apply Manually β</div> | |
| </a>`; | |
| }); | |
| html += `</div></div>`; | |
| } | |
| container.innerHTML = html; | |
| } catch(e) { | |
| document.getElementById('radarJobList').innerHTML = '<div class="text-red-400 text-xs">Failed to load radar data.</div>'; | |
| } | |
| } | |
| async function triggerRadar() { | |
| const btn = document.getElementById('radarBtn'); | |
| const status = document.getElementById('radarStatus'); | |
| btn.textContent = 'β³ Scanning...'; | |
| btn.disabled = true; | |
| try { | |
| const res = await fetch('api/run_radar', {method: 'POST'}); | |
| const data = await res.json(); | |
| status.textContent = 'β ' + data.message; | |
| status.style.color = '#4ade80'; | |
| setTimeout(loadRadar, 65000); // Reload results after ~1 min | |
| } catch(e) { | |
| status.textContent = 'β Scan failed'; | |
| } | |
| btn.textContent = 'π Scan Now'; | |
| btn.disabled = false; | |
| } | |
| loadRadar(); // Load on page open | |
| async function controlBot(action) { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = action === 'pause' ? 'β³ Pausing...' : 'β³ Resuming...'; | |
| try { | |
| const res = await fetch('api/' + action, {method: 'POST'}); | |
| const data = await res.json(); | |
| statusEl.textContent = data.message; | |
| statusEl.style.color = action === 'pause' ? '#fbbf24' : '#4ade80'; | |
| const badge = document.getElementById('botStateBadge'); | |
| if (action === 'pause') { | |
| badge.textContent = 'βΈοΈ PAUSED'; | |
| badge.style.color = '#fbbf24'; | |
| badge.style.background = 'rgba(245,158,11,0.15)'; | |
| badge.style.border = '1px solid rgba(245,158,11,0.4)'; | |
| } else { | |
| badge.textContent = 'βΆοΈ RUNNING'; | |
| badge.style.color = '#4ade80'; | |
| badge.style.background = 'rgba(34,197,94,0.15)'; | |
| badge.style.border = '1px solid rgba(34,197,94,0.4)'; | |
| } | |
| setTimeout(() => statusEl.textContent = '', 3000); | |
| } catch(e) { | |
| statusEl.textContent = 'β Error: ' + e; | |
| } | |
| } | |
| async function forceScan() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Forcing scan...'; | |
| try { | |
| const res = await fetch('api/run_radar', {method: 'POST'}); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => statusEl.textContent = '', 4000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function rotateKey() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Rotating key...'; | |
| try { | |
| const res = await fetch('api/rotate_key', {method: 'POST'}); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => window.location.reload(), 2000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function manualApply() { | |
| const url = document.getElementById('manualUrl').value; | |
| if(!url) return; | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Initiating manual apply...'; | |
| try { | |
| const res = await fetch('api/manual_apply', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({url: url}) | |
| }); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| document.getElementById('manualUrl').value = ''; | |
| setTimeout(() => statusEl.textContent = '', 4000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function updateProfile(field) { | |
| const val = document.getElementById(field + 'Url').value; | |
| if(!val) return; | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Updating ' + field + '...'; | |
| try { | |
| const res = await fetch('api/update_profile', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({field: field, value: val}) | |
| }); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β Updated!'; | |
| statusEl.style.color = '#4ade80'; | |
| document.getElementById(field + 'Url').value = ''; | |
| setTimeout(() => statusEl.textContent = '', 3000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function regenResume() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Regenerating PDF...'; | |
| try { | |
| const res = await fetch('api/regenerate_resume', {method: 'POST'}); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => statusEl.textContent = '', 3000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function resetHistory() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Resetting logs...'; | |
| try { | |
| const res = await fetch('api/reset_history', {method: 'POST'}); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => window.location.reload(), 2000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function testHealth() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Pinging AI Models...'; | |
| try { | |
| const res = await fetch('api/health_check'); | |
| const data = await res.json(); | |
| alert(`π©Ί AI Engine Health:\n\nGemini: ${data.gemini}\nGroq: ${data.groq}`); | |
| statusEl.textContent = ''; | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function scanInbox() { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Scanning Gmail for Interviews...'; | |
| try { | |
| const res = await fetch('api/scan_inbox', {method: 'POST'}); | |
| const data = await res.json(); | |
| alert(data.message); | |
| statusEl.textContent = 'β Inbox scan complete!'; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => statusEl.textContent = '', 3000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function uploadResume() { | |
| const fileInput = document.getElementById('resumeUpload'); | |
| if (!fileInput.files.length) return; | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Uploading and syncing Resume...'; | |
| const formData = new FormData(); | |
| formData.append('resume', fileInput.files[0]); | |
| try { | |
| const res = await fetch('api/upload_resume', {method: 'POST', body: formData}); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| setTimeout(() => window.location.reload(), 2000); | |
| } catch(e) { statusEl.textContent = 'β Error uploading resume'; } | |
| } | |
| async function markCRM(url, newStatus) { | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Updating CRM...'; | |
| try { | |
| const res = await fetch('api/mark_crm', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({url: url, status: newStatus}) | |
| }); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β Updated to ' + newStatus + '!'; | |
| setTimeout(() => window.location.reload(), 1000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| async function addChannel() { | |
| const ch = document.getElementById('newChannel').value; | |
| if(!ch) return; | |
| const statusEl = document.getElementById('ctrlStatus'); | |
| statusEl.textContent = 'β³ Adding channel...'; | |
| try { | |
| const res = await fetch('api/add_channel', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({channel: ch}) | |
| }); | |
| const data = await res.json(); | |
| statusEl.textContent = 'β ' + data.message; | |
| statusEl.style.color = '#4ade80'; | |
| document.getElementById('newChannel').value = ''; | |
| setTimeout(() => window.location.reload(), 2000); | |
| } catch(e) { statusEl.textContent = 'β Error'; } | |
| } | |
| </script> | |
| </body> | |
| </html> | |