2026-04-21 16:15:18 +00:00
2026-04-21 17:24:03 +00:00
2026-04-21 16:15:18 +00:00
2026-04-18 11:10:41 +00:00
2026-04-21 17:24:03 +00:00
2026-04-21 17:24:03 +00:00
2026-04-21 17:32:00 +00:00
2026-04-07 01:43:06 -05:00
2026-04-21 14:32:34 +00:00
2026-03-28 22:40:43 +00:00
2026-04-21 15:30:40 +00:00
2026-04-21 17:24:03 +00:00
2026-04-18 11:10:41 +00:00
2026-04-21 14:32:34 +00:00

Broccolini Bot

A Node.js Discord bot that unifies Gmail, Discord, and MongoDB for support ticketing. Incoming emails become Discord ticket channels; staff messages in those channels are sent back to customers via Gmail. Discord-originated tickets (panels, context menu) live entirely in Discord. State is stored in MongoDB via Mongoose.

Built for game-server hosting support (Indifferent Broccoli), with game detection from email content, tiered escalation, optional per-staff notification channels (reply alerts with cooldown + unclaimed-ticket digests), saved responses, /tag categorization, claimer emoji in channel names (STAFF_EMOJIS), and automation (auto-close, reminders, auto-unclaim, claim timeout).

Jump to: Features · Quick start · Configuration · Staff notifications · Broccolini settings page · Commands · Project layout


Table of contents


Features

Email → Discord

  • Polls Gmail about every 30 seconds for new unread primary mail (gmail-poll.js).
  • Creates a Discord text channel (or thread, per guild settings) per email ticket, with overflow category support when a category is full.
  • Detects game from subject/body using GAME_LIST and built-in aliases in config.js.
  • Posts welcome + action buttons (Close, Claim, Escalate / De-escalate where applicable).

Discord → Gmail

  • For email-sourced tickets, staff messages in the ticket channel are forwarded to the customer via Gmail (threaded).
  • Discord-only tickets (gmailThreadId prefix discord- / discord-msg-) do not use Gmail for replies; conversation stays in Discord.

Ticket management

  • Claim / Unclaim via buttons (not slash commands); optional claim overwrite, claim timeout, and auto-unclaim.
  • Priority (low / normal / medium / high) with configurable emojis and /priority.
  • Escalation: tier 2 and tier 3 categories (separate IDs for email vs Discord where configured); slash /escalate and in-channel buttons.
  • De-escalation one step at a time (/deescalate or button).
  • Close with confirmation; force-close for admins.
  • Transcripts posted to a configured channel; closure email for email tickets.
  • Auto-close, inactivity reminders, auto-unclaim (all optional via env).

Staff notifications & alerts (optional)

  • Per-staff notification channels: /notification add creates a dedicated text channel per staff member under STAFF_NOTIFICATION_CATEGORY_ID and stores it in StaffNotification (MongoDB). When a non-staff user replies in a ticket claimed by someone with a notification channel, the bot posts an alert there (subject to per-ticket cooldown via /notification set or admin /staffnotification).
  • Unclaimed digests: a background job runs every 30 minutes and, if UNCLAIMED_REMINDER_THRESHOLDS is set, posts unclaimed ticket digests to those same channels when tickets cross age thresholds.
  • DM reply alerts: /notifydm toggles optional DM alerts to the claimer on customer reply (separate from the notification channel); stored in StaffSettings.
  • Staff threads (optional): when STAFF_THREAD_ENABLED is true, each ticket channel can get a private staff-only thread named STAFF_THREAD_NAME; on claim, the claimer can be added to that thread, and (optionally) all members of STAFF_THREAD_ROLE_ID are auto-added.
  • Pins (optional): PIN_INITIAL_MESSAGE_ENABLED and PIN_ESCALATION_MESSAGE_ENABLED enable auto-pinning of the ticket welcome message and escalation messages; PIN_SUPPRESS_SYSTEM_MESSAGE hides the default “X pinned a message” system notice.
  • Chat monitoring & surge detection: see Patterns, surge & chat alerts for automatic alerts about busy chats, surging games, backlogs, and no-staff situations.

See Staff notification channels and Patterns, surge & chat alerts for details.

Note: Older docs referred to per-staffer mirror channels driven by STAFF_CATEGORIES. In current config.js that map is deprecated and always empty, and createStaffChannel is not called from the claim flow—staffChannelId on tickets is effectively unused. Reply alerts use StaffNotification channels instead, and staff discussion happens in optional staff threads.

