564
README.md
Normal file
564
README.md
Normal file
@@ -0,0 +1,564 @@
|
||||
# Gmail Bridge
|
||||
|
||||
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.
|
||||
|
||||
Built for game-server hosting support (Indifferent Broccoli), with built-in game detection, configurable automation, and a rich set of Discord slash commands.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Architecture](#architecture)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Installation](#installation)
|
||||
- [Configuration](#configuration)
|
||||
- [Discord](#discord)
|
||||
- [Google OAuth2 / Gmail](#google-oauth2--gmail)
|
||||
- [Zammad](#zammad)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [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
|
||||
- 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
|
||||
- **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
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ GMAIL BRIDGE │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌───────────┐ ┌────────────────┐ ┌──────────────────┐ │
|
||||
│ │ 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 │ │
|
||||
│ └───────┬────────┘ └──────────────────┘ │
|
||||
│ │ │
|
||||
│ v │
|
||||
│ ┌────────────────┐ ┌──────────────────┐ │
|
||||
│ │ MongoDB │ │ Express │ │
|
||||
│ │ (Mongoose) │ │ (healthcheck) │ │
|
||||
│ └────────────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
│ Events: │
|
||||
│ ready → Connect DB, register commands, start jobs │
|
||||
│ interactionCreate → Buttons, slash commands, modals, menus │
|
||||
│ messageCreate → Discord replies → Gmail + Zammad │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
| Requirement | Version |
|
||||
|---|---|
|
||||
| Node.js | >= 18.x |
|
||||
| npm | >= 9.x |
|
||||
| MongoDB | >= 5.x (Atlas or self-hosted) |
|
||||
| Zammad | >= 6.x |
|
||||
|
||||
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
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a `.env` file in the project root. 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.
|
||||
|
||||
### 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 |
|
||||
| `EMAIL_ESCALATED_CATEGORY_ID` | No | Category ID for escalated email tickets |
|
||||
| `ESCALATION_MESSAGE` | No | Message sent when a ticket is escalated |
|
||||
|
||||
### 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`) |
|
||||
|
||||
### 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 |
|
||||
|---|---|---|
|
||||
| `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 zammad-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, run `npm run register` (or restart) to re-register.
|
||||
|
||||
### Optional: Create Zammad Groups
|
||||
|
||||
If your Zammad instance doesn't already have the required groups:
|
||||
|
||||
```bash
|
||||
npm run create-zammad-objects
|
||||
```
|
||||
|
||||
This creates the "Email Users" and "Discord Users" groups and lists available priorities and states.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
```
|
||||
gmail-bridge/
|
||||
├── zammad-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
|
||||
│
|
||||
├── 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)
|
||||
│
|
||||
├── scripts/
|
||||
│ └── create-zammad-objects.js # Utility to create Zammad groups
|
||||
│
|
||||
├── docs/ # Additional documentation
|
||||
├── .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, Zammad ticket 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
|
||||
|
||||
### 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`).
|
||||
|
||||
```
|
||||
GET / → "Active"
|
||||
```
|
||||
|
||||
Use this endpoint for uptime monitoring or container health probes.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
ISC
|
||||
Reference in New Issue
Block a user