跳到正文
GitHub·· 7 天前AI 评分18

Nebula:WhatsApp 上的动漫下载与 AI 助手机器人

JCVERSA/nebula-p

AI 导读

Nebula 是一个部署在 WhatsApp 上的多设备机器人,默认以法语配音(VF)下载动漫剧集,单集约 90 MB,支持 1-12 集批量处理并生成离线 HTML 下载页。它集成 Gemini AI 提供聊天、图像生成、语音对话等功能,并以 NVIDIA NIM 上的 meta/llama-3.3-70b-instruct 作为故障回退,单个部署最多可运行 8 个机器人。

正文

🧭 Contents

What it looks like · Features · Command Flow · Architecture · Quick Start · Anime engine · Environment variables · Tests · Security · Docs · Extending


📸 What it looks like

Real bot output (VF by default, one high-speed link per episode, honest size labels):

.a sparks of tomorrow s1 1-12 r1

📦 Nebula Novabox - Batch Media Preparation 🚀
🎬 Anime: Sparks of Tomorrow
🗣️ Language: VF          ← French dub by default (VOSTFR on request)
⚙️ Resolution: 480P
📦 Episodes to Process: 9

🚀 NEBULA NOVABOX - BATCH DOWNLOAD COMPLETED 🚀
📦 Ready Episodes: 9/9          ⏳ Links Validity: 2 Hours
📥 Direct Episode Links:
• 🎬 Episode 1: [88.93 MB]   🔗 https://your-domain/api/media/download/<token>
• 🎬 Episode 2: [89.84 MB]   🔗 …

The interactive flow defaults to VF too — and never lies about the language actually delivered:

🎬 Novabox - Select Season 🎬
• Anime: Tomb Raider King
• Language: 🇫🇷 VF (Default)
💡 (Pour passer en VOSTFR, tape `.a vostfr`)

✨ Features