Extras

  • /panel: “Open ticket” UI (modal collects email, game, description).
  • /tag: ticket category dropdown; /response: saved templates with variable substitution.
  • /setup: setup wizard for guild defaults.
  • /accountinfo: website account lookup (email or Discord user).
  • /stats, /search, /backup, /export, /email-routing.
  • Context menus: create ticket from message; view user tickets.
  • Optional REST API under /api when the relevant API key env vars are set (see .env.example).

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     BROCCOLINI BOT                               │
├─────────────────────────────────────────────────────────────────┤
│  Gmail (inbox) ──► gmail-poll.js ──► Discord ticket channels     │
│                           │                    ▲                 │
│                           ▼                    │                 │
│                    services/gmail.js ◄── handlers/messages.js    │
│                    services/tickets.js      handlers/buttons.js │
│                    services/channelQueue.js                      │
│                    services/staffNotifications.js                  │
│                           │                                      │
│                           ▼                                      │
│                    MongoDB (Mongoose) ◄── models.js              │
│                                                                  │
│  Express: GET / → "Active" ; optional /api → routes/             │
└─────────────────────────────────────────────────────────────────┘

Typical email ticket lifecycle

  1. New unread mail → poll creates Discord channel + Ticket document.
  2. Staff reply in channel → message handler sends Gmail reply (email tickets only).
  3. Close confirmed → transcript, optional closure email, channel delete; DB marked closed.

Prerequisites

Requirement Notes
Node.js 18+; Docker image uses 20 (Dockerfile).
npm npm install locally; npm ci --omit=dev in Docker.
MongoDB Self-hosted; MONGODB_URI required at startup.
Discord application Bot token, application ID; intents: Message Content, Server Members; also Guilds + Guild Messages.
Google Cloud Gmail API enabled; OAuth2 client ID/secret + refresh token for the support mailbox.

Quick start

git clone <your-repo-url>
cd broccolini-bot
npm install
cp .env.example .env
  1. Fill Discord (DISCORD_TOKEN or DISCORD_BOT_TOKEN, DISCORD_APPLICATION_ID, DISCORD_GUILD_ID, categories, ROLE_ID_TO_PING, transcript/log channels).
  2. Fill MongoDB (MONGODB_URI).
  3. Fill Google OAuth (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, REFRESH_TOKEN, MY_EMAIL) — use node get-refresh-token.js once if needed.
  4. Run npm start.
  5. In Discord, use /setup or verify categories and roles manually.

Restart after any .env change. After changing slash command definitions, restart so registerCommands() re-registers with Discord.


Installation

Same as quick start. Optional:

  • Test env: copy .env.test.example.env.test. On Unix shells: npm run start:test (sets ENV_FILE). On Windows PowerShell: $env:ENV_FILE='.env.test'; node broccolini-discord.js (or set ENV_FILE in the environment your process manager uses). npm run test-mongodb:test has the same ENV_FILE pattern.
  • 1Password CLI: npm run start:1p / start:test:1p inject secrets (see docs/setup/1PASSWORD.md).

Do not commit .env or .env.test. AI/agents should not edit production .env without explicit approval; see ENV_AND_SECURITY.md.


Configuration

Configuration is environment variables only, loaded in config.js as CONFIG. Names below match .env.example unless noted.

Discord (core)

Variable Required Description
DISCORD_TOKEN Yes* Bot token (*or DISCORD_BOT_TOKEN, first non-empty wins after trim).
DISCORD_APPLICATION_ID Yes Used as CLIENT_ID for REST command registration.
DISCORD_GUILD_ID Yes Guild where slash commands are registered.
TICKET_CATEGORY_ID Yes Default category for email tickets (startup validates this).
DISCORD_TICKET_CATEGORY_ID No Category for Discord panel/context tickets (falls back to TICKET_CATEGORY_ID).
EMAIL_THREAD_CHANNEL_ID / DISCORD_THREAD_CHANNEL_ID No Parent text channels for thread-style tickets, when used.
EMAIL_TICKET_OVERFLOW_CATEGORY_IDS No Comma-separated extra categories when the main is full (50 channels).
DISCORD_TICKET_OVERFLOW_CATEGORY_IDS No Same for Discord ticket category.
ROLE_ID_TO_PING Yes Support role pinged on new tickets; alias ROLE_TO_PING_ID in code.
ADDITIONAL_STAFF_ROLES No Extra role IDs treated as staff for commands.
BLACKLISTED_ROLES No Roles blocked from opening tickets.
TRANSCRIPT_CHANNEL_ID No Where transcripts are posted on close.
LOGGING_CHANNEL_ID No Ticket lifecycle logs.
DEBUGGING_CHANNEL_ID No Optional error/debug forwarding.
BACKUP_EXPORT_CHANNEL_ID No Target for /backup and /export.
ACCOUNT_INFO_CHANNEL_ID No Account info flows.

