尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Multi-server + web dashboard: ZiGBoT can now be used in any server, with per-server AI behaviour, role mappings and channels configured from a Next.js dashboard (Vercel) backed by Supabase. See MULTI_SERVER.md for architecture, security model and deployment steps.
ZiGBoT (v1.0.0) is a witty, stress-relieving Discord chat companion and auto-reply bot. It mingles with server members, lightens the mood with dry Gen-Z / Hinglish humor, and offers fun conversation to de-stress. Built on Node.js with discord.js and LLM integrations (NVIDIA NIM / OpenAI-compatible APIs).
| Layer | Technology |
|---|---|
| Runtime | Node.js (CommonJS), entry: index.js → src/index.js |
| Discord | discord.js v14.27, @discordjs/voice v0.19, @discordjs/opus (codec), prism-media, ffmpeg-static |
| AI | openai SDK v7.5 pointed at https://integrate.api.nvidia.com/v1, default model openai/gpt-oss-20b |
| Config | dotenv (.env), validated in src/config/settings.js |
| Persistence | mongodb (official Node.js driver) via MongoDB Atlas — warns survive restarts; all collections in src/db/brain.js |
| Tests | Node's built-in node:test runner (npm test), syntax checks via npm run check |
index.js # 1-line launcher → src/index.js
src/
index.js # Discord client, MessageCreate + InteractionCreate handlers,
# intent dispatch (runIntent), all-channel memory capture, voice transcript handling
slash.js # Slash commands (/play /kick /ban /help): definitions,
# registration, interaction → intent conversion
config/settings.js # Env parsing/validation, role name defaults, GUILD_CONFIG parsing
ai/
client.js # OpenAI client, savage/gentle system prompts, creator FAQ,
# intent classifier (JSON schema), crisis gate, moderation filter
memory.js # Per-channel sliding conversation memory (8 msgs / 15 min TTL,
# sweeps inactive channels)
rateLimiter.js # Per-user sliding-window AI quota limiter
triggerDetector.js # Stress/fun keyword regexes (English + Hinglish) + cooldown tracker
roleDetector.js # Role normalization (Unicode-safe), gentle/gender/non-gentle detection
routing/
voiceRoute.js # Deterministic voice-transcript → intent routing;
# destructive/admin actions blocked from voice
security/
authorization.js # Owner checks (config ID must match actual guild owner), bot permission checks
confirmation.js # Destructive-action Confirm/Cancel buttons (45s expiry, audited)
ownerCommands.js # Owner-only roast targeting and private member-memory lookup
logging/auditLog.js # Console + optional LOG_CHANNEL_ID audit entries
tools/router.js # ~30 admin/info/voice/music/warn intents with auth gates,
# action catalog (permissions + descriptions), destructive set
db/
brain.js # MongoDB Atlas connection (native driver), users/warnings
# collections, async warn store and 30-day memory (all queries live here)
voice/
voiceManager.js # Join/leave voice, permission checks, reconnect retry logic
localSpeech.js # External STT/TTS executables ({input}/{output}/{text} templates)
voiceConversation.js # Opus capture → WAV → STT → transcript callback; TTS playback
music/player.js # Per-guild queue, HTTPS-only sources, ffmpeg → PCM playback, volume/loop
test/ # music, authorization, autoReply, router, p3 suites (node --test)
@ZiGBoT) or when a user directly replies to one of ZiGBoT's messages.CHAT_CHANNEL_IDS.COOLDOWN_SECONDS, default 15s) for keyword triggers, plus a separate per-user AI quota limiter (8 req/min by default) on every LLM call.she/her, gentleman, king, soft boy, queen, … including fullwidth Unicode variants like she ﹒ her).Users.heer role, which forces roast mode.he/him or she/her roles.LOG_CHANNEL_ID.src/index.js)@ZiGBoT (with/without !)message.reference)CHAT_CHANNEL_IDS channelRESPOND_TO_ALL_MESSAGES=trueAUTO_REPLY_KEYWORDS=true) — stress or fun regex match, gated by cooldownsguild.ownerId match SERVER_OWNER_ID) → respectful tone, unless Users.heer role → roast mode.chat.runIntent() (see §5) → confirmation UI, tool execution, or fall-through.ai.reply() (system prompt + channel history) → moderation check → reply → stored in memory.Every eligible message (and every voice transcript) flows through this pipeline:
isCrisisMessage): broad English + Hinglish self-harm/crisis patterns override everything — no tool routing, no persona, immediate sincere support with helpline numbers (Tele-MANAS 14416, Kiran 1800-599-0019, US 988). Deliberately tuned so hyperbole ("this deadline is killing me", "I'm dead laughing") stays roast while "I want to die", "marna chahta hun", "kill myself" can never reach the savage persona — and can't be bypassed by phrasing.classifyIntent): temperature-0 LLM call with a strict JSON schema ({"action", "target", "role", "channel", "message", "count", "durationMinutes", "volume", "reason"}) returns "chat" (→ persona reply path) or a tool action. Output is never trusted — every field is re-validated/length-limited by the router's text() helpers. Aliases ("kick" → kick_member, "pause" → pause_music …) are canonicalized in normalizeAction().runIntent):| Intent type | Path |
|---|---|
chat | Persona/LLM reply path (savage/gentle/owner), unchanged |
bot_help | Generated live from the action catalog — cannot drift from what the bot supports |
Destructive (delete_role, remove_role, delete_channel, timeout_member, kick_member, ban_member, unban_member, delete_messages, warn_member) | requestConfirmation() Confirm/Cancel buttons first; only the original requester (who must be an authorized actor) can click Confirm |
Other admin (send_message, role/channel create, add_role, voice listening …) | Authorization (strict owner → per-guild owner → admin role) + bot permission check, then executeTool() |
| Info/voice/music/warn-list | executeTool() directly, no owner gate |
moderateReplyText): every LLM reply is checked before sending — slurs (including leetspeak), protected-class violence, threats, and self-harm encouragement are blocked locally and replaced with a deflection. Prompt text alone is never the safety boundary.AI_RATE_LIMIT_MAX per AI_RATE_LIMIT_WINDOW_SECONDS, default 8/60s), independent of the chat cooldowns.Voice transcripts (src/routing/voiceRoute.js) use deterministic keyword routing (no LLM call, nothing leaves the machine beyond the local STT executable). Only playback/info actions are allowed from voice; anything matching admin/destructive phrasing (kick/ban/timeout/delete/role/channel …) is blocked with a redirect: "That command only works in a text channel, where I can check permissions and confirm anything destructive."
Stream safety: all raw streams (opusStream, opus decoder, fetched audio stream, ffmpeg stdin/stdout/process) have error listeners — an unhandled stream error can no longer crash the process. @discordjs/opus is a declared dependency so opus decoding resolves cleanly with npm install.
src/ai/client.js)getSystemPrompt() composes tone + gender pronoun instruction + owner instruction.temperature: 1, top_p: 1, max_tokens: 4096, non-streaming.cleanOutput() strips ZiGBoT: prefixes and echoed user quotes; moderateReplyText() then enforces guardrails locally (see §5).src/ai/memory.js): Map keyed by channel ID; keeps the last 8 messages within 15 minutes; user messages prefixed [DisplayName]:. A sweep() runs on every add/read, evicting expired messages and deleting channels that go fully inactive — no unbounded growth.src/ai/triggerDetector.js): two cooldown maps (channel, user) so the bot never floods; resettable for tests.src/ai/rateLimiter.js): per-user sliding-window quota protecting the NVIDIA API; users are tracked independently and recover automatically as the window slides.src/security/authorization.js + isAuthorizedActor() in the router):
SERVER_OWNER_ID must equal both the message author's ID and the actual guild.ownerId. This remains the single-server fallback.GUILD_CONFIG): configured owner ID for that specific guild only — power never crosses guilds.GUILD_CONFIG.adminRoleNames): case-insensitive role-name match, scoped to the configuring guild.botPermission() always checks the bot's own Discord permissions before any admin action.src/security/confirmation.js): every destructive action gets interactive Confirm/Cancel buttons; the clicker must be the original requester AND an authorized actor (strict owner, per-guild owner, or configured admin role); 45s expiry; pending/cancelled/expired states all audited.src/logging/auditLog.js): every security decision/action — success, failure, or denial — logged to console + optional log channel.kickable/bannable.https:// URLs — no scraping, search, or DRM-bypass logic, by design.DISCORD_TOKEN, NVIDIA_API_KEY, and raw voice transcripts are never logged or printed (STORE_TRANSCRIPTS=false by default).src/tools/router.js)The action catalog is the single source of truth (action → required permission + description); bot_help and the classifier schema are generated from it, so help output can't drift.
| Category | Intents |
|---|---|
| Info | get_server_info, get_member_info, get_channel_info |
| Admin (auth-gated) | send_message, create_role, delete_role, add_role, remove_role, create_channel, delete_channel, rename_channel, timeout_member, kick_member, ban_member, unban_member, delete_messages (1–100), warn_member* |
| Moderation (open) | list_warnings |
| Memory | memory_status (owner/admin diagnostics), forget_memory (own memories; admin-gated for others) |
| Accountability | behavior_status (owner/admin: a member's behavior record and standing, live from the ledger) |
| Voice | join_voice, leave_voice, voice_status, start_voice_listening, stop_voice_listening |
| Music | play (direct HTTPS URL), pause_music, resume_music, skip_music, stop_music, queue_music, now_playing, volume_music (0–100), loop_music |
| Meta | bot_help (live-generated) |
* = destructive → requires the Confirm/Cancel flow before execution.
✅ The router is fully wired: every message is classified into a tool intent and dispatched through
executeTool()— admin, info, voice, music, and warn commands are live, from text, slash, and voice entry points.
src/slash.js)Second entry point into the exact same router/authorization/confirmation path as text commands — /play, /kick, /ban, /help.
SLASH_COMMAND_GUILD_IDS is set (instant), otherwise globally (may take up to an hour to propagate)./kick and /ban carry Discord's DefaultMemberPermissions (hidden from members without Kick/Ban Members) and still require the Confirm/Cancel flow before executing./help renders from the live action catalog.executeTool, never in the converter.src/voice/)prism-media + @discordjs/opus → max 15s PCM buffer.src/voice/voiceManager.js)Transient disconnects (channel moves, regional blips) no longer kill playback silently: up to 3 reconnect attempts with capped exponential backoff (2s → 15s), using discord.js's recommended signal-wait race. Destroyed connections are never resurrected; if reconnect finally fails, the connection and music queue are cleaned up and the failure is logged.
src/music/player.js)ffmpeg into raw s16le 48kHz stereo with inline volume; all ffmpeg/stream pipes have error listeners.src/db/)mongodb Node.js driver (no ODM), connected at startup from MONGODB_URI; database zigbot with users, warnings, counters, memories, and behaviors collections.src/db/brain.js — unique index on users.userId, compound index on warnings (guildId, userId, created_at), index on memories (guildId, userId, created_at desc) and behaviors (guildId, userId, created_at desc); warning/memory ids come from an atomic counter, mirroring the old SQLite AUTOINCREMENT.warn_member (destructive → confirmation flow) records who warned, the reason, and when; list_warnings shows a member's warnings with timestamps. Every warning AND timeout also lands in the behavior ledger as a negative accountability signal.connectBrain() fails, the bot logs a clear error and exits instead of running in a broken state.memories collection): every guild message delivered to the bot is checked automatically, including messages in channels where it does not reply. Only durable facts, preferences, and notable events are saved; ordinary chat and crisis messages are skipped, and passwords/API keys/tokens are refused outright. All entries expire after 30 days (MongoDB TTL plus read-time filtering and startup cleanup), including topic-tagged events. The verified server owner can ask “what do you know about” and mention a member to receive that member's stored summary privately by DM. Users can ask what ZiGBoT remembers about themselves and delete their own memories with forget_memory (admins may target other members)..env.example)| Variable | Description |
|---|---|
DISCORD_TOKEN | Discord bot token from the Developer Portal |
NVIDIA_API_KEY | NVIDIA NIM API key (or OpenAI-compatible endpoint key) |
MONGODB_URI | MongoDB Atlas connection string (free M0 tier at cloud.mongodb.com) — the process exits at startup without it |
| Variable | Default | Description |
|---|---|---|
SERVER_OWNER_ID | — | Discord user ID of the server owner (ZiG: 1296202178263912448). Single-server fallback; still requires matching guild.ownerId. |
SERVER_OWNER_ROLE_NAME | ꧁༺ ZiG ༻꧂ | Display name of the owner's role (informational) |
GUILD_CONFIG | — | Per-guild owners/admins (multi-server). JSON: {"<guildId>": {"ownerId": "...", "adminRoleNames": ["..."]}} |
AI_MODEL | openai/gpt-oss-20b | Model used for chat completions |
LOG_CHANNEL_ID | — | Channel that receives audit log entries |
RESPOND_TO_ALL_MESSAGES | false | Reply to every message in every server |
CHAT_CHANNEL_IDS | — | Comma-separated channel IDs for always-on chat |
AUTO_REPLY_KEYWORDS | false | Enable stress/fun keyword triggers |
COOLDOWN_SECONDS | 15 | Anti-spam cooldown per channel and per user (keyword triggers) |
REPLY_TYPING_WPM_MIN | 80 | Minimum simulated typing speed for a reply |
REPLY_TYPING_WPM_MAX | 140 | Maximum simulated typing speed for a reply |
REPLY_TYPING_DELAY_MIN_MS | 250 | Fastest reply delay floor |
REPLY_TYPING_DELAY_MAX_MS | 2200 | Slowest reply delay ceiling |
REPLY_MULTI_MESSAGE_BASE_GAP_MS | 350 | Gap between split multi-part replies |
REPLY_MULTI_MESSAGE_JITTER_MS | 200 | Extra jitter added to multi-part gaps |
REPLY_MULTI_MESSAGE_SPLIT_CHANCE | 0.12 |
| Variable | Description |
|---|---|
GENTLE_ROLE_NAMES | Roles that receive gentle, wholesome treatment |
FEMALE_ROLE_NAMES | Roles that map to she/her pronouns |
MALE_ROLE_NAMES | Roles that map to he/him pronouns |
FEMALE_ROLE_IDS | Discord role IDs that map to she/her pronouns |
MALE_ROLE_IDS | Discord role IDs that map to he/him pronouns |
NON_GENTLE_ROLE_NAMES | Roles (e.g., Users.heer) that force savage mode even for the owner |
| Variable | Default | Description |
|---|---|---|
VOICE_MODE | push-to-talk | Voice mode |
STORE_TRANSCRIPTS | false | Persist voice transcripts (temp files deleted by default) |
NVIDIA_SPEECH | true | Use NVIDIA hosted ASR/TTS (no local binaries — Render-friendly). false restores legacy local STT/TTS commands |
NVIDIA_ASR_FUNCTION_ID | multilingual Parakeet | Override the hosted Riva ASR model function-id |
NVIDIA_TTS_ENDPOINT | Magpie multilingual | Override the hosted Magpie TTS base URL |
NVIDIA_TTS_LANGUAGE | en-US | TTS language (use hi-IN for Hindi) |
NVIDIA_TTS_VOICE | Magpie-Multilingual.EN-US.Aria | TTS voice name (list via the endpoint's list_voices) |
STT_COMMAND / STT_ARGS | — | Legacy local STT executable + JSON arg template ({input}, {output}) — only used when NVIDIA_SPEECH=false or as fallback |
TTS_COMMAND / TTS_ARGS | — | Legacy local TTS executable + JSON arg template ({text}, {output}) — only used when NVIDIA_SPEECH=false or as fallback |
STT_TIMEOUT_MS / TTS_TIMEOUT_MS | 60000 | Executable timeouts (legacy local path) |
| Suite | Covers |
|---|---|
test/music.test.js | HTTPS-only source validation, volume bounds (0–100) |
test/authorization.test.js | Owner checks, guild-owner match requirement, roast-target gating |
test/autoReply.test.js | Creator-question regexes, prompt contents, Users.heer override, gender-neutrality rules, stress/fun/neutral triggers, cooldowns, memory window + sweep, Unicode role detection, settings parsing, crisis-gate cases (incl. hyperbole false-positive guards), moderation blocks & banter pass-through |
test/router.test.js | Non-owner admin denial, destructive-set completeness, catalog/permission consistency, info/music/help without owner auth, alias canonicalization, role-hierarchy block, voice blocks destructive actions, voice allows playback/info |
test/memory.test.js | Memory store CRUD + indexes, credential refusal, capability truth-telling (connected/unconnected/failing), simulated restart persistence, memory-question detection & truthful answers, selective save heuristics, owner-gated memory_status, forget_memory auth + honest zero-delete reporting |
test/behavior.test.js | Behavior ledger CRUD + indexes, sliding-window scoring (+1/−2, 30-day window), tier boundaries, conservative signal detection (banter never punished), structural spam tracker, reputation grounding blocks, truthful reputation answers, owner-gated behavior_status, automatic warning signal on warns |
test/p3.test.js | Rate-limiter window & per-user independence, Mongo warn store (add/count/list, indexes, default profile shape, unconnected guard) via fake client, strict-fallback unchanged, per-guild owner scoping, admin-role matching/scoping, warn routing, slash intent conversion |
Run everything:
npm test # all suites
npm run check # syntax-check every source file
.env.example to .env:
cp .env.example .env
.env (see §13). At minimum:
DISCORD_TOKEN — from the Discord Developer Portal.NVIDIA_API_KEY — for NVIDIA NIM inference.npm start
Quick sanity checks after inviting the bot:
@ZiGBoT help (or /help) — lists every live action, generated from the catalog.@ZiGBoT kick @someone as the owner — should produce a Confirm/Cancel prompt, not an instant kick.ZiGBoT can watch a YouTube live stream's chat, greet viewers, and support owner-only moderation commands.
This feature is entirely opt-in and never blocks the Discord bot: any missing
YOUTUBE_* variable or a failing YouTube API call leaves everything else working.
One-time Google Cloud setup
http://127.0.0.1:5455/oauth-callback (matches scripts/youtube-auth.js).cp .env.example .env and set YOUTUBE_CLIENT_ID / YOUTUBE_CLIENT_SECRET.One-time OAuth run (on your local machine, never the server)
node scripts/youtube-auth.js
A browser opens, you consent to the youtube.force-ssl scope, and the script
prints ONLY the refresh token — paste it into .env as YOUTUBE_REFRESH_TOKEN.
The client id/secret are never printed or logged.
Which account consents? Use the separate bot channel's account (recommended in this project's design). That way replies are posted from the bot channel. Two consequences:
liveBroadcasts.list(mine=true). The implemented detector instead polls the
owner's uploads playlist (playlistItems.list + videos.list, 2 units
per poll) and finds the item whose liveBroadcastContent == "live".7-day refresh-token expiry warning: while the OAuth consent screen is in
Testing status, refresh tokens expire after 7 days. When that happens the
bot logs auth failure (refresh token expired?) and pauses YouTube until you
re-run scripts/youtube-auth.js. Publish the consent screen to
In production to stop the expiry.
Runtime options (all in .env.example, all optional):
YOUTUBE_OWNER_HANDLE — whose channel to detect (default @YourBoyZiG).YOUTUBE_OWNER_CHANNEL_ID — hard override for the channel id.YOUTUBE_AUTO_DETECT=true|false (default on) with
YOUTUBE_DETECT_INTERVAL_MS (default 3 minutes) and
YOUTUBE_ACTIVE_HOURS=HH-HH (server-local, wrapping windows like 22-4 allowed).YOUTUBE_QUOTA_BUDGET — daily safety cap in units (default 10000).Owner Discord commands
/watch <videoId> — force the bot to watch a specific stream's chat./unwatch — stop./ytstatus — is YouTube enabled? which video? how much quota used today?/ytgreet on|off — owner-only runtime switch for Phase 2 greetings and direct mentions./ytroast on|off|status — owner-only roast-mode control and stream counters.Phase 2 greeting replies: YOUTUBE_GREETINGS=true (default) enables short greeting and direct-mention replies. Greetings are limited to one per viewer per stream; mentions have a 60-second per-viewer cooldown. Replies are queued at least 5 seconds apart, capped at 60 per stream by default, and stop when daily quota reaches the 2,500-unit reserve. YOUTUBE_ENGAGEMENT_PROMPTS=false keeps the older unsolicited YouTube-chat prompts disabled; set it to true to restore them. The bot ignores its own/owner/moderator messages, configured channel IDs, common chat bots, old backlog, and commands.
Phase 3 YouTube moderation: YOUTUBE_MODERATION=true (default) enables owner-only live-chat commands using YOUTUBE_COMMAND_PREFIX (default !). Every command requires both the resolved stream-owner channel ID and YouTube's isChatOwner flag. Supported commands are !timeout <target> [minutes] [reason] (default 5 minutes), !ban <target> [reason] (requires !confirm or !cancel within 30 seconds), !unban <target> (only removes bans created by this bot), and !delete <target> [count] (up to 10 cached messages from the last 30 minutes). Names must resolve to one recent chatter; exact UC... channel IDs can be used when names are ambiguous. Moderation stops at the configurable 400-unit quota reserve and 25 actions per stream by default, and ban/timeout requests have a 2-second cooldown. Attempts and results are written to LOG_CHANNEL_ID. /ytmod on|off controls moderation at runtime, while /ytstatus reports its state and action count.
Phase 4 YouTube roast mode: YOUTUBE_ROAST=true (default) permits owner-only roast commands; automatic roast mode still starts off for every stream. With mode enabled using !roastmode on or /ytroast on, a viewer's first eligible greeting gets a short roast instead of a friendly greeting; /ytroast off|status controls or reports it. The owner can also use !roast <target> for one roast, !noroast <target> to protect a viewer, and !yesroast <target> to remove that protection. Targets use recent chat names or quoted names; ambiguous matches are refused. The owner, bot, moderators, and paying members (unless YOUTUBE_ROAST_MEMBERS=true) are protected. A viewer's latest cached message is checked for crisis language before any AI call; matching viewers are skipped. AI sees only the display name as untrusted data. Generated text passes the existing AI output guardrail plus a strict one-line, 200-character, no-link, no-hashtag, no-mention filter. Filtered or failed greeting roasts use the normal friendly greeting; rejected command roasts are not posted. The per-stream off-limits list is best-effort stored in MongoDB ytNoRoast with a 24-hour TTL.
Each roast uses one NVIDIA request and one 20-unit YouTube chat insert. The existing sender queue keeps its 5-second global spacing; roast generation is limited to one per 10 seconds, 25 successful roasts per stream, and one per viewer every 2 minutes. The existing greeting quota reserve applies before a roast is generated/sent. YouTube gets a separate small AI bucket of 2 requests per minute, and each request times out after 8 seconds. All limits are configurable with YOUTUBE_MAX_ROASTS_PER_STREAM, YOUTUBE_ROAST_INTERVAL_MS, YOUTUBE_ROAST_VIEWER_COOLDOWN_MS, YOUTUBE_ROAST_AI_RATE_LIMIT_MAX, YOUTUBE_ROAST_AI_RATE_LIMIT_WINDOW_MS, and YOUTUBE_ROAST_AI_TIMEOUT_MS. YOUTUBE_ROAST_MEMBERS defaults to false. Roast attempts are audited to LOG_CHANNEL_ID; generated text appears only as a preview capped at 60 characters. /ytstatus includes roast mode, successful roasts, and AI calls for the current stream.
The YouTube bot channel must be a live-chat moderator of the stream to timeout/ban viewers or delete messages. Add it through the chat participant list → ⋮ → Add moderator. A 403 pauses moderation for the stream; /ytmod on retries after moderator access has been fixed.
Quota math (live chat via liveChatMessages.list, 5 units per poll at the
API's own pollingIntervalMillis pacing ~5s→10s+): roughly 1,800–3,600
units/hour of active stream, or ~4,000–9,000 units per 2–3 hour stream —
about one stream per day within the default 10,000. Detection adds ~40
units/hour of polling. The bot also posts an initial chat welcome asking viewers
to like, subscribe, and join the conversation, then rotates a prompt every 15
minutes (20 quota units per message). It replies to mild jokes aimed at the
streamer/bot with a playful comeback, and gives viewers who use detected
English or Hindi/Hinglish profanity a 5-minute timeout (200 quota units per
timeout). The bot account must be a live-chat moderator for timeouts to work.
When the quota budget runs out, the bot logs it,
pauses every YouTube call until midnight Pacific, and keeps the Discord bot
running normally.
Manual step for the channel owner (not automatable): add the bot channel as a live-chat moderator of https://www.youtube.com/@YourBoyZiG during a live stream, via the chat's participant list → ⋮ → Add moderator. Moderation commands require this permission.
GUILD_CONFIG; conversation memory, music queues, and voice sessions are per-guild.NVIDIA_SPEECH=true): Discord audio is downsampled in pure JS (no ffmpeg) and sent to the hosted Riva ASR endpoint, and replies are synthesized with hosted Magpie TTS and played back as raw PCM — no local binaries, so it works on Render's free tier. Plain conversation in voice chat gets spoken replies; set NVIDIA_SPEECH=false to restore the legacy local-executable pipeline.SLASH_COMMAND_GUILD_IDS is empty) can take up to an hour to propagate on Discord's side; guild-scoped registration is instant.| Chance a long reply is split into multiple messages |
AI_RATE_LIMIT_MAX | 8 | Max AI calls per user per window (classification + replies) |
AI_RATE_LIMIT_WINDOW_SECONDS | 60 | Sliding window for the AI rate limit |
SLASH_COMMAND_GUILD_IDS | — | Comma-separated guild IDs for instant guild-only slash registration; empty = global |
MONGODB_URI | — | MongoDB Atlas connection string for the warn system (free M0 tier at cloud.mongodb.com) |
HUMOR_STYLE | witty_stress_relief | Personality preset |