# Adding a New Channel Integration to AI Manager

> Comprehensive guide for adding a new messaging channel (Telegram, WhatsApp, Messenger, custom) to AI Manager.
> Based on analysis of all 6 existing channel connectors in the codebase.

---

## Table of Contents

1. [Architecture Overview](#1-architecture-overview)
2. [Existing Connectors Comparison](#2-existing-connectors-comparison)
3. [The Common Kernel (Copy Verbatim)](#3-the-common-kernel)
4. [Layer 1: npm Connector Package](#4-layer-1-npm-connector-package)
5. [Layer 2: Server PubModule](#5-layer-2-server-pubmodule)
6. [Layer 3: Dashboard UI](#6-layer-3-dashboard-ui)
7. [Layer 4: Helm Configuration](#7-layer-4-helm-configuration)
8. [End-to-End Checklist](#8-end-to-end-checklist)
9. [Appendix: Existing Connector Anatomy](#9-appendix-connector-anatomy)

---

## 1. Architecture Overview

Every channel integration follows a strict **4-layer architecture**:

```
┌─────────────────────────────────────────────────────────────┐
│                    LAYER 1: npm PACKAGE                      │
│  @tiledesk/tiledesk-{channel}-connector                      │
│                                                              │
│  ┌─────────────┐  ┌────────────────┐  ┌──────────────────┐  │
│  │ Express      │  │ Translator     │  │ API Client       │  │
│  │ Router       │  │ (msg format)   │  │ (3rd party API)  │  │
│  │              │  │                │  │                  │  │
│  │ /configure   │  │ {channel}→     │  │ sendMessage()    │  │
│  │ /install     │  │ Tiledesk       │  │ setWebhook()    │  │
│  │ /uninstall   │  │ Tiledesk→     │  │ getUpdates()    │  │
│  │ /update      │  │ {channel}      │  │                  │  │
│  │ /disconnect  │  │                │  │                  │  │
│  │ POST /inbound │  └────────────────┘  └──────────────────┘  │
│  │ POST /outbound│                                           │
│  └─────────────┘                                           │
│                                                              │
│  ┌──────────────────────────────────────────────────────────┐│
│  │  Common Kernel (shared across all connectors):           ││
│  │  KVBaseMongo · TiledeskAppsClient · TiledeskChannel     ││
│  │  TiledeskSubscriptionClient · MessageHandler             ││
│  └──────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    LAYER 2: SERVER PUBMODULE                  │
│  pubmodules/{channel}/                                       │
│  ┌──────────────┐  ┌──────────────┐                          │
│  │ index.js     │  │ listener.js  │                          │
│  │ (exports     │  │ (startApp    │                          │
│  │  listener +  │  │  wrapper)    │                          │
│  │  route)      │  │              │                          │
│  └──────────────┘  └──────────────┘                          │
│                                                              │
│  Then registered in: pubModulesManager.js                    │
│  - Constructor: this.{channel} = undefined                   │
│  - use(): app.use('/modules/{channel}', route)               │
│  - init(): require + listener.listen + store route           │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    LAYER 3: DASHBOARD UI                      │
│  11+ files across the dashboard app:                         │
│                                                              │
│  integrations/utils.ts        → INTEGRATIONS_KEYS, list      │
│  integrations/component.ts    → fetch app, render iframe     │
│  utils/constants.ts           → CHANNEL_TYPE_{NAME}          │
│  utils/util.ts                → CHANNELS_NAME, CHANNELS[]    │
│  HTML templates (4x)          → channel badges/icons         │
│  design-studio/constants.ts   → same CHANNEL_TYPE            │
│  assets/img/int/              → icon.png, logo.png           │
│  assets/img/channel_icons/    → channel SVG                  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    LAYER 4: HELM CONFIG                       │
│  values.yaml       → {CHANNEL}_API_URL, {CHANNEL}_TOKEN ... │
│  configmap.yaml    → env var declarations                    │
│  server-deployment → inject env vars via configMapKeyRef     │
└─────────────────────────────────────────────────────────────┘
```

### Data Flow (Inbound + Outbound)

```
┌──────────┐  POST /{channel}  ┌──────────────────────────────┐
│ 3rd Party│───────────────────▶│  npm Connector Package       │
│  Provider │                   │                              │
│ (Telegram│                   │  Translator.toTiledesk()     │
│  /WhatsApp│                   │  TiledeskChannel.send()      │
│  /...    │                   │    → JWT auth (lead_id)      │
│          │                   │    → create/find request     │
│          │                   │    → post message             │
└──────────┘                   └──────────┬───────────────────┘
                                          │
                                          ▼
                              ┌──────────────────────────┐
                              │  AI Manager Server        │
                              │  (routing + agents)       │
                              └──────────┬────────────────┘
                                          │
                                          ▼
┌──────────┐  POST /tiledesk   ┌──────────────────────────────┐
│ 3rd Party│◀──────────────────│  npm Connector Package       │
│  Provider │                   │                              │
│          │                   │  Translator.to{Channel}()    │
│          │                   │  Channel.sendMessage()      │
└──────────┘                   └──────────────────────────────┘
```

---

## 2. Existing Connectors Comparison

### All Six Channel Connectors in the Codebase

The `pubModulesManager.js` registers 6 channel types:

| # | Channel | npm Package | Version | Status | Module Path |
|---|---------|------------|---------|--------|-------------|
| 1 | **Telegram** | `@tiledesk/tiledesk-telegram-connector` | 0.1.15 | Published | `/modules/telegram` |
| 2 | **WhatsApp** | `@tiledesk/tiledesk-whatsapp-connector` | 0.1.66 | Published | `/modules/whatsapp` |
| 3 | **Messenger** | `@tiledesk/tiledesk-messenger-connector` | 0.1.30 | Published | `/modules/messenger` |
| 4 | **SMS** | (custom) | — | In codebase | `/modules/sms` |
| 5 | **Voice** | (custom, Twilio) | — | In codebase | `/modules/voice` |
| 6 | **Voice Twilio** | `@tiledesk/tiledesk-twilio-connector` | — | Not on npm | `/modules/voice-twilio` |

### File Structure Comparison (npm packages)

```
Telegram 0.1.15 (805 loc)     WhatsApp 0.1.66 (1720 loc)     Messenger 0.1.30 (962 loc)
──────────────────────────    ──────────────────────────     ──────────────────────────
index.js                      index.js                       index.js
package.json                  package.json                   package.json
publish.sh                    publish.sh                     publish.sh
winston.js                    winston.js                     winston.js
models/Setting.js             models/Transaction.js          (no models/)
                              models/WhatsappLog.js
tiledesk/                     tiledesk/                      tiledesk/
├── KVBaseMongo.js            ├── KVBaseMongo.js             ├── KVBaseMongo.js
├── MessageHandler.js         ├── MessageHandler.js          ├── MessageHandler.js
├── TiledeskAppsClient.js     ├── TiledeskAppsClient.js      ├── TiledeskAppsClient.js
├── TiledeskChannel.js        ├── TiledeskChannel.js         ├── TiledeskChannel.js
├── TiledeskSubscription     ├── TiledeskSubscription       ├── TiledeskSubscription
    Client.js                     Client.js                      Client.js
├── TiledeskTelegram.js       ├── TiledeskWhatsapp.js        ├── FacebookClient.js
├── TiledeskTelegram         ├── TiledeskWhatsapp           ├── TiledeskMessenger
    Translator.js                 Translator.js                   Translator.js
                              ├── Scheduler.js
                              ├── TemplateManager.js
                              ├── TiledeskBotTester.js
                              ├── WhatsappLogger.js
template/                     template/                      template/
├── configure.html            ├── configure.html             ├── configure.html
├── detail.html               ├── detail.html                ├── detail.html
├── error.html                ├── error.html                 ├── error.html
├── css/                      ├── css/                       ├── css/
│   ├── configure.css         │   ├── configure.css          │   ├── configure.css
│   ├── detail.css            │   ├── detail.css             │   ├── detail.css
│   ├── error.css             │   ├── error.css              │   ├── error.css
│   └── style.css             │   ├── style.css              │   └── (no style.css)
│                             │   ├── template_detail.css
│                             │   └── templates.css
│                             ├── template_detail.html
│                             └── templates.html
```

### Common vs Provider-Specific Code

| Component | File(s) | Common? | Notes |
|-----------|---------|---------|-------|
| **KVBaseMongo** | `tiledesk/KVBaseMongo.js` | ✅ Nearly identical | Constructor API differs slightly: Telegram uses `{KVBASE_COLLECTION, log}` object, others use string |
| **TiledeskAppsClient** | `tiledesk/TiledeskAppsClient.js` | ✅ Same | REST client for Tiledesk Apps API |
| **TiledeskSubscriptionClient** | `tiledesk/TiledeskSubscriptionClient.js` | ✅ Same | Manages webhook subscriptions in Tiledesk |
| **TiledeskChannel** | `tiledesk/TiledeskChannel.js` | ✅ Same pattern | Core channel logic: auth, request create, message send |
| **MessageHandler** | `tiledesk/MessageHandler.js` | ✅ Same | Processes incoming messages |
| **Translator** | `tiledesk/*Translator.js` | ❌ Custom per channel | Converts message format: `{channel}↔Tiledesk` |
| **API Client** | `tiledesk/Tiledesk{channel}.js` or `{provider}Client.js` | ❌ Custom per channel | Calls the external provider's API |
| **lead_id prefix** | `TiledeskChannel.js` | ❌ Custom per channel | Telegram uses `telegram-` prefix in lead ID; WhatsApp/Messenger have this disabled |
| **Express routes** | `index.js` | ✅ Same structure | All expose same endpoints: `/`, `/detail`, `/configure`, `/install`, `/uninstall`, `/update`, `/disconnect`, inbound, outbound |
| **Handlebars templates** | `template/*.html` | ✅ Same structure | CSS/HTML pattern is identical, content differs per channel |
| **Helm env vars** | `values.yaml` | ❌ Only if needed | Telegram needs API URLs; WhatsApp/Messenger don't need any |

---

## 3. The Common Kernel

These files from the Telegram connector can be **copied verbatim** for any new channel (with minor adjustments).

### 3.1 KVBaseMongo.js

Simple key-value store using MongoDB. Stores connector settings (tokens, bot names, department mappings).

```js
// Telegram version (uses config object — recommended pattern):
const { KVBaseMongo } = require('./tiledesk/KVBaseMongo');
const db = new KVBaseMongo({ KVBASE_COLLECTION: 'kvstore', log: false });

// WhatsApp/Messenger version (uses string arg — simpler but no log control):
const { KVBaseMongo } = require('./tiledesk/KVBaseMongo');
const db = new KVBaseMongo('kvstore');
```

**Recommendation:** Use the config object pattern for new channels.

### 3.2 TiledeskAppsClient.js

REST client for the Tiledesk Apps API (`/modules/apps`). Used to register/unregister app installations and fetch settings.

```js
const { TiledeskAppsClient } = require('./tiledesk/TiledeskAppsClient');
const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
let installation = await appClient.getInstallations(projectId, appId);
```

### 3.3 TiledeskSubscriptionClient.js

Manages webhook subscriptions in Tiledesk. When a channel is installed, the connector subscribes to events (e.g., `message.create` for Telegram) so Tiledesk forwards outgoing messages to the connector.

```js
const { TiledeskSubscriptionClient } = require('./tiledesk/TiledeskSubscriptionClient');
const subClient = new TiledeskSubscriptionClient({ API_URL: API_URL });
const subscription = await subClient.subscribe(projectId, token, eventName, targetUrl);
```

### 3.4 TiledeskChannel.js

The core channel logic. This is the **same pattern** across all three connectors — it handles:
- Creating a JWT with the channel-specific `lead_id`
- Authenticating via `signinWithCustomToken`
- Creating or finding a conversation (request)
- Posting messages

**The customization point** is the lead_id prefix. In Telegram:

```js
// Telegram (TiledeskChannel.js, lines ~71-74):
new_request_id = "support-group-" + this.settings.project_id + "-"
    + uuidv4().substring(0, 8) + "-telegram-" + channel.from;

payload = {
    _id: 'telegram-' + channel.from,  // <-- THIS becomes the lead_id
    ...
}
```

For a new channel, replace `'telegram-'` with your channel's prefix (e.g., `'viber-'`, `'signal-'`).

### 3.5 MessageHandler.js

Processes incoming messages from the channel. Contains the logic for determining whether to create a new conversation or continue an existing one, and for formatting messages.

This file is **identical** across all three connectors and can be copied without changes.

---

## 4. Layer 1: npm Connector Package

### 4.1 Package Structure

```
tiledesk-{channel}-connector/
├── index.js                    # Main entry — Express router + startApp()
├── package.json                # npm package manifest
├── winston.js                  # Logger configuration
├── publish.sh                  # Publish script (optional)
├── tiledesk/
│   ├── KVBaseMongo.js          # Key-value store (common kernel)
│   ├── MessageHandler.js        # Message processor (common kernel)
│   ├── TiledeskAppsClient.js    # Apps API client (common kernel)
│   ├── TiledeskChannel.js       # Core channel logic (common kernel)
│   ├── TiledeskSubscription    # Subscription client (common kernel)
│   │   Client.js
│   ├── Tiledesk{Channel}.js    # ** YOUR CHANNEL'S API CLIENT **
│   └── Tiledesk{Channel}       # ** YOUR CHANNEL'S TRANSLATOR **
│       Translator.js
├── template/
│   ├── configure.html           # Configuration form (Handlebars)
│   ├── detail.html             # App detail page (Handlebars)
│   ├── error.html              # Error page (Handlebars)
│   └── css/
│       ├── configure.css
│       ├── detail.css
│       ├── error.css
│       └── style.css
└── test/
    └── test_translate_{channel}.js  # Tests for translator
```

### 4.2 Required Exports

The package MUST export exactly:

```js
module.exports = { router: router, startApp: startApp };
```

### 4.3 startApp(settings, callback)

Mandatory settings:
```js
startApp({
    MONGODB_URL: '...',           // MongoDB connection string (mandatory)
    dbconnection: existingConn,    // Existing mongoose connection (optional)
    API_URL: '...',               // AI Manager API URL (mandatory)
    BASE_URL: '...',              // Base URL for this connector (for webhooks)
    APPS_API_URL: '...',          // Tiledesk Apps API URL
    BRAND_NAME: '...',            // Optional brand name
    log: 'debug'                  // Log level
}, (err) => {
    if (!err) winston.info("Channel started");
});
```

### 4.4 Required Express Routes

| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/` | Health check / welcome message |
| `GET` | `/detail` | App detail page (query params: `project_id`, `token`, `app_id`) |
| `GET` | `/configure` | Configuration form (query params: `project_id`, `token`, `app_id`) |
| `POST` | `/install` | Install the app — creates installation record |
| `POST` | `/uninstall` | Uninstall the app — removes installation |
| `POST` | `/update` | Save configuration (bot name, token, department) |
| `POST` | `/disconnect` | Disconnect — remove settings + webhook |
| `POST` | `/{channel}` | **Inbound**: receive messages from the provider |
| `POST` | `/tiledesk` | **Outbound**: receive messages FROM Tiledesk TO the provider |

### 4.5 Translator Class

The translator converts between the provider's message format and Tiledesk's internal format.

```js
// Inbound: 3rd Party → Tiledesk
class Tiledesk{Channel}Translator {
    static CHANNEL_NAME = "telegram"; // your channel name

    toTiledesk(providerMessage) {
        // Convert provider message format → Tiledesk format
        return {
            text: providerMessage.text,
            sender: providerMessage.from.id,
            senderFullName: providerMessage.from.first_name,
            timestamp: providerMessage.date,
            // ...
        };
    }

    to{Channel}(tiledeskMessage) {
        // Convert Tiledesk message → provider format
        return {
            chat_id: recipientId,
            text: tiledeskMessage.text,
            // ...
        };
    }
}
```

### 4.6 API Client Class

The API client handles communication with the external provider's API.

```js
class Tiledesk{Channel} {
    constructor(settings) {
        this.API_TOKEN = settings.API_TOKEN;
        this.API_URL = settings.API_URL;
    }

    async sendMessage(chatId, text) {
        // Call the provider's API to send a message
        // e.g., POST https://api.provider.com/sendMessage
    }

    async setWebhook(url) {
        // Register your webhook URL with the provider
    }
}
```

### 4.7 lead_id Pattern

Each channel identifies its users with a unique `lead_id` prefix. This is set in `TiledeskChannel.js`:

```js
// Telegram pattern:
new_request_id = "support-group-" + projectId + "-" + uuidv4() + "-telegram-" + userId;
payload._id = 'telegram-' + channel.from;
```

For your new channel, choose a unique prefix:
```js
new_request_id = "support-group-" + projectId + "-" + uuidv4() + "-{channel}-" + userId;
payload._id = '{channel}-' + channel.from;
```

The Dashboard parses this prefix to show the correct channel badge (see Layer 3).

### 4.8 MongoDB KV Store Schema

Settings are stored in the `kvstore` MongoDB collection:

```js
// Stored by TiledeskAppsClient on install:
{
    app_version: "1.0.0",
    project_id: "projectId",
    token: "jwt_token",
    subscriptionId: "sub_id",
    secret: "webhook_secret",     // generated by Tiledesk
    bot_name: "MyBot",
    {channel}_token: "provider_api_token",
    department_id: "dept_id"
}
```

---

## 5. Layer 2: Server PubModule

### 5.1 pubmodules/{channel}/index.js

```js
const listener = require('./listener');

const {channel} = require("@tiledesk/tiledesk-{channel}-connector");
const {channel}Route = {channel}.router;

module.exports = { listener: listener, {channel}Route: {channel}Route }
```

### 5.2 pubmodules/{channel}/listener.js

```js
const {channel} = require("@tiledesk/tiledesk-{channel}-connector");
var winston = require('../../config/winston');
var configGlobal = require('../../config/global');
const mongoose = require("mongoose");

const apiUrl = process.env.API_URL || configGlobal.apiUrl;

class Listener {
    listen(config) {
        winston.info("{Channel} Listener listen");

        // Read any channel-specific env vars
        let channel_api_url = process.env.{CHANNEL}_API_URL || "https://default.api.url";

        // If the channel doesn't need custom env vars (like WhatsApp/Messenger),
        // you can omit the env var reading

        {channel}.startApp({
            MONGODB_URL: config.databaseUri,
            dbconnection: mongoose.connection,
            API_URL: apiUrl,
            BASE_URL: apiUrl + "/modules/{channel}",
            APPS_API_URL: apiUrl + "/modules/apps",
            // Add any channel-specific settings here
        }, (err) => {
            if (!err) {
                winston.info("Tiledesk {Channel} Connector successfully started");
            } else {
                winston.info("unable to start Tiledesk {Channel} Connector. " + err);
            }
        });
    }
}

var listener = new Listener();
module.exports = listener;
```

### 5.3 Registration in pubModulesManager.js

You need to add 3 things in `pubModulesManager.js`:

**1. Constructor (properties declaration):**
```js
// Line ~33 (alongside other channels):
this.{channel} = undefined;
this.{channel}Route = undefined;
```

**2. Route mounting in `use()` method:**
```js
// Line ~99 (alongside other channels):
if (this.{channel}Route) {
    app.use('/modules/{channel}', this.{channel}Route);
    winston.info("PubModulesManager {channel}Route controller loaded");
}
```

**3. Module loading in `init()` method:**
```js
// Alongside other channel inits (line ~340):
try {
    this.{channel} = require('./{channel}');
    winston.info("this.{channel}: " + this.{channel});
    this.{channel}.listener.listen(config);

    this.{channel}Route = this.{channel}.{channel}Route;

    winston.info("PubModulesManager initialized apps ({channel}).")
} catch(err) {
    if (err.code == 'MODULE_NOT_FOUND') {
        winston.info("PubModulesManager init apps module not found ");
    } else {
        winston.info("PubModulesManager error initializing init apps module", err);
    }
}
```

### 5.4 WhatsApp Has an Optional Env Guard

The Voice connectors use an env var guard to conditionally load:
```js
if (process.env.VOICE_TOKEN === process.env.VOICE_SECRET) {
    // Only load voice if tokens match
}
```

You can add a similar guard if your channel should only load when configured.

---

## 6. Layer 3: Dashboard UI

### 6.1 Required Constants (5 files)

**File 1: `dashboard/src/app/utils/constants.ts`**
```ts
export const CHANNEL_TYPE_{CHANNEL} = '{channel}';
```

**File 2: `dashboard/src/app/utils/util.ts` — CHANNELS_NAME**
```ts
CHANNELS_NAME.{CHANNEL} = '{channel}';
```

**File 3: `dashboard/src/app/utils/util.ts` — CHANNELS array**
```ts
CHANNELS: [
    // ... existing channels
    { id: '{channel}', name: '{Channel Name}' }
]
```

**File 4: `dashboard/src/app/integrations/utils.ts` — INTEGRATIONS_KEYS**
```ts
INTEGRATIONS_KEYS.{CHANNEL} = '{channel}';
```

**File 5: `dashboard/src/app/integrations/utils.ts` — APPS_TITLE**
```ts
APPS_TITLE.{CHANNEL} = "{Channel Display Name}";
```

**File 6: `dashboard/src/app/integrations/utils.ts` — INTEGRATIONS array**
```ts
{ name: "{Channel Name}",
  category: INTEGRATIONS_CATEGORIES.CHANNEL,
  key: INTEGRATIONS_KEYS.{CHANNEL},
  src_icon: "assets/img/int/{channel}-icon.png",
  src_logo: "assets/img/int/{channel}-logo.png",
  pro: false,
  plan: 'Sandbox' }
```

**File 7: `design-studio/src/chat21-core/utils/constants.ts`**
```ts
export const CHANNEL_TYPE_{CHANNEL} = '{channel}';
```

### 6.2 Integrations Component

In `integrations/integrations.component.ts`, add to the `getApps()` method:

```ts
let {channel}App = response.apps.find(a => (a.title === APPS_TITLE.{CHANNEL} && a.version === "v2"));
if (environment['{channel}ConfigUrl']) {
    if ({channel}App) {
        {channel}App.runURL = environment['{channel}ConfigUrl'];
        {channel}App.channel = "{channel}";
    } else {
        {channel}App = {
            runURL: environment['{channel}ConfigUrl'],
            channel: "{channel}"
        }
    }
} else {
    if ({channel}App) {
        {channel}App.channel = "{channel}";
    }
}
this.availableApps.push({channel}App);
```

Also add visibility management in `manageAppVisibility()`:
```ts
if (projectProfileData.customization[this.INT_KEYS.{CHANNEL}] === false) {
    let index = this.INTEGRATIONS.findIndex(i => i.key === this.INT_KEYS.{CHANNEL});
    if (index != -1) { this.INTEGRATIONS.splice(index, 1) };
}
```

### 6.3 HTML Templates (4 files)

Add channel badge/icon handling in each conversation list template:

**`ws-requests-msgs.component.html`** — conversation detail (channel badge):
```html
<ng-container *ngIf="request.channel_type === CHANNELS_NAME.{CHANNEL}">
    <img src="assets/img/channel_icons/{channel}.svg" class="channel-icon-small">
    <span>{{Channel Name}} </span>
    <!-- Phone number display if applicable -->
</ng-container>
```

**`ws-requests-unserved.component.html`** — unserved list:
```html
<ng-container *ngSwitchCase="CHANNELS_NAME.{CHANNEL}">
    <img src="assets/img/channel_icons/{channel}.svg" class="channel-icon">
</ng-container>
```

**`ws-requests-served.component.html`** — served list:
```html
<ng-container *ngSwitchCase="CHANNELS_NAME.{CHANNEL}">
    <img src="assets/img/channel_icons/{channel}.svg" class="channel-icon">
</ng-container>
```

**`history-and-nort-convs.component.html`** — history list:
```html
<ng-container *ngSwitchCase="CHANNELS_NAME.{CHANNEL}">
    <img src="assets/img/channel_icons/{channel}.svg" class="channel-icon">
</ng-container>
```

### 6.4 Assets

```
dashboard/src/assets/img/int/
├── {channel}-icon.png    # Small icon for integrations list
└── {channel}-logo.png    # Logo for integrations list

dashboard/src/assets/img/channel_icons/
└── {channel}.svg         # SVG icon for conversation badges
```

---

## 7. Layer 4: Helm Configuration

### 7.1 Optional: Only If Your Channel Needs Custom Env Vars

Telegram needs custom env vars because it calls the Telegram Bot API directly. WhatsApp and Messenger do NOT need custom env vars — their API credentials are stored in the connector's MongoDB KV store (configured via the dashboard UI).

**If your channel needs custom env vars (like Telegram):**

**values.yaml:**
```yaml
{CHANNEL}_API_URL: https://api.provider.com/bot
{CHANNEL}_FILE_URL: https://api.provider.com/file/bot
{CHANNEL}_LOG: info
```

**configmap.yaml:**
```yaml
{CHANNEL}_API_URL: {{ .Values.{CHANNEL}_API_URL | quote }}
{CHANNEL}_FILE_URL: {{ .Values.{CHANNEL}_FILE_URL | quote }}
```

**server-deployment.yaml:**
```yaml
- name: {CHANNEL}_API_URL
  valueFrom:
    configMapKeyRef:
      name: {{ include "aimanager.fullname" . }}-config
      key: {CHANNEL}_API_URL
```

### 7.2 If Your Channel Doesn't Need Custom Env Vars

No Helm changes needed. The connector stores everything in MongoDB via its own configuration form (like WhatsApp/Messenger).

---

## 8. End-to-End Checklist

### Phase 1: Create the npm Package

- [ ] Clone `@tiledesk/tiledesk-telegram-connector` as template
- [ ] Rename package in `package.json` to `@tiledesk/tiledesk-{channel}-connector`
- [ ] Create `tiledesk/Tiledesk{Channel}.js` — API client for the new provider
- [ ] Create `tiledesk/Tiledesk{Channel}Translator.js` — message format converter
- [ ] Update `index.js`:
  - [ ] Rename the provider references
  - [ ] Update `startApp()` to read any channel-specific settings
  - [ ] Update route paths for inbound/outbound webhooks
- [ ] Update `TiledeskChannel.js`:
  - [ ] Change `lead_id` prefix from `telegram-` to `{channel}-`
  - [ ] Update request_id format
- [ ] Update `template/configure.html` — change form fields for your provider
- [ ] Publish to npm: `npm publish`

### Phase 2: Server PubModule

- [ ] Create `pubmodules/{channel}/index.js`
- [ ] Create `pubmodules/{channel}/listener.js`
- [ ] Add npm dependency to server's `package.json`
- [ ] Register in `pubModulesManager.js`:
  - [ ] Constructor: `this.{channel} = undefined;`
  - [ ] `use()`: `app.use('/modules/{channel}', route)`
  - [ ] `init()`: try/catch with require + listen

### Phase 3: Dashboard UI

- [ ] Add `CHANNEL_TYPE_{CHANNEL}` to dashboard `constants.ts`
- [ ] Add `CHANNELS_NAME.{CHANNEL}` and CHANNELS[] entry in `util.ts`
- [ ] Add `INTEGRATIONS_KEYS.{CHANNEL}` and `APPS_TITLE.{CHANNEL}` in `integrations/utils.ts`
- [ ] Add INTEGRATIONS[] entry (with icons, plan)
- [ ] Add channel app fetching in `integrations.component.ts` `getApps()`
- [ ] Add channel visibility management in `manageAppVisibility()`
- [ ] Add channel badge to 4 HTML templates
- [ ] Add `CHANNEL_TYPE_{CHANNEL}` to design-studio `constants.ts`
- [ ] Add icon, logo, and SVG channel icons to assets

### Phase 4: Helm (if needed)

- [ ] Add env vars to `values.yaml` (if provider-specific URLs needed)
- [ ] Add env vars to `configmap.yaml`
- [ ] Add env vars to `server-deployment.yaml`

### Phase 5: Deploy

- [ ] Push the npm package
- [ ] Commit server submodule with new pubmodule + dep
- [ ] Commit dashboard submodule changes
- [ ] Commit Helm changes (if any)
- [ ] ArgoCD sync
- [ ] Test: Dashboard → Apps → Configure → Send test message

---

## 9. Appendix: Connector Anatomy

### Telegram Connector (Reference Implementation)

**Used as the best starting template.** Has all the pieces, well-structured, moderate complexity.

```
index.js (805 lines):
├── Express Router setup
├── Handlebars helpers
├── GET  / → welcome
├── GET  /detail → app detail page
├── GET  /configure → configuration form
├── POST /install → install app
├── POST /uninstall → uninstall app
├── POST /update → save settings
├── POST /update_advanced → toggle settings
├── POST /disconnect → remove + cleanup
├── POST /telegram → INBOUND (Telegram→Tiledesk)
├── POST /tiledesk → OUTBOUND (Tiledesk→Telegram)
└── startApp() → bootstrap + webhook register

tiledesk/TiledeskTelegram.js (API client):
├── constructor(config)
├── sendMessage(chatId, text, options)
└── setWebhook(url, certificate)

tiledesk/TiledeskTelegramTranslator.js:
├── CHANNEL_NAME = "telegram"
├── toTiledesk(rawMessage) → Tiledesk format
└── toTelegram(tiledeskMsg) → Telegram format

tiledesk/TiledeskChannel.js (175 lines):
├── JWT creation with lead_id
├── signinWithCustomToken
├── create/find request
├── post message
└── sender_id filtering (skip echo)
```

### WhatsApp Connector (Most Feature-Rich)

```
index.js (1720 lines):
├── Same route structure as Telegram
├── Plus: /ext and /api routes
├── Plus: Redis integration for session management
├── Plus: Twilio WhatsApp template management
├── Plus: Bot tester utilities
└── startApp() → async

Additional files:
├── tiledesk/Scheduler.js           → message scheduling
├── tiledesk/TemplateManager.js     → WhatsApp message templates
├── tiledesk/TiledeskBotTester.js   → bot testing utilities
├── tiledesk/WhatsappLogger.js      → dedicated logging
├── models/Transaction.js           → transaction tracking
├── models/WhatsappLog.js           → log storage
```

### Messenger Connector (Simplest)

```
index.js (962 lines):
├── Same route structure as Telegram
└── Facebook-specific webhook verification

Additional files:
├── tiledesk/FacebookClient.js      → Facebook Graph API client
```

---

## Quick Reference Card

```
NEW CHANNEL CHECKLIST:
─────────────────────────────────────────────────────────
□ npm package  → tiledesk/{channel}/ (index.js + startApp)
□ Translator   → Tiledesk{Channel}Translator.js
□ API client   → Tiledesk{Channel}.js
□ lead_id      → "{channel}-" prefix in TiledeskChannel.js
□ listener.js  → pubmodules/{channel}/listener.js
□ index.js     → pubmodules/{channel}/index.js
□ manager      → 3 edits in pubModulesManager.js
□ constants    → CHANNEL_TYPE in 2 files (dashboard + CDS)
□ integrations → INTEGRATIONS_KEYS + APPS_TITLE + list entry
□ component    → getApps() in integrations.component.ts
□ templates    → 4 HTML files for channel badges
□ assets       → icon.png + logo.png + channel.svg
□ Helm         → env vars (only if provider-specific URLs)
─────────────────────────────────────────────────────────
```