Escalation categories

Variable Description
EMAIL_ESCALATED_CATEGORY_ID Legacy fallback; alias ESCALATED_CATEGORY_ID.
DISCORD_ESCALATED_CATEGORY_ID Discord fallback tier-2style bucket.
DISCORD_ESCALATED2_CHANNEL_ID Tier 2 placement for Discord tickets (or + fallback category).
EMAIL_ESCALATED2_CHANNEL_ID Tier 2 for email tickets (env name says CHANNEL for legacy reasons).
DISCORD_ESCALATED3_CHANNEL_ID Tier 3 Discord.
EMAIL_ESCALATED3_CHANNEL_ID Tier 3 email.

Slash /escalate and buttons require the appropriate tier IDs for non-thread channels (threads skip category moves).

Staff notifications, claimer display, admin

Variable Description
STAFF_NOTIFICATION_CATEGORY_ID Category where /notification add creates per-staffer notification channels.
STAFF_EMOJIS Comma-separated discordUserId:emoji pairs; used in channel name when a ticket is claimed.
CLAIMER_EMOJI_FALLBACK Emoji if the claimer has no STAFF_EMOJIS entry.
ADMIN_ID Discord user ID allowed to use /staffnotification (override cooldown for another member).
UNCLAIMED_REMINDER_THRESHOLDS Comma-separated hours (e.g. 1,2,4); drives unclaimed ticket alerts into notification channels.

Logging & observability

Variable Description
GMAIL_LOG_CHANNEL_ID Channel for Gmail poll activity logs.
AUTOMATION_LOG_CHANNEL_ID Channel for auto-close/auto-unclaim/reminder logs.
RENAME_LOG_CHANNEL_ID Channel for channel rename queue logs.
SECURITY_LOG_CHANNEL_ID Channel for security/audit logs.
SYSTEM_LOG_CHANNEL_ID Channel for bot lifecycle logs (startup, shutdown, DB events).

Pattern detection & surge/chat alerts

Core behaviour is configured via .env.example; high level:

  • Pattern detection (patternStore.js, patternChecker.js):
    • USER_PATTERNS_CHANNEL_ID, GAME_PATTERNS_CHANNEL_ID, TAG_PATTERNS_CHANNEL_ID, ESCALATION_PATTERNS_CHANNEL_ID, STAFF_PATTERNS_CHANNEL_ID, COMBINED_PATTERNS_CHANNEL_ID select where pattern embeds are posted.
    • Threshold envs like PATTERN_USER_TICKET_THRESHOLD, PATTERN_GAME_TICKET_THRESHOLD, PATTERN_UNCLAIMED_HOURS, PATTERN_ESCALATION_THRESHOLD, PATTERN_RAPID_CLOSE_SECONDS tune when alerts fire.
    • Windows (today, week, month) reset automatically via scheduled timers in patternStore.scheduleResets().
  • Surge detection (surgeChecker.js):
    • ALL_STAFF_CHANNEL_ID is the primary surge-alert channel; SURGE_ROLE_ID is pinged when set.
    • SURGE_TICKET_COUNT / SURGE_TICKET_WINDOW_MINUTES, SURGE_GAME_TICKET_COUNT / SURGE_GAME_TICKET_WINDOW_MINUTES, SURGE_STALE_*, SURGE_NEEDS_RESPONSE_*, SURGE_UNCLAIMED_*, SURGE_TIER3_UNCLAIMED_MINUTES, SURGE_COOLDOWN_MINUTES control volume/backlog alerts.
    • SURGE_NO_STAFF_OPEN_TICKET_THRESHOLD, SURGE_NO_STAFF_COOLDOWN_MINUTES, STAFF_IDS, and STAFF_DND_COUNTS_AS_AVAILABLE drive “no staff available” alerts (presence-based with message activity fallback).
  • Chat monitoring (chatAlertChecker.js):
    • CHAT_ALERT_CHANNEL_IDS lists channels to monitor.
    • CHAT_ALERT_MESSAGE_COUNT, CHAT_ALERT_HOURS_WITHOUT_RESPONSE, CHAT_ALERT_COOLDOWN_MINUTES configure when to send chat-attention alerts to ALL_STAFF_CHAT_ALERT_CHANNEL_ID.

