Sync broccolini-bot: rename from zammad, docs in docs/, security gitignore, remove zammad deps

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
samkintop
2026-02-12 02:56:00 -06:00
parent 08a16b4a75
commit 29a13768f7
37 changed files with 1093 additions and 3229 deletions

174
README.md
View File

@@ -1,8 +1,10 @@
# Gmail Bridge
# Broccolini Bot
A Node.js support-ticket bridge that connects **Gmail**, **Discord**, **Zammad**, and **MongoDB** into a unified helpdesk system. Incoming support emails are automatically turned into Discord ticket channels, staff replies in Discord are relayed back to the sender via Gmail, and every interaction is synced to Zammad and persisted in MongoDB.
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 built-in game detection, configurable automation, and a rich set of Discord slash commands.
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)
---
@@ -15,7 +17,6 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
- [Configuration](#configuration)
- [Discord](#discord)
- [Google OAuth2 / Gmail](#google-oauth2--gmail)
- [Zammad](#zammad)
- [MongoDB](#mongodb)
- [Branding & Messages](#branding--messages)
- [Automation](#automation)
@@ -24,6 +25,7 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
- [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)
@@ -31,7 +33,9 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
- [Database Schema](#database-schema)
- [API Integrations](#api-integrations)
- [Healthcheck](#healthcheck)
- [Documentation](#documentation)
- [Troubleshooting](#troubleshooting)
- [References](#references)
- [License](#license)
---
@@ -47,12 +51,6 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
### 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
- Each reply is also recorded as an article in Zammad
### Zammad Integration
- Automatically creates Zammad tickets with game info, priority, and group assignment
- Syncs user data (email, Discord ID) between MongoDB and Zammad
- Closes Zammad tickets when Discord tickets are resolved
### Ticket Management
- **Claim / Unclaim** -- Staff can claim tickets; optional auto-unclaim after inactivity
@@ -88,20 +86,20 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
```
┌────────────────────────────────────────────────────────────────────┐
GMAIL BRIDGE
BROCCOLINI BOT
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ ┌────────────────┐ ┌──────────────────┐ │
│ │ Gmail │─────>│ gmail-poll.js │─────>│ Discord │ │
│ │ (inbox) │ │ (every 30s) │ │ (ticket channel)│ │
│ │ Gmail │─────>│ gmail-poll.js │─────>│ Discord │ │
│ │ (inbox) │ │ (every 30s) │ │ (ticket channel)│ │
│ └───────────┘ └───────┬────────┘ └───────▲──────────┘ │
│ │ │ │
│ v │ │
┌───────────┐ ┌────────────────┐ ┌──────────────────┐ │
│ Zammad │<────>│ services/ │<────>│ handlers/ │ │
(tickets) │ │ gmail.js │ messages.js │ │
└───────────┘zammad.js │ │ buttons.js │ │
│ │ tickets.js │ │ commands.js │ │
┌────────────────┐ ┌──────────────────┐ │
│ services/ │ handlers/ │ │
│ gmail.js │<────>│ messages.js │ │
tickets.js │ │ buttons.js │ │
│ │ guildSettings │ │ commands.js │ │
│ └───────┬────────┘ └──────────────────┘ │
│ │ │
│ v │
@@ -113,53 +111,53 @@ Built for game-server hosting support (Indifferent Broccoli), with built-in game
│ Events: │
│ ready → Connect DB, register commands, start jobs │
│ interactionCreate → Buttons, slash commands, modals, menus │
│ messageCreate → Discord replies → Gmail + Zammad
│ messageCreate → Discord replies → Gmail
└────────────────────────────────────────────────────────────────────┘
```
**Ticket lifecycle:**
1. **Inbound email** -- Gmail poll detects a new unread message, creates a Discord channel, a Zammad ticket, and a MongoDB record.
2. **Staff reply** -- A message in the Discord ticket channel is forwarded to the sender via Gmail and added as an article in Zammad.
3. **Close** -- A transcript is generated, a closure email is sent, the Zammad ticket is closed, and the Discord channel is deleted.
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) |
| Zammad | >= 6.x |
|-------------|---------|
| 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)
- A **Zammad instance** accessible via URL with an API token
---
## Installation
```bash
# Clone the repository
git clone <your-repo-url>
cd gmail-bridge
Single-level repo: all commands run from the repo root. Create `.env` in the repo root (copy from `.env.example`).
# Install dependencies
```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 project root. All configuration is loaded via environment variables.
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 zammad-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.
> **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
@@ -177,9 +175,10 @@ Create a `.env` file in the project root. All configuration is loaded via enviro
| `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 |
| `EMAIL_ESCALATED_CATEGORY_ID` | No | Category ID for escalated email tickets |
| `ESCALATION_MESSAGE` | No | Message sent when a ticket is escalated |
| `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
@@ -190,17 +189,6 @@ Create a `.env` file in the project root. All configuration is loaded via enviro
| `REFRESH_TOKEN` | Yes | OAuth2 Refresh Token for the support inbox |
| `MY_EMAIL` | Yes | The support email address (e.g. `support@example.com`) |
### Zammad
| Variable | Required | Description |
|---|---|---|
| `ZAMMAD_URL` | Yes | Base URL of your Zammad instance |
| `ZAMMAD_TOKEN` | Yes | Zammad API token |
| `ZAMMAD_EMAIL_GROUP` | No | Zammad group for email-sourced tickets (default: `Email Users`) |
| `ZAMMAD_DISCORD_GROUP` | No | Zammad group for Discord-sourced tickets (default: `Discord Users`) |
> **Tip:** If your Zammad instance is behind ngrok, set `NGROK_URL` and use `ZAMMAD_URL=${NGROK_URL}` -- dotenv-expand will resolve it.
### MongoDB
| Variable | Required | Description |
@@ -304,7 +292,7 @@ GAME_LIST=Project Zomboid, Satisfactory, Palworld, Minecraft, Valheim, ...
npm start
# Or directly
node zammad-discord.js
node broccolini-discord.js
```
On startup the bot will:
@@ -316,17 +304,19 @@ On startup the bot will:
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 dont update, run `npm run register` (or restart) to re-register.
**Note:** Changing `.env` requires restarting the bot. Slash commands are registered on startup; if commands dont update, restart the bot to re-register.
### Optional: Create Zammad Groups
### Test Environment
If your Zammad instance doesn't already have the required groups:
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 create-zammad-objects
npm run start:test
```
This creates the "Email Users" and "Discord Users" groups and lists available priorities and states.
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`.
---
@@ -430,8 +420,8 @@ The panel system allows users to create tickets directly from Discord without se
## Project Structure
```
gmail-bridge/
├── zammad-discord.js # Entry point - initializes bot, events, and jobs
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.)
@@ -447,17 +437,20 @@ gmail-bridge/
│ ├── 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
── messages.js # Discord → Gmail reply forwarding
│ └── setup.js # Guild setup / configuration flow
├── services/
│ ├── gmail.js # Gmail OAuth2, send replies, closure emails
│ ├── tickets.js # Ticket CRUD, auto-close, reminders, auto-unclaim
── zammad.js # Zammad API client (tickets, users, articles)
│ ├── 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/
── create-zammad-objects.js # Utility to create Zammad groups
── backup-env.js # Copy .env to .env.backup
│ └── test-mongodb.js # MongoDB connection test
├── docs/ # Additional documentation
├── docs/ # Additional documentation (QUICKSTART, MONGODB_SETUP, ENV_AND_SECURITY, etc.)
├── .env # Environment variables (not committed)
├── package.json
└── package-lock.json
@@ -471,7 +464,7 @@ The bot uses MongoDB via Mongoose. Key collections:
| Collection | Purpose |
|---|---|
| `Ticket` | Core ticket data: Gmail thread ID, Discord channel ID, Zammad ticket ID, sender info, status, priority, claimed-by, timestamps |
| `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) |
@@ -498,15 +491,6 @@ The bot uses MongoDB via Mongoose. Key collections:
- **Interactions:** Slash commands, buttons, modals, context menus, autocomplete
- **Channels:** Create/delete ticket channels, manage permissions per user
### Zammad API
- **Authentication:** Token-based via `Authorization: Token token=...`
- **Tickets:** Create (`POST /api/v1/tickets`), update/close (`PATCH /api/v1/tickets/:id`)
- **Articles:** Add notes and replies (`POST /api/v1/ticket_articles`)
- **Users:** Search (`GET /api/v1/users/search`), create (`POST /api/v1/users`), update (`PATCH /api/v1/users/:id`)
---
## Healthcheck
An Express server runs on the port defined by `DISCORD_ONLY_PORT` (default: `5000`).
@@ -515,7 +499,24 @@ An Express server runs on the port defined by `DISCORD_ONLY_PORT` (default: `500
GET / → "Active"
```
Use this endpoint for uptime monitoring or container health probes.
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 |
---
@@ -536,15 +537,10 @@ Use this endpoint for uptime monitoring or container health probes.
### 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.
### Zammad API errors
- Confirm `ZAMMAD_URL` is reachable (if using ngrok, ensure the tunnel is active).
- Verify the `ZAMMAD_TOKEN` has sufficient permissions.
- Run `npm run create-zammad-objects` to ensure required groups exist.
### Tickets not creating
- Check that `TICKET_CATEGORY_ID` points to a valid Discord category.
@@ -559,6 +555,22 @@ Use this endpoint for uptime monitoring or container health probes.
---
## 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