Feature
📺 Anime VF downloader — voir-anime.to (VF-first) with nakanime fallback, VidMoly/Voe HLS mirrors, cat-catch style segment downloader, honest quality+size labels, WhatsApp-friendly files (~90 MB/ep), MyAnimeList info cards (.anime, Jikan), anime identification from a screenshot (.trace, trace.moe)
🗣️ VF by default — quick mode and interactive menus; .a vostfr switches back; honest "VF non disponible" when a title has no dub
🔔 New-episode watcher — .a watch on a VF season: cron polling (default every 6 h), quiet hours 23h–7h, WhatsApp notification with the ready-made download command; .a unwatch <title> / .a watchlist
📦 Batch episodes — 1-12 ranges, sequential pipeline hardened for ~1 GB containers, one offline HTML download page per batch (per-episode buttons + "Tout télécharger" in Chrome, sliding 30 min TTL reset on each download, 2 h total cap), optional season ZIP via NEBULA_BATCH_ZIP=1
🤖 Gemini AI — chat, image generation, audio transcription, voice conversations with TTS, per-user daily budget + global concurrency cap; NVIDIA NIM fallback keeps .ai alive through Gemini outages (text, meta/llama-3.3-70b-instruct by default); defined persona (sober, mirrors the user's language, WhatsApp-tailored, overridable via NEBULA_AI_PERSONALITY; **command-aware (8.79)** — the AI knows every registered command (auto-synced, panel commands included) and proactively guides users to the exact one to type, with a hand-written step-by-step .awalkthrough; per-conversation persistent memory (sliding 10 h TTL, rolling summary,.ai forget`)
💬 WhatsApp multi-device (Baileys) — QR and pairing-code linking, auto-reconnect, bad-session recovery
🤖 Multi-bots (8.75) — up to 8 WhatsApp bots in one deployment: per-bot process/session/persona, crash-isolated, see docs/MULTI_BOTS.md
🧩 Curated command set (owner decision 8.59): .a anime, .ytv/.ytm/.yts YouTube, .tiktok/.instagram downloads, .up (temporary share links for any file), .w episode watch, .ai/.image, .define, .sweb, .qr, .base64, .getpp, .whois, .ping/.menu/.help, .purge/.p (owner: immediate disk purge sparing in-flight batches), .gce/.g (Cameroon GCE past papers as PDFs from cameroongcerevision.com — O/L, A/L and regional mocks, interactive or one-line, text fallback when no PDF is published), .agent/.ag (AI agent, beta: understands natural-language requests in DMs and executes the matching command as the user, with confirmations for heavy actions) + sandboxed panel-created ones (no fs/process/network, no restart) — the inventory is locked by a registry test; the legacy corpus, its CJS bridge (8.56), the media toolkit, moderation suite and coin system were REMOVED
🛡️ Welcome/goodbye + RoleGuard core wiring kept for the group experience; the moderation command suite was removed in the 8.59 curation
🖥️ Web control panel — live simulator, secrets manager (masked), command customizer, analytics, ZIP export
🛰️ One-command ops — manage.sh start/stop/update/doctor/env/logs/clean on any VPS, behind a Cloudflare Tunnel

🔄 Command Flow

Nebula Bot — command flow: WhatsApp message through the engine (access control, command registry, anime pipeline, AI fallback) and back with document links

🏗️ Architecture

flowchart TD

subgraph group_runtime["Bot Runtime"]
  node_server["Server bootstrap<br/>[server.ts]"]
  node_supervisor["Bot supervisor<br/>[botSupervisor.ts]"]
  node_engine["WhatsApp bot engine<br/>[botEngine.ts]"]
  node_registry["Command registry<br/>[commandRegistry.ts]"]
end

subgraph group_anime["Anime Media"]
  node_anime_command["Anime commands<br/>[anime.ts]"]
  node_anime_parser["Quick anime parser"]
  node_anime_sources["Anime source resolver<br/>[animeSources.ts]"]
  node_anime_clients["Anime source clients"]
  node_stream["Stream extraction"]
  node_hls["HLS downloader<br/>[hlsDownloader.ts]"]
  node_watch["Episode watcher"]
end

subgraph group_ai["AI Services"]
  node_ai_command["AI commands<br/>[ai.ts]"]
  node_ai_engine["AI fallback client<br/>[geminiClient.ts]"]
  node_ai_memory[("Conversation memory<br/>[aiMemory.ts]")]
end

subgraph group_panel["Web Control Panel"]
  node_web_app["Panel API<br/>[app.ts]"]
  node_panel_ui["Panel interface<br/>[App.tsx]"]
  node_panel_factory["Panel app factory<br/>[panelApp.ts]"]
end

subgraph group_delivery["Download Delivery"]
  node_batch["Batch job manager"]
  node_zip["Batch ZIP manager<br/>[batchZipManager.ts]"]
  node_temp_store[("Temporary download store")]
end

node_whatsapp(("WhatsApp users"))
node_browser(("Panel users"))
node_baileys["WhatsApp platform"]
node_catalogs["Anime catalogs"]
node_gemini["Gemini AI"]
node_nim["NVIDIA NIM"]

node_whatsapp -->|"sends commands"| node_engine
node_engine -->|"connects through"| node_baileys
node_server -->|"starts bots"| node_supervisor
node_server -->|"creates panel"| node_panel_factory
node_panel_factory -->|"controls bots"| node_supervisor
node_engine -->|"dispatches commands"| node_registry
node_registry -->|"registers"| node_anime_command
node_engine -->|"dispatches"| node_anime_command
node_anime_command -->|"parses request"| node_anime_parser
node_anime_command -->|"resolves source"| node_anime_sources
node_anime_sources -->|"queries catalog"| node_anime_clients
node_anime_clients -->|"fetches metadata"| node_catalogs
node_anime_command -->|"resolves episode stream"| node_stream
node_stream -->|"downloads segments"| node_hls
node_engine -->|"starts watcher"| node_watch
node_watch -->|"sends notifications"| node_engine
node_engine -->|"dispatches"| node_ai_command
node_ai_command -->|"generates response"| node_ai_engine
node_engine -->|"generates direct reply"| node_ai_engine
node_engine -->|"reads context"| node_ai_memory
node_ai_engine -->|"requests generation"| node_gemini
node_ai_engine -.->|"falls back"| node_nim
node_browser -->|"uses panel"| node_panel_ui
node_panel_ui -->|"calls API"| node_web_app
node_web_app -->|"controls session"| node_engine
node_anime_command -->|"tracks batch"| node_batch
node_batch -->|"requests packaging"| node_zip
node_zip -->|"registers archive"| node_temp_store
node_web_app -->|"serves downloads"| node_temp_store

click node_server "https://github.com/jcversa/nebula-p/blob/main/server.ts"
click node_supervisor "https://github.com/jcversa/nebula-p/blob/main/src/bot/botSupervisor.ts"
click node_engine "https://github.com/jcversa/nebula-p/blob/main/src/bot/botEngine.ts"
click node_registry "https://github.com/jcversa/nebula-p/blob/main/src/bot/commandRegistry.ts"
click node_anime_command "https://github.com/jcversa/nebula-p/blob/main/src/bot/commands/anime.ts"
click node_anime_parser "https://github.com/jcversa/nebula-p/blob/main/src/bot/utils/quickAnimeParser.ts"
click node_anime_sources "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/animeSources.ts"
click node_anime_clients "https://github.com/jcversa/nebula-p/tree/main/src/bot/services"
click node_stream "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/animeStreamExtractor.ts"
click node_hls "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/hlsDownloader.ts"
click node_watch "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/episodeWatchService.ts"
click node_ai_command "https://github.com/jcversa/nebula-p/blob/main/src/bot/commands/ai.ts"
click node_ai_engine "https://github.com/jcversa/nebula-p/blob/main/src/bot/geminiClient.ts"
click node_ai_memory "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/aiMemory.ts"
click node_web_app "https://github.com/jcversa/nebula-p/blob/main/app.ts"
click node_panel_ui "https://github.com/jcversa/nebula-p/blob/main/src/App.tsx"
click node_panel_factory "https://github.com/jcversa/nebula-p/blob/main/src/panel/panelApp.ts"
click node_batch "https://github.com/jcversa/nebula-p/blob/main/src/bot/batchDownloadManager.ts"
click node_zip "https://github.com/jcversa/nebula-p/blob/main/src/bot/services/batchZipManager.ts"
click node_temp_store "https://github.com/jcversa/nebula-p/blob/main/src/bot/tempDownloadManager.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_server,node_supervisor,node_engine,node_registry,node_whatsapp,node_browser toneBlue
class node_anime_command,node_anime_parser,node_anime_sources,node_anime_clients,node_stream,node_hls,node_watch toneAmber
class node_ai_command,node_ai_engine,node_ai_memory toneMint
class node_web_app,node_panel_ui,node_panel_factory toneRose
class node_batch,node_zip,node_temp_store,node_baileys,node_catalogs,node_gemini,node_nim toneIndigo

Loading

Repo layout

server.ts (entry) ── createApp() (app.ts: auth, rate limiting, /api routes)
 ├── src/bot/botEngine.ts        Baileys socket, QR, reconnect, moderation
 ├── src/bot/commands/*.ts       individual commands (BotCommand interface)
 ├── src/bot/services/           anime clients, HLS downloader, streaming zip,
 │                               temp downloads, batch manager, proxies
 ├── src/bot/panelAuth.ts        HttpOnly session cookies (12h sliding)
 ├── app.ts                      panel SPA + media API + health
 ├── manage.sh                   VPS lifecycle: start/stop/update/doctor/env…
 └── docs/                       deployment, tunnel & migration guides

🚀 Quick Start

On a VPS (recommended — one line)

curl -fsSL "https://raw.githubusercontent.com/JCVERSA/nebula-p/main/scripts/install.sh" | sh

This installs dependencies (git, Node ≥ 22, ffmpeg), clones the repo, builds it, creates the nebula command available everywhere and offers the .env wizard. Then:

nebula env        # APP_URL, PANEL_TOKEN, GEMINI_API_KEY… (if not done yet)
nebula start      # start + wait for the panel

The installer is idempotent — running it again just updates the installation. Manual equivalent: git clone -b main https://github.com/JCVERSA/nebula-p /root/p && cd /root/p && ./manage.sh setup.

Then put it behind HTTPS with a Cloudflare Tunnel and set APP_URL — see docs/MIGRATION_NOUVEAU_VPS.md (French, step-by-step).

Tip: alias nebula='bash /root/p/manage.sh' — then everything is nebula <command>.

Local development

npm install
npm run dev        # panel at http://localhost:3000

Requirements: Node.js ≥ 22 and ffmpeg on PATH for media — see Requirements.

Day-to-day operations

Command What it does
./manage.sh start / stop / restart Lifecycle; start waits for the panel and tails the log on failure
./manage.sh update git pull --ff-only → npm install (only if deps changed) → build → restart
./manage.sh status Process RAM vs cgroup cap, panel + public URL probes, temp usage, disk
./manage.sh logs [filter] Live log tail, optionally filtered (e.g. logs NOVABOX)
./manage.sh env Interactive .env wizard — 26 documented keys, secrets masked
./manage.sh doctor Full diagnostic: node/ffmpeg/.env/RAM/disk/network, exit 1 on blockers
./manage.sh clean Purges orphan staging (>1h) and expired temp files (>3h)
./manage.sh pair [bot] <phone> Links a WhatsApp number with an 8-digit pairing code (no QR needed)
./manage.sh bots Multi-bots overview: per-bot process + WhatsApp state (8.75)
./manage.sh bot <id> <start/stop/restart/status> Controls one bot's engine process

📺 Anime engine

Built and battle-tested against real mirrors (every fix traced in ANIME_DOWNLOAD_AUDIT.md, §8.1–8.18):

  • VF by default from voir-anime.to (VF guaranteed by URL structure), nakanime VOSTFR fallback, VidMoly-first mirror ranking with Voe/voembed support; since 8.96 voir-anime is also the DEFAULT catalog for a bare .a <titre> (.a as <titre> reaches the full catalog; if the operator disables voir-anime the implicit default falls back to it)
  • Interactive choices memory (8.97/8.98) — the bot remembers each chat's anime downloads (last one detailed + the 4 previous on a compact line, 7-day TTL, NEBULA_ANIME_CHOICES_TTL_HOURS) so the agent resolves "le même anime", "l'épisode suivant", "le même en 720p", "qu'est-ce qu'on avait pris avant ?" into an exact command or answer; wiped by .ai forget
  • .up — temporary file share links (9.5) — .up (alias .upload .tr .lu .opload .opl .upl) turns any file sent with the command as its caption — or quoted in a reply — into a temporary download link: the bot streams the media straight to disk (never fully buffered in RAM — the engine heap is 192 MB) and stores it in the temp-download vault behind the same /d/<token> route as the anime episodes (2 h sliding window — each download resets the timer, 2 h total life cap), and replies with name, size and expiry. Oversized files are refused before processing (NEBULA_UPLOAD_MAX_MB, default 500, editable in the dashboard env editor, clamped 10-4096), each user is capped at 50 uploads/hour, and a bare .up with no media answers with a clear usage hint. Light command — the agent may run it directly.
  • Agent media awareness + .up reply fix (9.6b) — production transcript: « je veux un lien » + a reply to a video took FOUR clarification questions and then .up answered « Aucun fichier détecté ». Two fixes: (1) a reply wrapped in ephemeralMessage/viewOnce layers (disappearing-message chats) hid the quoted media from .up — extraction now unwraps the invoking message (defense in depth, the util itself is wrapper-tolerant); (2) the agent now receives a one-line media hint when the user's message attaches or quotes media (« répond (reply) à une vidéo » — nature only, never the content), so « un lien pour ça » resolves straight to .up instead of a question loop.
  • YouTube hardening (9.6) — three production-driven changes: (1) downloads now prefer H.264+AAC (real mp4 — "best video" is VP9/AV1 remuxed into an .mp4 container, which WhatsApp and Windows players refuse with « souci avec le fichier vidéo »; YouTube serves avc1 up to 1080p, old behavior kept as fallback); (2) the session cookies are only attached as a fallback when a video demands sign-in — anonymous first, so the authenticated volume (what Google actually watches from a datacenter IP) drops to the strict minimum, with a global cap of 50 session downloads/hour (honest refusal beyond; anonymous stays unlimited); (3) nebula update now self-updates the yt-dlp binary (stale versions trigger more bot checks). Best practices remain: use a throwaway Google account, never the personal one, and only from the VPS.
  • YouTube cookies pasted in the dashboard (9.4) — the Système section now takes the cookies.txt content directly: paste the file exported by "Get cookies.txt LOCALLY" from a signed-in youtube.com, and the panel validates it (strict Netscape format, at least one youtube.com cookie, not already expired), writes it atomically with 0600 perms, auto-configures NEBULA_YTDLP_COOKIES and shows a full diagnostic (cookie count, domains, furthest expiry date). Replacing the file applies hot (no restart); only the first setup needs one. A delete button reverts to anonymous mode. The cookie content is never displayed nor returned by the API — write-only, like the API keys.
  • .env editor in the dashboard + agent honesty fixes (9.3) — the Système section now edits a whitelisted set of .env variables directly from the panel (AI/web quotas, primary engine, digest on/off & hour, timezone) and takes API keys write-only (Tavily/Gemini/NIM masked at display — paste, never shown; YouTube cookies path). Strict per-type validation (no newlines/quotes — nothing injectable), atomic write in manage.sh format, and each variable reports whether it applies live or needs a restart (the restart button is right there). Agent fixes from production transcripts: recency questions now MUST go through .search (no more invented 2026 dates presented as "recent web research"), a failed anime search triggers the international-title recovery instead of a catalog switch ("attack des titans" → "attack on titan"), and the error recovery can no longer offer doc placeholders like example.com.
  • System admin console (9.2) — a new "Système" section in the panel dashboard: current version/commit/branch, a one-click Mettre à jour button (launches the exact nebula update script — git pull, install, build, restart — detached so it survives the panel restart), plus restart, stop (with an explicit "SSH only" warning) and a live bot.log tail with Copy (copies the displayed lines) and Export (downloads a .txt of the last 300 lines with a dated version header — share it as-is for debugging). Confirmation modal on every action; while updating, the UI tracks the panel going down and back up and auto-reloads on the new version ("Déjà à jour" when nothing changed). All routes sit behind the panel session with a dedicated rate limit, and no user input ever reaches a shell — only the fixed manage.sh actions.
  • Autonomous agent loop (9.1) — the agent now observes what the commands it runs reply (selection lists, season/quality pickers) and drives them to completion: you ask in natural language once ("télécharge mushoku tensei saison 3 épisode 12 en 480p"), it builds the full one-liner, auto-picks the right entry from the list, and hands you the link — zero typing. Bounded loop: max 3 AI decisions per message, identical-command rerun guard, quota re-checked at every step, and every iteration goes through the same zero-trust validation (denylist, argument guardrail, heavy-command confirmation). The last command screen stays in the agent's context for 10 minutes so a natural reply ("le 2e", "en VF") is grounded instead of looping. The .a parser now accepts natural French (saison 3, épisode 12, en 480p) — useful for humans too. Preference questions (VF/VOSTFR, unrequested quality) are always relayed to the user, never auto-answered.
  • Web research tools (9.0) — .search (real web results, keyless DuckDuckGo by default, optional TAVILY_API_KEY upgrades to the Tavily API), .fetch <url> (read a web page as clean text) and .wiki (French Wikipedia summaries). Freshness flags on .search: -d last 24h, -w week, -m month, -y year (no filter = all years, relevance first). Zero AI tokens (deterministic commands the agent can execute directly when asked to look something up). All requests go through the SSRF-hardened safeFetch (no localhost/private IPs — the panel and engine stay unreachable), sizes and timeouts capped, and a shared per-user daily budget (NEBULA_WEB_DAILY_LIMIT, default 20) protects the server IP.
  • Agent hardening + daily digest (8.99) — research-driven upgrades from hermes-agent, OpenAI Agents SDK, Mastra & Pydantic AI: native JSON mode for agent decisions (Gemini responseMimeType / NIM response_format, tolerant parsing kept as the fail-closed guard), an argument guardrail (URLs/backticks/shell flags stripped before execution), selective anime-history injection (context only when the message is about anime), per-turn agent metrics (/api/bot/agent-health + an "AI Agent — last 24 h" card in the panel, no message contents stored) and a daily owner DM digest (watches, disk, AI quota, agent health — 8 AM, NEBULA_DIGEST_HOUR, NEBULA_DIGEST=0 to disable). agent-replay gained scenario severities and a --json scored output.
  • Agent safety model & replay harness (8.98) — the agent's security model is documented in SAFETY.md (scope, denylist, fail-closed confirmations, budgets, privacy — lessons from hermes-agent & deepseek-harness), the knowledge sheet is kept under a CI-enforced context budget, and npm run agent-replay replays reference conversations against the REAL AI to catch decision regressions (manual, needs an AI key — in the container: docker exec -it <container> node dist/agent-replay.cjs)
  • Cross-catalog language fallback (8.95) — when the requested language is missing on the current catalog, the bot looks for the SAME anime (exact title match only, language verified structurally) on the other catalog and continues there with a 🔄 notice; if the language exists nowhere, it stops and asks (.a oui / .a non) instead of silently continuing in the other language
  • Honest labels — real HLS variant resolutions and sizes; a fat 403 MB "480P" is auto-downgraded to the lightest ≤480p variant (fast-lane size guard)
  • Container-friendly — sequential batches, disk-streamed segments with backpressure, capped V8 heap, streaming (STORE) ZIP writer instead of in-RAM archives, startup debris purge
  • Resilience — when every mirror of an episode fails (CDN-level 403), the bot retries it on the secondary anime catalog (VF lists first, then VOSTFR; honest language in the filename) — disable with NEBULA_VOSTFR_FALLBACK=0
  • YouTube / TikTok / Instagram — .ytv [360|480|720|1080], .ytm (AAC-converted audio), .yts (search links), .tiktok (no watermark, tikwm), .instagram (up to 20 media per post): original neb command ports; since 8.86 downloads go through the local yt-dlp binary first (API chains kept as fallback), and 8.87 adds --js-runtimes node plus optional session cookies for videos that require a signed-in session (NEBULA_YTDLP_COOKIES — see .env.example); 9.6 prefers H.264/AAC formats, keeps the session cookies as an anonymous-first fallback (50/hour global cap) and self-updates yt-dlp on every update
  • Delivery — batches >1 episode arrive as ONE offline HTML page: per-episode direct buttons + automatic "Tout télécharger" (sliding 30 min link TTL reset on each download, 2 h total cap — NEBULA_LINK_TTL_MIN; HTTP range streaming); single episodes still get a plain link; optional season ZIP behind NEBULA_BATCH_ZIP=1
  • Resource ceilings: NEBULA_NOVABOX_MAX_EPISODES (12), NEBULA_NOVABOX_MAX_BATCH_MB (2048/bot), NEBULA_TEMP_MAX_BYTES (2 GiB), plus a cross-bot disk guard (8.78, real-need claim since 8.81): every batch claims its real estimated need (per-episode size × episodes × 1.5, capped by the batch ceiling) in a shared claims dir and new batches are refused while free space would drop under NEBULA_MIN_FREE_DISK_MB (500) — the refusal tells the user how many episodes still fit

🔑 Environment Variables

Copy .env.example to .env (or run ./manage.sh env). Highlights:

Variable Required Description
APP_URL for public links Public panel URL (e.g. https://bot.example.com). Also enables the host-header guard — must match the tunnel hostname exactly
PANEL_TOKEN recommended Panel access key; exchanged for an HttpOnly session cookie, never stored in the browser. Auto-generated and printed once if unset
GEMINI_API_KEY for AI Google Gemini key (settable from the panel, masked display)
NVIDIA_NIM_API_KEY optional NVIDIA NIM key — AI fallback when Gemini is unavailable (free on build.nvidia.com)
PORT no HTTP port (default 3000)
NEBULA_VF_DEFAULT no 0 disables VF-by-default
NEBULA_VOIRANIME_DISABLED no 1 disables the voir-anime.to source
NEBULA_BATCH_ZIP no 1 re-enables the all-in-one season ZIP
NEBULA_BATCH_CONCURRENCY no Parallel episode downloads (default 1 — sequential; keep 1 under ~1 GB RAM)
NEBULA_NOVABOX_MAX_EPISODES / _MAX_BATCH_MB no Batch ceilings (12 / 2048)
NEBULA_DOWNLOAD_TIMEOUT_MS no Hard global deadline per episode download (default 600000 = 10 min)
NEBULA_WATCH_CRON / _QUIET / _TZ no Episode watcher schedule (0 */6 * * *), quiet window (23-7) and timezone (Africa/Douala)
NEBULA_TEMP_MAX_BYTES no Temp storage ceiling (2 GiB since 8.83 — sized for small containers)
NEBULA_LINK_TTL_MIN no Sliding TTL of download links in minutes (30, clamped 5-120) — each download resets the timer, total link life capped at 2 h (8.83)
NEBULA_UPLOAD_MAX_MB no Max file size in MB accepted by .up share links (500, clamped 10-4096) — bigger files are refused before processing (9.5)
NEBULA_MIN_FREE_DISK_MB no Cross-bot disk guard (8.78): free-space floor kept when admitting a new batch (500 MB)
NEBULA_DISK_GUARD no off disables the cross-bot disk guard
NEBULA_HEALTH_SWEEP_MS / NEBULA_HEALTH_FAILS no Supervisor health sweep (8.78): probe interval (60 s) and consecutive failures before a frozen engine is force-restarted (3)
NEBULA_LOG_FILE / NEBULA_LOG_MAX_MB no In-app log size guard (8.82): log path (set by manage.sh) and cap before truncation (150 MB) — the WhatsApp crypto layer dumps large session traces per message, and in Docker containers cron/logrotate often never runs
NEBULA_AI_DAILY_LIMIT / _MAX_CONCURRENT no AI budget (40/day/user) and concurrency (3)
NEBULA_PANEL_COMMANDS no off disables sandboxed panel-created commands
NEBULA_ENABLE_LEGACY no 1 re-enables the vendored legacy command corpus (quarantined by default)
NEBULA_DATA_DIR / NEBULA_ENV_FILE / NEBULA_AUTH_DIR no Runtime state, env file and WhatsApp session locations