Google / Gmail

Variable Required Description
GOOGLE_CLIENT_ID Yes OAuth2 client ID.
GOOGLE_CLIENT_SECRET Yes OAuth2 secret.
REFRESH_TOKEN Yes Long-lived refresh for the inbox account.
MY_EMAIL Yes Canonical support address (lowercased in config).

MongoDB

Variable Required
MONGODB_URI Yes

Test: npm run test-mongodb (optionally with ENV_FILE / .env.test as above).

Server & optional HTTP API

Variable Default Description
DISCORD_ONLY_PORT 5000 Express listen port (CONFIG.PORT).
HEALTHCHECK_HOST (all interfaces) e.g. 127.0.0.1 for local-only bind.

Additional variables for mounting /api (API key, CORS, etc.) are listed in .env.example if you use that integration.

Messaging & branding

See .env.example for defaults: ESCALATION_MESSAGE ({support_name}), TICKET_WELCOME_MESSAGE, TICKET_CLAIMED_MESSAGE / TICKET_UNCLAIMED_MESSAGE ({staff_mention}, {staff_name}), DISCORD_CLOSE_MESSAGE, DISCORD_TRANSCRIPT_MESSAGE ({channel_name}, {email}, {date_opened}, {date_closed}), EMAIL_SIGNATURE (\n<br>), embed color hex vars, button labels/emojis, SUPPORT_NAME, LOGO_URL.

Automation & limits

  • Auto-close: AUTO_CLOSE_ENABLED, AUTO_CLOSE_AFTER_HOURS, AUTO_CLOSE_MESSAGE.
  • Reminders: REMINDER_ENABLED, REMINDER_AFTER_HOURS, REMINDER_MESSAGE ({ping}, {hours}).
  • Limits: GLOBAL_TICKET_LIMIT, TICKET_LIMIT_PER_CATEGORY, RATE_LIMIT_TICKETS_PER_USER, RATE_LIMIT_WINDOW_MINUTES.
  • Claim: ALLOW_CLAIM_OVERWRITE, AUTO_UNCLAIM_*, CLAIM_TIMEOUT_ENABLED, CLAIM_TIMEOUT_HOURS.
  • Priority: PRIORITY_ENABLED, DEFAULT_PRIORITY, PRIORITY_*_EMOJI.

Game list

GAME_LIST=comma,separated,names — used for detection/normalization in email handling (plus aliases in config.js).

Thread-style tickets (legacy)

USE_THREADS, THREAD_PARENT_CHANNEL (see .env.example) — optional legacy paths; primary behavior is also governed by GuildSettings.emailRouting (/email-routing: thread | category).


Staff notification channels & reply alerts

When STAFF_NOTIFICATION_CATEGORY_ID is set:

  1. /notification add (with a target member) creates a channel under that category and saves userIdchannelId + default cooldown in StaffNotification.
  2. /notification set hours: (16) updates the cooldown between reply alerts for that users claimed tickets (same ticket keys off gmailThreadId).
  3. /staffnotification (admin, ADMIN_ID) sets cooldown for another staff member.
  4. On messageCreate, if the ticket has a claimerId and the author is not detected as having ROLE_ID_TO_PING, notifyStaffOfReply may post in the claimers notification channel (respecting cooldown).
  5. Every 30 minutes, notifyAllStaffUnclaimed evaluates open tickets with claimedBy: null against UNCLAIMED_REMINDER_THRESHOLDS and posts to all configured notification channels (tracks sent thresholds on the ticket in unclaimedReminderssent).

/notifydm toggles StaffSettings.notifyDm for the invoking user; when enabled, claimers can also receive a DM on customer reply (in addition to any notification channel).


Broccolini settings page

