This commit is contained in:
indifferentketchup
2026-04-06 23:56:56 -05:00
parent 7b924b2af2
commit ca63ecbcfd

162
README.md
View File

@@ -2,9 +2,9 @@
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 staff “mirror” channels per claimer, saved responses, `/tag` categorization, and automation (auto-close, reminders, auto-unclaim).
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](#features) · [Quick start](#quick-start) · [Configuration](#configuration) · [Staff categories](#staff-personal-categories--mirror-channels) · [Commands](#discord-commands) · [Project layout](#project-structure)
**Jump to:** [Features](#features) · [Quick start](#quick-start) · [Configuration](#configuration) · [Staff notifications](#staff-notification-channels--reply-alerts) · [Commands](#discord-commands) · [Project layout](#project-structure)
---
@@ -16,8 +16,8 @@ Built for game-server hosting support (Indifferent Broccoli), with game detectio
- [Quick start](#quick-start)
- [Installation](#installation)
- [Configuration](#configuration)
- [Staff personal categories & mirror channels](#staff-personal-categories--mirror-channels)
- [Running the bot](#running-the-bot-test-and-docker)
- [Staff notification channels & reply alerts](#staff-notification-channels--reply-alerts)
- [Running the bot (test and Docker)](#running-the-bot-test-and-docker)
- [Discord commands](#discord-commands)
- [Ticket UI (buttons & modals)](#ticket-ui-buttons--modals)
- [Tag & response system](#tag--response-system)
@@ -25,9 +25,10 @@ Built for game-server hosting support (Indifferent Broccoli), with game detectio
- [Channel renames & moves (rate limits)](#channel-renames--moves-rate-limits)
- [Project structure](#project-structure)
- [Database collections](#database-collections)
- [HTTP: healthcheck & bOSScord API](#http-healthcheck--bosscord-api)
- [HTTP: healthcheck & optional API](#http-healthcheck--optional-api)
- [Gmail OAuth refresh token](#gmail-oauth-refresh-token)
- [Documentation in `docs/`](#documentation-in-docs)
- [Development & CI](#development--ci)
- [Troubleshooting](#troubleshooting)
- [References](#references)
- [License](#license)
@@ -35,12 +36,12 @@ Built for game-server hosting support (Indifferent Broccoli), with game detectio
---
## Features
###########
### Email → Discord
- Polls Gmail about every **30 seconds** for new mail.
- Creates a **Discord text channel** per email ticket (with overflow category support when a category is full).
- Detects **game** from subject/body using `GAME_LIST`.
- 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
@@ -50,7 +51,7 @@ Built for game-server hosting support (Indifferent Broccoli), with game detectio
### Ticket management
- **Claim / Unclaim** via buttons (not slash commands); optional claim overwrite and auto-unclaim.
- **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).
@@ -58,13 +59,16 @@ Built for game-server hosting support (Indifferent Broccoli), with game detectio
- **Transcripts** posted to a configured channel; closure email for email tickets.
- **Auto-close**, **inactivity reminders**, **auto-unclaim** (all optional via env).
### Staff personal categories (optional)
### Staff notifications (optional)
- Per-staff Discord **category map** so when someone **claims**, the main ticket channel can move into their category and a **mirror channel** can be created with a pinned embed linking back to the real ticket.
- When a **non-claimer** posts in the ticket channel, the mirror channel can be **pinged** with a quote and jump link; optional **DM** via `/notifydm`.
- Unclaim and close clean up mirror channels when configured.
- **`/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 who has a notification channel, the bot posts an alert there (subject to **per-ticket cooldown** hours, configurable via `/notification set` or admin **`/staffnotification`**).
- 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.
- **`/notifydm`** toggles optional **DM** alerts to the claimer on customer reply (separate from the notification channel); stored in **`StaffSettings`**.
See [Staff personal categories](#staff-personal-categories--mirror-channels).
See [Staff notification channels](#staff-notification-channels--reply-alerts).
**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.
### Extras
@@ -74,7 +78,7 @@ See [Staff personal categories](#staff-personal-categories--mirror-channels).
- **`/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` for the bOSScord cockpit when `BOSSCORD_API_KEY` is set.
- **Optional REST API** under `/api` when the relevant API key env vars are set (see `.env.example`).
---
@@ -84,18 +88,18 @@ See [Staff personal categories](#staff-personal-categories--mirror-channels).
┌─────────────────────────────────────────────────────────────────┐
│ BROCCOLINI BOT │
├─────────────────────────────────────────────────────────────────┤
│ Gmail (inbox) ──► gmail-poll.js ──► Discord ticket channels │
│ Gmail (inbox) ──► gmail-poll.js ──► Discord ticket channels
│ │ ▲ │
│ ▼ │ │
│ services/gmail.js ◄── handlers/messages.js │
│ services/tickets.js handlers/buttons.js
│ services/channelQueue.js handlers/commands.js
│ services/staffChannel.js
│ services/tickets.js handlers/buttons.js │
│ services/channelQueue.js
│ services/staffNotifications.js
│ │ │
│ ▼ │
│ MongoDB (Mongoose) ◄── models.js │
│ │
│ Express: GET / → "Active" ; optional /api → routes/bosscord.js
│ Express: GET / → "Active" ; optional /api → routes/
└─────────────────────────────────────────────────────────────────┘
```
@@ -111,10 +115,10 @@ See [Staff personal categories](#staff-personal-categories--mirror-channels).
| Requirement | Notes |
|-------------|--------|
| **Node.js** | **18+** recommended (Dockerfile uses 20). |
| **npm** | Install dependencies with `npm install`. |
| **MongoDB** | Atlas or self-hosted; connection string in `MONGODB_URI`. |
| **Discord application** | Bot token, application ID, privileged intents: **Message Content**, **Server Members**; also Guilds + Guild Messages. |
| **Node.js** | **18+**; Docker image uses **20** (`Dockerfile`). |
| **npm** | `npm install` locally; `npm ci --omit=dev` in Docker. |
| **MongoDB** | Atlas or 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. |
---
@@ -142,8 +146,8 @@ Restart after **any** `.env` change. After changing **slash command definitions*
Same as quick start. Optional:
- **Test env:** copy `.env.test.example``.env.test`, run `npm run start:test` (sets `ENV_FILE`).
- **1Password CLI:** `npm run start:1p` / `start:test:1p` inject secrets (see `docs/setup/1PASSWORD.md`).
- **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](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](docs/setup/ENV_AND_SECURITY.md).
@@ -151,7 +155,7 @@ Same as quick start. Optional:
## Configuration
Configuration is **environment variables** only, loaded in [`config.js`](config.js) as `CONFIG`. Discord env names in tables below match `.env.example`.
Configuration is **environment variables** only, loaded in [`config.js`](config.js) as `CONFIG`. Names below match `.env.example` unless noted.
### Discord (core)
@@ -160,12 +164,12 @@ Configuration is **environment variables** only, loaded in [`config.js`](config.
| `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 also validates this). |
| `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, if you use threads. |
| `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; aliases include `ROLE_TO_PING_ID` in code paths. |
| `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. |
@@ -187,19 +191,15 @@ Configuration is **environment variables** only, loaded in [`config.js`](config.
Slash `/escalate` and buttons require the appropriate tier IDs for **non-thread** channels (threads skip category moves).
### Staff personal categories & mirror channels
Optional organizational layer: **main ticket channel** stays where customers and staff talk; **mirror channel** is per-staffer category for notes + pings.
### Staff notifications, claimer display, admin
| Variable | Description |
|----------|-------------|
| `STAFF_CATEGORIES` | Map: `discordUserId:categoryId,discordUserId2:categoryId2`. If a claimer has **no** entry, **no** mirror channel is created (silent skip). |
| `STAFF_T1_CATEGORY` | Category for **unclaimed** “normal” tickets after unclaim (rename uses 🟢 prefix when rename limits allow). |
| `STAFF_T2_CATEGORY` | Pool category for **unclaimed tier-2** escalated tickets (used when moving after escalation / de-escalation flows). |
| `STAFF_T3_CATEGORY` | Pool category for **unclaimed tier-3** escalated tickets. |
| `UNCLAIMED_CATEGORY_ID` | Reserved in config for future/general fallback (currently not wired in code beyond `CONFIG`). |
If any of `STAFF_T1_CATEGORY` / `STAFF_T2_CATEGORY` / `STAFF_T3_CATEGORY` is unset, the corresponding **move is skipped** (no error).
| `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. |
### Google / Gmail
@@ -216,16 +216,16 @@ If any of `STAFF_T1_CATEGORY` / `STAFF_T2_CATEGORY` / `STAFF_T3_CATEGORY` is uns
|----------|----------|
| `MONGODB_URI` | Yes |
Test: `npm run test-mongodb` (or with `ENV_FILE=.env.test`).
Test: `npm run test-mongodb` (optionally with `ENV_FILE` / `.env.test` as above).
### Server & optional API
### 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. |
| `BOSSCORD_API_KEY` | — | If set, mounts **`/api`** (bOSScord); use a strong random key. |
| `BOSSCORD_CORS_ORIGIN` | `*` | Optional CORS for the API. |
Additional variables for mounting **`/api`** (API key, CORS, etc.) are listed in `.env.example` if you use that integration.
### Messaging & branding
@@ -236,24 +236,30 @@ See `.env.example` for defaults: `ESCALATION_MESSAGE` (`{support_name}`), `TICKE
- **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_*`.
- **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.
`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 personal categories & mirror channels
## Staff notification channels & reply alerts
When configured:
When `STAFF_NOTIFICATION_CATEGORY_ID` is set:
1. **Claim:** DB stores `claimerId` (Discord user id) and optional `staffChannelId`. Main channel may **move** to `STAFF_CATEGORIES.get(claimerId)`. A **mirror** text channel may be created under that staffers category, with a **pinned embed** (ticket number, customer, game, subject, link to original channel).
2. **Customer / other user messages** in the real ticket channel: mirror channel gets a **ping**, quote (truncated), and **jump link**; if the claimer enabled **`/notifydm`**, they also get a **DM** (`StaffSettings` in MongoDB).
3. **Unclaim:** mirror channel is **deleted**; `claimerId` / `staffChannelId` cleared; channel may rename with 🟢 and move to `STAFF_T1_CATEGORY` if set.
4. **Escalation / de-escalation:** slash and button flows may apply **priority emojis** to names and move channels to `STAFF_T2_CATEGORY` / `STAFF_T3_CATEGORY` / `STAFF_T1_CATEGORY` when configured; mirror channels can move with the tier category.
5. **Close:** mirror channel is **deleted** when the ticket closes.
1. **`/notification add`** (with a target member) creates a channel under that category and saves `userId``channelId` + 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).
---
@@ -265,11 +271,10 @@ npm start
node broccolini-discord.js
```
**Test:**
**Test / alternate env file:** see [Installation](#installation) for `ENV_FILE` on Windows vs Unix.
```bash
npm run start:test
npm run test-mongodb:test
npm run test-mongodb
```
**Docker** (see [`Dockerfile`](Dockerfile)):
@@ -279,7 +284,7 @@ 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.
Ensure `MONGODB_URI` and Discord token are available inside the container. A sample [`docker-compose.yml`](docker-compose.yml) exists—adjust **ports** and **env_file** for your host (do not copy production-specific bind addresses into new deployments without review).
---
@@ -294,19 +299,21 @@ Most commands require **staff** (`ROLE_ID_TO_PING` or `ADDITIONAL_STAFF_ROLES`).
| **`/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`** | `on` / `off` — DM when a **non-claimer** replies in a ticket you claimed (mirror ping still applies). |
| **`/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. |
| **`/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 TSV exports to `BACKUP_EXPORT_CHANNEL_ID`. |
| **`/backup`**, **`/export`** | Post exports to `BACKUP_EXPORT_CHANNEL_ID`. |
| **`/help`** | In-bot command summary embed. |
**Context menus**
@@ -318,7 +325,7 @@ Most commands require **staff** (`ROLE_ID_TO_PING` or `ADDITIONAL_STAFF_ROLES`).
## Ticket UI (buttons & modals)
- **Open ticket** (panel): modal fields are **account email**, **game**, **description** (not “priority” in the modal).
- **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`](utils/ticketComponents.js) / [`handlers/buttons.js`](handlers/buttons.js).
- **Email routing** and **tag delete** confirmations use additional button custom IDs.
@@ -328,7 +335,7 @@ Most commands require **staff** (`ROLE_ID_TO_PING` or `ADDITIONAL_STAFF_ROLES`).
### `/tag`
Sets `ticketTag` from a fixed list (Server Down, Billing, etc.). Channel naming may incorporate tag/priority emojis via ticket naming logic.
Sets `ticketTag` from the fixed list in `config.js` (`TICKET_TAGS`). Channel naming may incorporate tag/priority emojis via ticket naming logic.
### `/response`
@@ -355,9 +362,9 @@ Discord allows **two renames per 10 minutes** per channel. The bot serializes re
```
broccolini-bot/
├── broccolini-discord.js # Entry: Discord client, Express, Gmail poll interval, jobs
├── config.js # Env → CONFIG (+ STAFF_CATEGORIES map, game lists)
├── 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, …)
├── 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
@@ -366,7 +373,7 @@ broccolini-bot/
├── handlers/
│ ├── buttons.js # Claim/close/modals/escalate buttons, ticket create modal
│ ├── commands.js # Slash handlers, runEscalation/runDeescalation
│ ├── messages.js # Staff ↔ Gmail relay; mirror pings + notifydm
│ ├── messages.js # Staff ↔ Gmail relay; notifydm; notification alerts
│ ├── accountinfo.js
│ ├── analytics.js
│ └── setup.js
@@ -374,15 +381,17 @@ broccolini-bot/
│ ├── gmail.js
│ ├── tickets.js # Auto-close, reminders, auto-unclaim, naming helpers
│ ├── channelQueue.js # enqueueRename / enqueueMove
│ ├── staffChannel.js # Mirror create/ping/move/delete
│ ├── 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/bosscord.js # Optional /api routes
├── api/bosscordClient.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
```
@@ -393,21 +402,22 @@ broccolini-bot/
| Model / collection | Role |
|--------------------|------|
| **Ticket Gmail thread id, Discord channel/thread id, status, priority, claim (`claimedBy` display name), `claimerId`, `staffChannelId`, escalation tier, `welcomeMessageId`, `ticketTag`, etc.** |
| **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 & bOSScord API
## HTTP: healthcheck & optional API
- **`GET /`** → plain text **`Active`** (intended for load balancers / Docker `HEALTHCHECK`).
- **`/api/*`** is registered **only after** `ready` when `BOSSCORD_API_KEY` is set. JSON body parsing enabled. See [`routes/bosscord.js`](routes/bosscord.js) for routes.
- **`/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.
---
@@ -431,6 +441,13 @@ Index: **[docs/README.md](docs/README.md)**. Highlights:
| [MONGODB_SETUP.md](docs/setup/MONGODB_SETUP.md) | Database |
| [QUICKSTART.md](docs/setup/QUICKSTART.md) | First-time orientation |
| [PROJECT_STRUCTURE.md](docs/setup/PROJECT_STRUCTURE.md) | Layout (may overlap this README) |
| [1PASSWORD.md](docs/setup/1PASSWORD.md) | 1Password CLI for `npm run start:1p` |
---
## Development & CI
This repo includes [`.gitlab-ci.yml`](.gitlab-ci.yml) with GitLab **SAST** and **secret detection** templates. Adjust or extend stages in GitLab as needed for your fork.
---
@@ -444,6 +461,7 @@ Index: **[docs/README.md](docs/README.md)**. Highlights:
| **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. |
---