Secrets can also be managed from the panel (Settings & Access → API Secrets): values are written atomically to .env, applied live without restart, and only ever shown masked. Only allowlisted variables can be set from the web UI.

🧪 Tests

npm test           # vitest — 439 tests across 47 files
npm run lint       # strict TypeScript typecheck
npm run build      # production build (client + server)
npm start          # serve the production build (capped V8 heap, gc exposed)

Tests run against an isolated temp data directory and never touch real WhatsApp sessions or the Gemini API. The suite covers the anime engine (labels, language routing, fast-lane guard), the streaming ZIP writer (byte-exact round-trips), temp-download purges and start-flag regressions.

🔒 Security Notes

  • Panel auth: every /api/* route requires login; cookies are server-side HttpOnly + CSRF-checked (Origin/Referer); bearer tokens work for tooling
  • SSRF guard: user-supplied URLs validated per redirect hop with DNS pinning + host allowlists
  • Sandboxed commands: panel-created commands run in a vm with no fs/process/network
  • Host-header guard: foreign Host headers rejected when APP_URL is set; temp links inherit the validated base URL
  • Resource caps: streamed downloads with byte caps, temp quotas, batch limits, AI budgets
  • ZIP export never embeds your live API key; simulator output is HTML-escaped

Details: SECURITY.md, AUDIT_REPORT.md.

📚 Documentation

Doc Contents
ANIME_DOWNLOAD_AUDIT.md Full anime-download engineering log: every bug, root cause and fix (§1–8.18)
docs/MIGRATION_NOUVEAU_VPS.md Move to a new VPS/container; Cloudflare Tunnel on a bare domain (subdomain left empty)
docs/GUIDE_DEPLOIEMENT_VPS.md Zero-to-production French guide (tunnel, pairing, doctor, troubleshooting)
docs/CLOUDFLARE_TUNNEL_DEPLOYMENT.md Cloudflare Tunnel reference (tokens, headers, checklist)
docs/RAPPORT_SYSTEME_TELECHARGEMENT.md Download-system design report
PHASE2_STATUS.md · PHASE3_SCOPE.md · RELEASE_NOTES_v1.1.0.md Hardening status, roadmap, release notes

🧩 Extending

  • Add a command: drop a BotCommand file in src/bot/commands/ (see ping.ts) — auto-loaded at startup; or generate one from the panel (sandboxed, stored as data)
  • Data flow: WhatsApp message → engine → moderation filters → command registry → command context (reply, react, downloadMedia, isOwner, isAdmin)
  • Local runner: the panel's ZIP export ships a self-contained bot (transpiled commands, Baileys runtime, config, placeholder .env)

Nebula Bot — built container-first, validated on real mirrors. ⭐ if it saves you time.

./manage.sh doctor should always end with RIEN DE BLOQUANT 🎉

来源:GitHub · github.com