The repo includes an optional Broccolini settings web UI under settings-site/ for configuring the bot without editing .env directly.

  • Runs as a small Express app (settings-site/server.js) on SETTINGS_PORT and talks to the bots internal API on INTERNAL_API_PORT using INTERNAL_API_SECRET.
  • Serves a password-protected dashboard (SETTINGS_ADMIN_PASSWORD) where you can adjust Discord channels, categories, Gmail credentials, ticket behavior, surge alerts, pattern thresholds, appearance, staff options, and advanced settings.
  • Changes are sent to the bots internal /internal/config endpoints and can be saved as pending, applied immediately, or saved and paired with an immediate or scheduled restart.

To use it, run node settings-site/server.js alongside the bot (or via Docker), set the SETTINGS_* and INTERNAL_API_* variables as in .env.example, and put it behind HTTPS with your preferred reverse proxy.


Running the bot (test and Docker)

npm start
# or
node broccolini-discord.js

Test / alternate env file: see Installation for ENV_FILE on Windows vs Unix.

npm run test-mongodb

Docker (see Dockerfile):

docker build -t broccolini-bot .
docker run --env-file .env -p 5000:5000 broccolini-bot

Ensure MONGODB_URI and Discord token are available inside the container. A sample docker-compose.yml exists—adjust ports and env_file for your host (do not copy production-specific bind addresses into new deployments without review).


Discord commands

Most commands require staff (ROLE_ID_TO_PING or ADDITIONAL_STAFF_ROLES). /help is available more broadly per registration.

Command Description
/setup Guild setup wizard (panel, role, category, transcript channel, etc.).
/panel Post a ticket Open button in a channel (optional type: thread / category / both; custom title/description).
/email-routing Choose whether new email tickets create threads vs category channels (GuildSettings in DB).
/escalate Required: level (Tier 2 or Tier 3), action (unclaim clears claimedBy + claimerId after escalation, keep preserves claim).
/deescalate Step down one tier (tier 3 → 2 → normal).
/notifydm setting: on / off — DM when a non-staff user replies in a ticket you claimed.
/notification Subcommands: set (cooldown hours), add (create notification channel for a member).
/staffnotification Admin only (ADMIN_ID); override another members notification cooldown.
/add, /remove Add/remove user overwrites on the current ticket channel.
/transfer Set claimedBy to another staff member (must have staff role).
/move Move channel to another category (direct setParent).
/force-close Close without button confirmation (still archives transcript best-effort).
/topic Set Discord channel topic.
/priority low / normal / medium / high.
/tag Set ticket tag category from dropdown (choices from TICKET_TAGS in config.js).
/response Subcommands: send, create, edit, delete, list (saved responses).
/accountinfo Subcommands: email, discord.
/search Search tickets by email, subject, or number.
/stats Bot analytics snapshot.
/backup, /export Post exports to BACKUP_EXPORT_CHANNEL_ID.
/help In-bot command summary embed.

Context menus

  • Create Ticket From Message — opens a ticket prefilled from a message.
  • View User Tickets — lists recent tickets for a user (by sender tag match).

Ticket UI (buttons & modals)

  • Open ticket (panel): modal fields are account email, game, description.
  • In ticket channels: Close, Claim/Unclaim, Escalate (tier choice), De-escalate as built in utils/ticketComponents.js / handlers/buttons.js.
  • Email routing and tag delete confirmations use additional button custom IDs.

Tag & response system

/tag

Sets ticketTag from the fixed list in config.js (TICKET_TAGS). Channel naming may incorporate tag/priority emojis via ticket naming logic.

/response

Templates support variables such as {ticket.user}, {ticket.email}, {ticket.number}, {ticket.subject}, {staff.name}, {staff.mention}, {server.name}, {date}, {time} (see utils.js / handler docs).


Panel system

  1. Run /panel targeting a channel (and optional style: thread-only, category-only, or both buttons).
  2. User clicks Open ticket → modal → bot creates thread or channel per configuration.
  3. Welcome embeds + action row are posted; Ticket stores discordThreadId, ticketNumber, etc.

Channel renames & moves (rate limits)

Discord allows two renames per 10 minutes per channel. The bot serializes renames/moves through services/channelQueue.js (p-queue). If rename is blocked, staff see a message with a relative time to retry.


Project structure

