Initial commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-02-10 08:22:19 -06:00
commit 519788c633
39 changed files with 17121 additions and 0 deletions

102
docs/MONGODB_ZAMMAD_LINK.md Normal file
View File

@@ -0,0 +1,102 @@
# MongoDB Bridge ↔ Zammad Schema Mapping
Reference for linking the gmail-bridge (MongoDB) with Zammad's PostgreSQL schema. Source: [schema zammad.txt](schema%20zammad.txt).
## Primary link: Ticket ↔ Zammad ticket
| MongoDB (Ticket) | Zammad (tickets) | Notes |
|--------------------|------------------|--------|
| `zammadTicketId` | `id` (serial) | Primary link; set when we create a Zammad ticket via API. |
| `gmailThreadId` | — | Bridge-only; Gmail thread ID. |
| `discordThreadId` | — | Bridge-only; Discord channel ID. |
| `senderEmail` | `customer_id` → users.email | Zammad creates/finds user by email when we POST ticket. |
| `subject` | `title` | Ticket title. |
| `status` (open/closed) | `state_id` → ticket_states | We PATCH state to closed on force-close. |
## Zammad tables we touch via API
- **tickets** Create (POST), update state (PATCH). Key columns: `id`, `group_id`, `priority_id`, `state_id`, `title`, `customer_id`, `owner_id`, `number`, `discordusername`, `gameid`.
- **ticket_articles** Create (POST) when staff reply in Discord. Key columns: `ticket_id`, `type_id` (e.g. note), `sender_id`, `body`, `content_type`, `internal`, `subject`, `from`.
- **users** Zammad creates/finds customer by email on ticket create. If the sender email matches a MongoDB **User** (website user) with `discordID`, the bridge PATCHes the Zammad user with `discord_id` so the customer in Zammad has the Discord ID. Requires adding a custom attribute **discord_id** on the **User** object in Zammad (Admin → Object Manager → User).
## Zammad custom fields on tickets (from schema)
- **discordusername** (limit 120) Can store Discord display name for the ticket.
- **gameid** (limit 255) We already send this when creating a ticket (game key from bridge).
## Important MongoDB fields → Zammad custom attributes
These MongoDB Ticket fields matter for Zammad; the schema only has two custom columns on `tickets`, so we map into those or would need new attributes in Zammad.
| MongoDB (Ticket) | Zammad today | Action |
|-------------------|--------------|--------|
| **gameKey** (from email/game) | **gameid** | Already sent on ticket create. |
| **claimedBy** (Discord display name) | **discordusername** | Not set today. Should PATCH ticket when someone claims (e.g. set `discordusername` to `claimedBy` on claim, clear on unclaim). |
| **priority** (low/normal/medium/high) | **priority_id** (core) | Not synced. We could PATCH ticket when `/priority` is used so Zammad shows same priority. |
| **gmailThreadId** | — | No column in Zammad. If you add a custom attribute (e.g. `gmail_thread_id`) in Zammad, we can send it on create/update. |
| **discordThreadId** | — | No column in Zammad. Same: add e.g. `discord_channel_id` in Zammad if you want it there. |
| **escalated** | — | No column in Zammad. Add a custom attribute (e.g. `escalated` boolean) in Zammad if you want it visible in Zammad. |
**Recommended now (no Zammad schema change):**
1. **Set `discordusername` on claim** When a staff member claims the ticket in Discord, PATCH the Zammad ticket with `discordusername: claimedBy` so Zammad shows who is handling it on Discord. Clear it on unclaim.
2. **Sync priority to Zammad** When priority changes in Discord via `/priority`, PATCH the Zammad ticket with the matching priority (e.g. Zammad uses `1` low, `2` normal, `3` high or similar; check your Zammad priority_id values).
**Optional (requires adding custom attributes in Zammad):**
- **gmail_thread_id** Useful for agents to open or reference the Gmail thread.
- **discord_channel_id** Useful for deep links to the Discord thread.
- **escalated** If you want Zammad to show that the ticket was escalated from Discord.
## Email ticket → Zammad user with discord_id
When an email ticket comes in, the bridge:
1. Extracts sender email (`sEmail`).
2. Looks up **MongoDB User** (website user) by `email` (case-insensitive). If that user has a `discordID`, it is stored for the next step.
3. Creates the Zammad ticket (Zammad creates or finds the customer user by email).
4. If the ticket response has `customer_id` and we have a `discordID` from step 2, the bridge **PATCHes the Zammad user** with `discord_id: discordID`.
**Requirement:** In Zammad, add a custom attribute **discord_id** (text) on the **User** object: Admin → Object Manager → User → add attribute `discord_id`. Without it, the PATCH will fail (the bridge logs the error and continues).
## Discord ticket → ensure Zammad user exists
When a **Discord ticket** is created (modal “Create Support Ticket” or “Create ticket from message”):
1. The bridge looks up **MongoDB User** (website user) by the creators Discord ID (`interaction.user.id` or `message.author.id`).
2. If a User is found with an **email**, the bridge calls **ensureZammadUserForDiscordUser**:
- Searches Zammad for a user with that email (`GET /api/v1/users/search?query=...`).
- If none exists, **creates** a Zammad user (Customer) with email, firstname, lastname, and `discord_id`.
- If a user exists, optionally sets `discord_id` on them via PATCH.
3. No Zammad **ticket** is created for Discord-only tickets; only the Zammad **user** is ensured so they exist when you later create tickets or link them.
**Requirement:** Same as above: add **discord_id** on the User object in Zammad Object Manager if you want Discord ID stored. User create will still work without it; only the `discord_id` field will be skipped.
## Flow summary
1. **New email → new ticket**
Bridge creates MongoDB `Ticket` and calls Zammad `POST /api/v1/tickets`; stores returned `id` in `Ticket.zammadTicketId`.
2. **Staff reply in Discord**
Bridge sends reply to Gmail and calls Zammad `POST /api/v1/ticket_articles` with `ticket_id: ticket.zammadTicketId`.
3. **Force-close in Discord**
Bridge sets MongoDB `status: 'closed'` and calls Zammad `PATCH /api/v1/tickets/:id` with `state: 'closed'`.
## Creating Zammad objects to match the bridge
To ensure Zammad has the groups (and reference data) the bridge expects, run from `gmail-bridge`:
```bash
npm run create-zammad-objects
```
This script (see [scripts/create-zammad-objects.js](../scripts/create-zammad-objects.js)) uses the same `.env` as the bridge and:
- Creates the groups **Email Users** and **Discord Users** if they do not exist (from `ZAMMAD_EMAIL_GROUP` and `ZAMMAD_DISCORD_GROUP`).
- Lists **ticket priorities** and **ticket states** so you can verify IDs (e.g. for future priority sync).
Custom attributes (e.g. `gmail_thread_id`, `discord_channel_id`, `escalated`) must be added in Zammads admin UI (Object Manager) if you want them; the API for creating object attributes is admin-only.
## Optional improvements
- **Store Zammad customer id** If we ever need to reference the same customer across tickets, add `zammadCustomerId` to MongoDB Ticket and set it from the Zammad ticket create response.
- **Set discordusername on ticket** When adding an article from Discord, we could PATCH the ticket to set `discordusername` to the repliers display name (if Zammad API supports updating that field).