577 lines
27 KiB
Markdown
577 lines
27 KiB
Markdown
# Broccolini Bot
|
||
|
||
A Node.js support-ticket bot that connects **Gmail**, **Discord**, and **MongoDB** into a unified ticketing system. Incoming support emails become Discord ticket channels; staff replies in Discord are sent back to the sender via Gmail. All ticket state is persisted in MongoDB.
|
||
|
||
Built for game-server hosting support (Indifferent Broccoli), with game detection from email content, configurable automation (auto-close, reminders, auto-unclaim), and a full set of Discord slash commands, buttons, modals, and context menus.
|
||
|
||
**Quick links:** [Installation](#installation) · [Configuration](#configuration) · [Discord Commands](#discord-commands) · [Documentation](#documentation)
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Features](#features)
|
||
- [Architecture](#architecture)
|
||
- [Prerequisites](#prerequisites)
|
||
- [Installation](#installation)
|
||
- [Configuration](#configuration)
|
||
- [Discord](#discord)
|
||
- [Google OAuth2 / Gmail](#google-oauth2--gmail)
|
||
- [MongoDB](#mongodb)
|
||
- [Branding & Messages](#branding--messages)
|
||
- [Automation](#automation)
|
||
- [Ticket Limits & Permissions](#ticket-limits--permissions)
|
||
- [Priority Levels](#priority-levels)
|
||
- [Claiming Options](#claiming-options)
|
||
- [Button & Embed Customization](#button--embed-customization)
|
||
- [Running the Bot](#running-the-bot)
|
||
- [Test Environment](#test-environment)
|
||
- [Discord Commands](#discord-commands)
|
||
- [Tag System](#tag-system)
|
||
- [Panel System](#panel-system)
|
||
- [Project Structure](#project-structure)
|
||
- [Database Schema](#database-schema)
|
||
- [API Integrations](#api-integrations)
|
||
- [Healthcheck](#healthcheck)
|
||
- [Documentation](#documentation)
|
||
- [Troubleshooting](#troubleshooting)
|
||
- [References](#references)
|
||
- [License](#license)
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
### Email-to-Discord Ticketing
|
||
- Polls Gmail every 30 seconds for unread emails in the primary inbox
|
||
- Creates a dedicated Discord channel per ticket (`ticket-{sender}-{number}`)
|
||
- Detects the game from the email subject/body and tags the ticket accordingly
|
||
- Sends a rich embed with ticket metadata and action buttons (Claim, Close)
|
||
|
||
### Discord-to-Email Replies
|
||
- Staff messages in a ticket channel are forwarded to the original sender via Gmail
|
||
- Replies are threaded in Gmail so the sender sees a continuous conversation
|
||
|
||
### Ticket Management
|
||
- **Claim / Unclaim** -- Staff can claim tickets; optional auto-unclaim after inactivity
|
||
- **Priority Levels** -- Low, Normal, High with color-coded embeds
|
||
- **Escalation** -- Move urgent tickets to a dedicated escalation category
|
||
- **Transfer / Move** -- Reassign tickets between staff or categories
|
||
- **Close Confirmation** -- Prevents accidental closes with a confirmation prompt
|
||
- **Transcripts** -- Full conversation transcripts posted to a dedicated channel on close
|
||
- **Auto-Close** -- Automatically close tickets after configurable hours of inactivity
|
||
- **Inactivity Reminders** -- Notify the channel when a ticket goes stale
|
||
|
||
### Panel System
|
||
- Deploy a "Open Ticket" button panel to any channel with `/panel`
|
||
- Users click the button, fill out a modal form, and a ticket is created
|
||
|
||
### Tag System (Saved Responses)
|
||
- Set ticket category with `/tag` (dropdown); create reusable response templates with `/response create`
|
||
- Dynamic template variables: `{ticket.user}`, `{staff.name}`, `{server.name}`, `{date}`, etc.
|
||
- Autocomplete-enabled `/tag` command for instant use
|
||
|
||
### Account Info Lookup
|
||
- `/accountinfo` searches website users by email or Discord ID
|
||
- Results show linked servers, game details, and user metadata
|
||
|
||
### Analytics & Logging
|
||
- In-memory tracking of command usage, button clicks, and errors
|
||
- `/stats` shows uptime, interaction counts, and error rate
|
||
- Configurable logging channel for ticket lifecycle events
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ BROCCOLINI BOT │
|
||
├────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌───────────┐ ┌────────────────┐ ┌──────────────────┐ │
|
||
│ │ Gmail │─────>│ gmail-poll.js │─────>│ Discord │ │
|
||
│ │ (inbox) │ │ (every 30s) │ │ (ticket channel)│ │
|
||
│ └───────────┘ └───────┬────────┘ └───────▲──────────┘ │
|
||
│ │ │ │
|
||
│ v │ │
|
||
│ ┌────────────────┐ ┌──────────────────┐ │
|
||
│ │ services/ │ │ handlers/ │ │
|
||
│ │ gmail.js │<────>│ messages.js │ │
|
||
│ │ tickets.js │ │ buttons.js │ │
|
||
│ │ guildSettings │ │ commands.js │ │
|
||
│ └───────┬────────┘ └──────────────────┘ │
|
||
│ │ │
|
||
│ v │
|
||
│ ┌────────────────┐ ┌──────────────────┐ │
|
||
│ │ MongoDB │ │ Express │ │
|
||
│ │ (Mongoose) │ │ (healthcheck) │ │
|
||
│ └────────────────┘ └──────────────────┘ │
|
||
│ │
|
||
│ Events: │
|
||
│ ready → Connect DB, register commands, start jobs │
|
||
│ interactionCreate → Buttons, slash commands, modals, menus │
|
||
│ messageCreate → Discord replies → Gmail │
|
||
└────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Ticket lifecycle:**
|
||
|
||
1. **Inbound email** -- Gmail poll detects a new unread message, creates a Discord channel and a MongoDB record.
|
||
2. **Staff reply** -- A message in the Discord ticket channel is forwarded to the sender via Gmail.
|
||
3. **Close** -- A transcript is generated, a closure email is sent, and the Discord channel is deleted.
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
| Requirement | Version |
|
||
|-------------|---------|
|
||
| Node.js | ≥ 18.x |
|
||
| npm | ≥ 9.x |
|
||
| MongoDB | ≥ 5.x (Atlas or self-hosted) |
|
||
|
||
You will also need:
|
||
|
||
- A **Discord bot** with the following intents enabled: Guilds, Guild Messages, Message Content, Guild Members
|
||
- A **Google Cloud project** with the Gmail API enabled and OAuth2 credentials (Client ID, Client Secret, Refresh Token)
|
||
---
|
||
|
||
## Installation
|
||
|
||
Single-level repo: all commands run from the repo root. Create `.env` in the repo root (copy from `.env.example`).
|
||
|
||
```bash
|
||
git clone <your-repo-url>
|
||
cd broccolini-bot
|
||
npm install
|
||
cp .env.example .env
|
||
# Edit .env with your Discord, Gmail, and MongoDB credentials (see Configuration).
|
||
```
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
Create a `.env` file in the repo root (same directory as this README). All configuration is loaded via environment variables.
|
||
|
||
> **Important:** After changing `.env`, you must **restart the process** (`npm start` / `node broccolini-discord.js`) for new values to take effect. If you add or change **slash commands** (e.g. `/escalate`, `/email-routing`, `/panel` options), restart the bot so it can **re-register** commands with Discord; otherwise new or updated commands may not appear.
|
||
|
||
> **Agent rule:** Changes to `.env` by an AI/agent must **require explicit user confirmation**. Prefer proposing changes to `.env.test` first and migrating to `.env` only after the user approves. See [ENV_AND_SECURITY.md](docs/ENV_AND_SECURITY.md).
|
||
|
||
### Discord
|
||
|
||
| Variable | Required | Description |
|
||
|---|---|---|
|
||
| `DISCORD_TOKEN` | Yes | Bot token from the Discord Developer Portal |
|
||
| `DISCORD_GUILD_ID` | Yes | Server (guild) ID where the bot operates |
|
||
| `DISCORD_APPLICATION_ID` | Yes | Application ID for registering slash commands |
|
||
| `TICKET_CATEGORY_ID` | Yes | Channel category ID where email ticket channels are created |
|
||
| `EMAIL_TICKET_OVERFLOW_CATEGORY_IDS` | No | Comma-separated category IDs; used when main email category has 50 channels |
|
||
| `DISCORD_TICKET_CATEGORY_ID` | No | Category for Discord panel tickets (defaults to `TICKET_CATEGORY_ID`) |
|
||
| `DISCORD_TICKET_OVERFLOW_CATEGORY_IDS` | No | Comma-separated category IDs; used when main Discord ticket category has 50 channels |
|
||
| `ROLE_ID_TO_PING` | Yes | Role ID to ping when a new ticket arrives |
|
||
| `TRANSCRIPT_CHANNEL_ID` | No | Channel ID for posting ticket transcripts |
|
||
| `LOGGING_CHANNEL_ID` | No | Channel ID for lifecycle log messages |
|
||
| `DEBUGGING_CHANNEL_ID` | No | Channel ID for error logs (escalate, deescalate, email-routing, Gmail poll, etc.) |
|
||
| `BACKUP_EXPORT_CHANNEL_ID` | No | Channel ID where `/backup` and `/export` post ticket dump files |
|
||
| `ACCOUNT_INFO_CHANNEL_ID` | No | Channel ID for account info lookups (and `/accountinfo` visibility) |
|
||
| `EMAIL_ESCALATED_CATEGORY_ID` | No | Category ID for escalated email tickets (tier 2+) |
|
||
| `DISCORD_ESCALATED_CATEGORY_ID` | No | Category ID for escalated Discord-origin tickets |
|
||
| `ESCALATION_MESSAGE` | No | Message sent when a ticket is escalated (supports `{support_name}`) |
|
||
|
||
### Google OAuth2 / Gmail
|
||
|
||
| Variable | Required | Description |
|
||
|---|---|---|
|
||
| `GOOGLE_CLIENT_ID` | Yes | OAuth2 Client ID from Google Cloud Console |
|
||
| `GOOGLE_CLIENT_SECRET` | Yes | OAuth2 Client Secret |
|
||
| `REFRESH_TOKEN` | Yes | OAuth2 Refresh Token for the support inbox |
|
||
| `MY_EMAIL` | Yes | The support email address (e.g. `support@example.com`) |
|
||
|
||
### MongoDB
|
||
|
||
| Variable | Required | Description |
|
||
|---|---|---|
|
||
| `MONGODB_URI` | Yes | MongoDB connection string (e.g. `mongodb+srv://user:pass@cluster/dbname`) |
|
||
|
||
### Branding & Messages
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `SUPPORT_NAME` | -- | Display name for the support system |
|
||
| `LOGO_URL` | -- | URL to the logo shown in embeds |
|
||
| `EMAIL_SIGNATURE` | -- | HTML signature appended to outgoing emails (use `\n` for line breaks) |
|
||
| `TICKET_CLOSE_SUBJECT_PREFIX` | `[Resolved]` | Prefix added to the subject of closure emails |
|
||
| `TICKET_CLOSE_MESSAGE` | *(see config.js)* | Body of the ticket closure email |
|
||
| `TICKET_CLOSE_SIGNATURE` | *(see config.js)* | Signature on the closure email |
|
||
| `TICKET_WELCOME_MESSAGE` | *(see config.js)* | Message posted when a ticket channel is created |
|
||
| `TICKET_CLAIMED_MESSAGE` | *(see config.js)* | Message posted when a ticket is claimed (supports `{staff_name}`) |
|
||
| `TICKET_UNCLAIMED_MESSAGE` | *(see config.js)* | Message posted when a ticket is unclaimed |
|
||
|
||
### Automation
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `AUTO_CLOSE_ENABLED` | `false` | Enable automatic ticket closure after inactivity |
|
||
| `AUTO_CLOSE_AFTER_HOURS` | `72` | Hours of inactivity before auto-close triggers |
|
||
| `AUTO_CLOSE_MESSAGE` | *(see config.js)* | Message sent when a ticket is auto-closed |
|
||
| `REMINDER_ENABLED` | `false` | Enable inactivity reminder messages |
|
||
| `REMINDER_AFTER_HOURS` | `24` | Hours of inactivity before a reminder is sent |
|
||
| `REMINDER_MESSAGE` | *(see config.js)* | Reminder message (supports `{hours}` variable) |
|
||
|
||
### Ticket Limits & Permissions
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `GLOBAL_TICKET_LIMIT` | `5` | Maximum concurrent open tickets globally |
|
||
| `TICKET_LIMIT_PER_CATEGORY` | `3` | Maximum tickets per category |
|
||
| `RATE_LIMIT_TICKETS_PER_USER` | `0` | Max tickets a user can create per window (0 = disabled) |
|
||
| `RATE_LIMIT_WINDOW_MINUTES` | `60` | Window in minutes for per-user ticket creation limit |
|
||
| `BLACKLISTED_ROLES` | -- | Comma-separated role IDs that cannot open tickets |
|
||
| `ADDITIONAL_STAFF_ROLES` | -- | Comma-separated role IDs with staff-level permissions |
|
||
|
||
### Priority Levels
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `PRIORITY_ENABLED` | `false` | Enable the priority system |
|
||
| `DEFAULT_PRIORITY` | `normal` | Default priority for new tickets |
|
||
| `PRIORITY_HIGH_EMOJI` | `🔴` | Emoji for high-priority tickets |
|
||
| `PRIORITY_MEDIUM_EMOJI` | `🟡` | Emoji for normal/medium-priority tickets (default level is normal) |
|
||
| `PRIORITY_LOW_EMOJI` | `🟢` | Emoji for low-priority tickets |
|
||
|
||
### Claiming Options
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `AUTO_UNCLAIM_ENABLED` | `false` | Automatically unclaim tickets after inactivity |
|
||
| `AUTO_UNCLAIM_AFTER_HOURS` | `24` | Hours before auto-unclaim triggers |
|
||
| `ALLOW_CLAIM_OVERWRITE` | `false` | Allow claiming an already-claimed ticket |
|
||
| `CLAIM_TIMEOUT_ENABLED` | `false` | Enable claim timeout |
|
||
| `CLAIM_TIMEOUT_HOURS` | `48` | Hours before a claim times out |
|
||
|
||
### Channel rename rate limit
|
||
|
||
Ticket channels are renamed automatically when you **claim**, **unclaim**, **escalate**, or **deescalate**. [Discord’s API](https://discord.com/developers/docs/topics/rate-limits) allows **2 channel renames per 10 minutes** per channel. The bot enforces this: if the limit is reached, the rename is skipped and the channel gets:
|
||
|
||
**Channel renamed too quickly. Try again \<t:*unlock_timestamp*:R\>.**
|
||
|
||
The timestamp is a Discord relative-time marker (e.g. “in 8 minutes”). After the window resets, the next claim/unclaim/escalate/deescalate can rename again.
|
||
|
||
### Button & Embed Customization
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `BUTTON_LABEL_CLOSE` | `Close Ticket` | Label for the close button |
|
||
| `BUTTON_LABEL_CLAIM` | `Claim` | Label for the claim button |
|
||
| `BUTTON_LABEL_UNCLAIM` | `Unclaim` | Label for the unclaim button |
|
||
| `BUTTON_EMOJI_CLOSE` | `🔒` | Emoji on the close button |
|
||
| `BUTTON_EMOJI_CLAIM` | `📌` | Emoji on the claim button |
|
||
| `BUTTON_EMOJI_UNCLAIM` | `🔓` | Emoji on the unclaim button |
|
||
| `EMBED_COLOR_OPEN` | `0x00FF00` | Embed color for open tickets |
|
||
| `EMBED_COLOR_CLOSED` | `0xFF0000` | Embed color for closed tickets |
|
||
| `EMBED_COLOR_CLAIMED` | `0xFFFF00` | Embed color for claimed tickets |
|
||
| `EMBED_COLOR_ESCALATED` | `0xFF6600` | Embed color for escalated tickets |
|
||
| `EMBED_COLOR_INFO` | `0x1e2124` | Embed color for info messages (and embeds next to ticket buttons) |
|
||
|
||
### Game List
|
||
|
||
Set `GAME_LIST` to a comma-separated list of game names. The bot uses this list for auto-detection from email subjects/bodies:
|
||
|
||
```env
|
||
GAME_LIST=Project Zomboid, Satisfactory, Palworld, Minecraft, Valheim, ...
|
||
```
|
||
|
||
---
|
||
|
||
## Running the Bot
|
||
|
||
```bash
|
||
# Start the bot
|
||
npm start
|
||
|
||
# Or directly
|
||
node broccolini-discord.js
|
||
```
|
||
|
||
On startup the bot will:
|
||
|
||
1. Validate required environment variables
|
||
2. Connect to MongoDB (with automatic reconnection)
|
||
3. Register all slash commands to the configured guild
|
||
4. Begin polling Gmail every 30 seconds
|
||
5. Start background jobs (auto-close, reminders, auto-unclaim)
|
||
6. Launch an Express healthcheck server
|
||
|
||
**Note:** Changing `.env` requires restarting the bot. Slash commands are registered on startup; if commands don’t update, restart the bot to re-register.
|
||
|
||
### Test Environment
|
||
|
||
To try config changes without affecting production, use a **test env**. Copy `.env.test.example` to `.env.test`, fill it with test-only values (e.g. test guild, test MongoDB database), and run:
|
||
|
||
```bash
|
||
npm run start:test
|
||
```
|
||
|
||
Other test scripts: `npm run test-mongodb:test`. After confirming behavior in test, migrate only the desired variables to `.env`. See **[ENV_AND_SECURITY.md](docs/ENV_AND_SECURITY.md)** for the full workflow, security checklist, and agent rules.
|
||
|
||
To test the MongoDB connection from the repo root: `npm run test-mongodb`.
|
||
|
||
---
|
||
|
||
## Discord Commands
|
||
|
||
### Ticket Management
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `/claim` | Claim the current ticket |
|
||
| `/unclaim` | Release your claim on the current ticket |
|
||
| `/close` | Close the current ticket (with confirmation) |
|
||
| `/force-close` | Close the current ticket without confirmation |
|
||
| `/priority <level>` | Set ticket priority (`low`, `normal`, `medium`, `high`). Posts: *upgraded to [Emoji][Level][Emoji]*, *downgraded to...*, or *returned to Normal*. Email sent when set to **high**. |
|
||
| `/topic <text>` | Set the ticket channel topic |
|
||
| `/escalate [reason] [tier]` | Escalate the ticket to tier 2 or 3 (optional tier; buttons also available) |
|
||
| `/deescalate` | De-escalate the ticket one step |
|
||
|
||
### User & Channel Management
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `/add <user>` | Add a user to the current ticket channel |
|
||
| `/remove <user>` | Remove a user from the current ticket channel |
|
||
| `/transfer <staff>` | Transfer the ticket to another staff member |
|
||
| `/move <category>` | Move the ticket to a different category |
|
||
|
||
### Tags & Saved Responses
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `/tag` | Set ticket category (dropdown: ⬇️ Server Down, ⏳ Stuck Restarting, 📵 Can't Connect, 🐌 Server Lag, 💳 Billing, 💸 Refund Request, 🔧 Mod Help, 💾 Backup Restore, 🌍 World / Save, ⚙️ Server Config). Posts: *Your ticket has been categorized as [Emoji][Tag][Emoji].* |
|
||
| `/response send <name>` | Send a saved response (autocomplete-enabled) |
|
||
| `/response create <name> <content>` | Create a new saved response |
|
||
| `/response edit <name> <content>` | Edit an existing saved response |
|
||
| `/response delete <name>` | Delete a saved response |
|
||
| `/response list` | List all saved responses |
|
||
|
||
### Utilities
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `/panel [channel] [type] [title] [description]` | Deploy a ticket-creation panel (type: thread, category, or both) |
|
||
| `/email-routing` | Switch where new email tickets are created (threads or category channels) |
|
||
| `/accountinfo <email or discord>` | Look up a user's account information |
|
||
| `/search <query>` | Search tickets |
|
||
| `/stats` | Show bot statistics and analytics |
|
||
| `/backup` | Export full ticket list to a .txt file in the backup/export channel |
|
||
| `/export [status] [limit]` | Export tickets (optional filter and limit) to a .txt file in the backup/export channel |
|
||
| `/help` | Display the command reference |
|
||
|
||
### Context Menus
|
||
|
||
| Menu | Description |
|
||
|---|---|
|
||
| **Create Ticket From Message** | Right-click a message to create a ticket from it |
|
||
|
||
---
|
||
|
||
## Tag & Response System
|
||
|
||
### Ticket category (`/tag`)
|
||
|
||
Use `/tag` in a ticket channel and pick a category from the dropdown (e.g. ⬇️ Server Down, 💳 Billing, 🔧 Mod Help). The bot posts: *Your ticket has been categorized as [Emoji][Tag][Emoji].* Channel name is not changed.
|
||
|
||
### Saved response tags (`/response`)
|
||
|
||
Saved responses are reusable templates stored in MongoDB. Use `/response send`, `/response create`, etc. They support dynamic variables that are replaced at send time:
|
||
|
||
| Variable | Resolves To |
|
||
|---|---|
|
||
| `{ticket.user}` | Ticket sender's name |
|
||
| `{ticket.email}` | Ticket sender's email |
|
||
| `{ticket.number}` | Ticket number |
|
||
| `{ticket.subject}` | Ticket subject line |
|
||
| `{staff.name}` | Current staff member's display name |
|
||
| `{staff.mention}` | Current staff member's mention |
|
||
| `{server.name}` | Discord server name |
|
||
| `{date}` | Current date |
|
||
| `{time}` | Current time |
|
||
|
||
**Example:**
|
||
|
||
```
|
||
/response create name:greeting content:Hi {ticket.user}! Thanks for reaching out about "{ticket.subject}". I'm {staff.name} and I'll be helping you today.
|
||
```
|
||
|
||
---
|
||
|
||
## Panel System
|
||
|
||
The panel system allows users to create tickets directly from Discord without sending an email.
|
||
|
||
1. Deploy a panel: `/panel #support title:Need Help? description:Click below to open a ticket!`
|
||
2. Users click the **Open Ticket** button
|
||
3. A modal form appears asking for subject, description, and priority
|
||
4. On submission, a ticket channel is created with all the same features as email tickets
|
||
|
||
---
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
broccolini-bot/
|
||
├── broccolini-discord.js # Entry point - initializes bot, events, and jobs
|
||
├── config.js # Environment variable loading and CONFIG export
|
||
├── db-connection.js # MongoDB connection with reconnect logic
|
||
├── models.js # Mongoose schemas (Ticket, User, Tag, etc.)
|
||
├── utils.js # Text processing, game detection, template vars
|
||
├── gmail-poll.js # Gmail polling loop and ticket creation
|
||
├── game-options.json # Game configuration data
|
||
│
|
||
├── commands/
|
||
│ └── register.js # Slash command and context menu registration
|
||
│
|
||
├── handlers/
|
||
│ ├── accountinfo.js # /accountinfo command and button handler
|
||
│ ├── analytics.js # In-memory analytics and error tracking
|
||
│ ├── buttons.js # Button interactions (claim, close, priority, etc.)
|
||
│ ├── commands.js # All slash command handlers
|
||
│ ├── messages.js # Discord → Gmail reply forwarding
|
||
│ └── setup.js # Guild setup / configuration flow
|
||
│
|
||
├── services/
|
||
│ ├── debugLog.js # Structured debug logging
|
||
│ ├── gmail.js # Gmail OAuth2, send replies, closure emails
|
||
│ ├── guildSettings.js # Guild-specific settings (DB + cache)
|
||
│ └── tickets.js # Ticket CRUD, auto-close, reminders, auto-unclaim
|
||
│
|
||
├── scripts/
|
||
│ ├── backup-env.js # Copy .env to .env.backup
|
||
│ └── test-mongodb.js # MongoDB connection test
|
||
│
|
||
├── docs/ # Additional documentation (QUICKSTART, MONGODB_SETUP, ENV_AND_SECURITY, etc.)
|
||
├── .env # Environment variables (not committed)
|
||
├── package.json
|
||
└── package-lock.json
|
||
```
|
||
|
||
---
|
||
|
||
## Database Schema
|
||
|
||
The bot uses MongoDB via Mongoose. Key collections:
|
||
|
||
| Collection | Purpose |
|
||
|---|---|
|
||
| `Ticket` | Core ticket data: Gmail thread ID, Discord channel ID, sender info, status, priority, claimed-by, timestamps |
|
||
| `TicketCounter` | Auto-incrementing ticket numbers per sender |
|
||
| `Transcript` | Transcript message references for closed tickets |
|
||
| `Tag` | Saved response templates (name, content, creator) |
|
||
| `CloseRequest` | Tracks pending close confirmations |
|
||
| `User` | Website user accounts (email, Discord ID, linked servers) |
|
||
| `Host` | Game server/host metadata and metrics |
|
||
| `DashboardMetrics` | Aggregated dashboard statistics |
|
||
| `ErrorLog` | Persisted error records |
|
||
|
||
---
|
||
|
||
## API Integrations
|
||
|
||
### Gmail API
|
||
|
||
- **Authentication:** OAuth2 with Client ID, Client Secret, and Refresh Token
|
||
- **Polling:** `users.messages.list` for unread messages in the primary inbox
|
||
- **Reading:** `users.messages.get` to fetch full message content
|
||
- **Sending:** `users.messages.send` for threaded replies and closure emails
|
||
|
||
### Discord API (discord.js v14)
|
||
|
||
- **Intents:** Guilds, GuildMessages, MessageContent, GuildMembers
|
||
- **Interactions:** Slash commands, buttons, modals, context menus, autocomplete
|
||
- **Channels:** Create/delete ticket channels, manage permissions per user
|
||
|
||
## Healthcheck
|
||
|
||
An Express server runs on the port defined by `DISCORD_ONLY_PORT` (default: `5000`).
|
||
|
||
```
|
||
GET / → "Active"
|
||
```
|
||
|
||
Use this endpoint for uptime monitoring or container health probes. Optional: set `HEALTHCHECK_HOST=127.0.0.1` in `.env` to bind the healthcheck server to localhost only; omit to listen on all interfaces.
|
||
|
||
---
|
||
|
||
## Documentation
|
||
|
||
Additional guides and reference docs live in **`docs/`**:
|
||
|
||
| Doc | Description |
|
||
|-----|-------------|
|
||
| [QUICKSTART](docs/QUICKSTART.md) | Get started in a few minutes: first response, panel, tags, priority |
|
||
| [ENV_AND_SECURITY](docs/ENV_AND_SECURITY.md) | Test env workflow, security checklist, agent rules |
|
||
| [MONGODB_SETUP](docs/MONGODB_SETUP.md) | MongoDB connection, schemas, and testing |
|
||
| [PROJECT_STRUCTURE](docs/PROJECT_STRUCTURE.md) | File and directory layout |
|
||
| [PROPOSAL](docs/PROPOSAL.md) | Roadmap and possible next steps |
|
||
| [PHASE_FEATURES](docs/PHASE_FEATURES.md) | Phased feature list and variables |
|
||
| [FEATURES_SUMMARY](docs/FEATURES_SUMMARY.md) · [NEW_FEATURES](docs/NEW_FEATURES.md) | Feature overview and changelog |
|
||
| [DISCORD_API_IMPROVEMENTS](docs/DISCORD_API_IMPROVEMENTS.md) · [DISCORD_API_VALIDATION](docs/DISCORD_API_VALIDATION.md) | Discord API implementation notes |
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Slash commands not appearing in Discord
|
||
|
||
- Commands are registered per-guild on startup. Wait up to one hour for Discord to propagate.
|
||
- Verify `DISCORD_APPLICATION_ID` and `DISCORD_GUILD_ID` are correct.
|
||
- Restart the bot.
|
||
|
||
### Gmail polling not working
|
||
|
||
- Ensure `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `REFRESH_TOKEN` are set correctly.
|
||
- The refresh token may have expired -- regenerate it via the Google OAuth2 Playground.
|
||
- Check that the Gmail API is enabled in your Google Cloud Console project.
|
||
|
||
### MongoDB connection failures
|
||
|
||
- Verify `MONGODB_URI` is correct and the database is accessible.
|
||
- Run `npm run test-mongodb` from the repo root to test the connection.
|
||
- If using MongoDB Atlas, ensure your IP is whitelisted.
|
||
- The bot has automatic reconnection -- check logs for retry attempts.
|
||
|
||
### Tickets not creating
|
||
|
||
- Check that `TICKET_CATEGORY_ID` points to a valid Discord category.
|
||
- Ensure the bot has `Manage Channels` and `View Channel` permissions in that category.
|
||
- Review the logging channel for error messages.
|
||
|
||
### Modal not appearing when clicking "Open Ticket"
|
||
|
||
- Verify the bot has proper guild permissions.
|
||
- Try in a different channel.
|
||
- Restart the bot.
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
This project builds on or references the following:
|
||
|
||
| Technology | Description | Links |
|
||
|------------|-------------|--------|
|
||
| **discord.js** | Node.js library for the Discord API; used for the bot, slash commands, buttons, and embeds. | [discord.js](https://discord.js.org/) · [GitHub](https://github.com/discordjs/discord.js) |
|
||
| **Discord Tickets** | Open-source ticket bot; referenced for patterns and feature inspiration (panels, tags, transcripts). | [Discord Tickets](https://discordtickets.app/) · [GitHub](https://github.com/discord-tickets/bot) |
|
||
| **Node.js** | JavaScript runtime used to run the bot. | [Node.js](https://nodejs.org/en) |
|
||
| **MongoDB** | Database for tickets, transcripts, and persistence (via Mongoose). | [MongoDB](https://www.mongodb.com/) |
|
||
| **Express** | HTTP server for the healthcheck endpoint. | [Express](https://expressjs.com/) |
|
||
| **Mongoose** | MongoDB ODM used for schemas and connection handling. | [Mongoose](https://mongoosejs.com/) |
|
||
| **Google APIs (googleapis)** | Gmail API client for polling and sending email. | [Google APIs Node.js](https://github.com/googleapis/google-api-nodejs-client) |
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
ISC
|