broccolini-bot/
├── broccolini-discord.js      # Entry: Discord client, Express, Gmail poll interval, jobs
├── config.js                  # Env → CONFIG (game lists, TICKET_TAGS, STAFF_EMOJIS map, …)
├── db-connection.js           # Mongo connect + require models
├── models.js                  # Mongoose schemas (Ticket, Tag, StaffSettings, StaffNotification, …)
├── utils.js                   # Email/game helpers, template variables
├── utils/ticketComponents.js  # Action row builders
├── gmail-poll.js              # Ingest Gmail → Discord ticket creation
├── get-refresh-token.js       # One-shot OAuth refresh token helper
├── commands/register.js       # Slash + context menu registration (discord.js v14)
├── handlers/
│   ├── buttons.js             # Claim/close/modals/escalate buttons, ticket create modal
│   ├── commands.js            # Slash handlers, runEscalation/runDeescalation
│   ├── messages.js            # Staff ↔ Gmail relay; notifydm; notification alerts
│   ├── accountinfo.js
│   ├── analytics.js
│   └── setup.js
├── services/
│   ├── gmail.js
│   ├── tickets.js             # Auto-close, reminders, auto-unclaim, naming helpers
│   ├── channelQueue.js        # enqueueRename / enqueueMove
│   ├── staffChannel.js        # Legacy mirror helpers (unused in current claim flow)
│   ├── staffNotifications.js  # Reply alerts + unclaimed reminders
│   ├── staffSettings.js       # notifydm prefs
│   ├── guildSettings.js
│   └── debugLog.js
├── routes/                    # Optional Express `/api` routes
├── api/                       # Bot client accessor for HTTP layer
├── scripts/                   # Maintenance / one-off utilities
├── docs/                      # Deeper guides (setup, security, MongoDB, API notes)
├── Dockerfile
├── docker-compose.yml
├── package.json
└── .env.example / .env.test.example

Database collections

Model / collection Role
Ticket Gmail thread id, Discord channel/thread id, status, priority, claim (claimedBy display label, claimerId), legacy staffChannelId, escalation tier, welcomeMessageId, ticketTag, unclaimedReminderssent, etc.
TicketCounter Per-sender local counters (legacy paths).
Transcript Links closed tickets to transcript message IDs.
Tag Saved response name + content.
GuildSettings e.g. emailRouting: thread | category.
StaffSettings Per-user notifyDm (+ guildId, updatedAt).
StaffNotification Per-user channelId, cooldownHours for reply/unclaimed alerts.
CloseRequest Pending close workflow if used.
User, Host, DashboardMetrics, ErrorLog Shared / website-era schemas in the same models.js file.

HTTP: healthcheck & optional API

  • GET / → plain text Active (intended for load balancers / Docker HEALTHCHECK).
  • /api/* is registered only after the bot is ready and the optional HTTP API is enabled via env (see .env.example). JSON body parsing is enabled; auth uses a Bearer token from configuration. Route definitions live under routes/ in this repo.

Gmail OAuth refresh token

node get-refresh-token.js

Requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env, and redirect URI http://localhost:3000/oauth2callback registered on the Google OAuth client. Paste the printed refresh token into .env as REFRESH_TOKEN.


Documentation in docs/

Index: docs/README.md. Highlights:

Doc Topic
ENV_AND_SECURITY.md Secrets, test env, agent rules
MONGODB_SETUP.md Database
QUICKSTART.md First-time orientation
PROJECT_STRUCTURE.md Layout (may overlap this README)
1PASSWORD.md 1Password CLI for npm run start:1p

Development & CI

This repo includes .gitlab-ci.yml with GitLab SAST and secret detection templates. Adjust or extend stages in GitLab as needed for your fork.


Troubleshooting

Symptom Checks
Commands missing Correct DISCORD_APPLICATION_ID + DISCORD_GUILD_ID; restart bot; Discord can take time to sync.
Gmail not ingesting REFRESH_TOKEN, API enablement, inbox auth; regenerate token if revoked.
MongoDB errors MONGODB_URI, npm run test-mongodb.
Channels not creating Bot Manage Channels in ticket categories; category not full (50) unless overflow set.
Modal / button no response Intents + permissions; bot online; check DEBUGGING_CHANNEL_ID / console.
Renames “too quickly” Discord rename cooldown; wait for channel queue / timestamp in bot message.
Test script env on Windows npm run start:test sets ENV_FILE Unix-style; use PowerShell ENV_FILE + node if the script fails.

References

Technology Link
discord.js v14 discord.js guide
Google APIs (Gmail) googleapis Node
Mongoose mongoosejs.com
Express expressjs.com

License

ISC

Description
No description provided
Readme 2.4 MiB
Languages
JavaScript 85.2%
CSS 8.1%
HTML 6.6%