@builderbot/provider-gohighlevel 1.3.15-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +463 -0
- package/dist/gohighlevel/core.d.ts +20 -0
- package/dist/gohighlevel/core.d.ts.map +1 -0
- package/dist/gohighlevel/provider.d.ts +115 -0
- package/dist/gohighlevel/provider.d.ts.map +1 -0
- package/dist/index.cjs +13056 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/interface/gohighlevel.d.ts +12 -0
- package/dist/interface/gohighlevel.d.ts.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/channelLister.d.ts +24 -0
- package/dist/utils/channelLister.d.ts.map +1 -0
- package/dist/utils/contactResolver.d.ts +10 -0
- package/dist/utils/contactResolver.d.ts.map +1 -0
- package/dist/utils/downloadFile.d.ts +11 -0
- package/dist/utils/downloadFile.d.ts.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/number.d.ts +2 -0
- package/dist/utils/number.d.ts.map +1 -0
- package/dist/utils/processIncomingMsg.d.ts +3 -0
- package/dist/utils/processIncomingMsg.d.ts.map +1 -0
- package/dist/utils/tokenManager.d.ts +36 -0
- package/dist/utils/tokenManager.d.ts.map +1 -0
- package/dist/utils/webhookVerification.d.ts +20 -0
- package/dist/utils/webhookVerification.d.ts.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# @builderbot/provider-gohighlevel
|
|
2
|
+
|
|
3
|
+
GoHighLevel provider for BuilderBot - Supports SMS, WhatsApp, Email, Live Chat, Facebook, Instagram and Custom channels via GHL API v2.
|
|
4
|
+
|
|
5
|
+
## Supported Channels
|
|
6
|
+
|
|
7
|
+
| Channel | Type |
|
|
8
|
+
|---------|------|
|
|
9
|
+
| SMS | `SMS` |
|
|
10
|
+
| WhatsApp | `WhatsApp` |
|
|
11
|
+
| Email | `Email` |
|
|
12
|
+
| Live Chat | `Live_Chat` |
|
|
13
|
+
| Facebook | `Facebook` |
|
|
14
|
+
| Instagram | `Instagram` |
|
|
15
|
+
| Custom | `Custom` |
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @builderbot/provider-gohighlevel
|
|
21
|
+
# or
|
|
22
|
+
npm install @builderbot/provider-gohighlevel
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Prerequisites - GoHighLevel Configuration
|
|
26
|
+
|
|
27
|
+
Before using this provider, you need to configure your GoHighLevel App in the Marketplace.
|
|
28
|
+
|
|
29
|
+
### Step 1: Create App in GHL Marketplace
|
|
30
|
+
|
|
31
|
+
1. Go to [GHL Marketplace](https://marketplace.gohighlevel.com)
|
|
32
|
+
2. Click on **"Create App"**
|
|
33
|
+
3. Fill in the app details:
|
|
34
|
+
- App Name
|
|
35
|
+
- Description
|
|
36
|
+
- App Type: **Private** (for your own use) or **Public**
|
|
37
|
+
|
|
38
|
+
### Step 2: Configure OAuth2 Scopes
|
|
39
|
+
|
|
40
|
+
In your app settings, enable the following scopes:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
conversations.readonly
|
|
44
|
+
conversations.write
|
|
45
|
+
conversations/message.readonly
|
|
46
|
+
conversations/message.write
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 3: Get Client Credentials
|
|
50
|
+
|
|
51
|
+
After creating the app, you'll receive:
|
|
52
|
+
- **Client ID** - Your OAuth2 client identifier
|
|
53
|
+
- **Client Secret** - Your OAuth2 client secret (keep this secure!)
|
|
54
|
+
|
|
55
|
+
### Step 4: Configure Redirect URI
|
|
56
|
+
|
|
57
|
+
Set your Redirect URI to point to your server's OAuth callback endpoint:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
https://your-domain.com/oauth/callback
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For local development:
|
|
64
|
+
```
|
|
65
|
+
http://localhost:3000/oauth/callback
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 5: Get Location ID
|
|
69
|
+
|
|
70
|
+
1. Go to your GoHighLevel sub-account
|
|
71
|
+
2. Navigate to **Settings > Business Profile**
|
|
72
|
+
3. Copy the **Location ID** (also visible in the URL)
|
|
73
|
+
|
|
74
|
+
Alternatively, find it in the URL when logged into a sub-account:
|
|
75
|
+
```
|
|
76
|
+
https://app.gohighlevel.com/v2/location/YOUR_LOCATION_ID/...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Environment Variables
|
|
80
|
+
|
|
81
|
+
Create a `.env` file with your credentials:
|
|
82
|
+
|
|
83
|
+
```env
|
|
84
|
+
# Required
|
|
85
|
+
GHL_CLIENT_ID=your_client_id_here
|
|
86
|
+
GHL_CLIENT_SECRET=your_client_secret_here
|
|
87
|
+
GHL_LOCATION_ID=your_location_id_here
|
|
88
|
+
|
|
89
|
+
# Optional
|
|
90
|
+
GHL_REDIRECT_URI=http://localhost:3000/oauth/callback
|
|
91
|
+
GHL_WEBHOOK_SECRET=your_webhook_secret_for_hmac_verification
|
|
92
|
+
GHL_CHANNEL_TYPE=WhatsApp
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Basic Usage
|
|
96
|
+
|
|
97
|
+
### Minimal Setup
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { createBot, createProvider, createFlow, addKeyword } from '@builderbot/bot'
|
|
101
|
+
import { GoHighLevelProvider } from '@builderbot/provider-gohighlevel'
|
|
102
|
+
import { MemoryDB } from '@builderbot/bot'
|
|
103
|
+
|
|
104
|
+
// Create the provider
|
|
105
|
+
const provider = createProvider(GoHighLevelProvider, {
|
|
106
|
+
clientId: process.env.GHL_CLIENT_ID,
|
|
107
|
+
clientSecret: process.env.GHL_CLIENT_SECRET,
|
|
108
|
+
locationId: process.env.GHL_LOCATION_ID,
|
|
109
|
+
channelType: 'WhatsApp',
|
|
110
|
+
redirectUri: process.env.GHL_REDIRECT_URI,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Create a simple flow
|
|
114
|
+
const welcomeFlow = addKeyword(['hello', 'hi'])
|
|
115
|
+
.addAnswer('Welcome! How can I help you today?')
|
|
116
|
+
|
|
117
|
+
// Create and start the bot
|
|
118
|
+
const main = async () => {
|
|
119
|
+
await createBot({
|
|
120
|
+
flow: createFlow([welcomeFlow]),
|
|
121
|
+
provider,
|
|
122
|
+
database: new MemoryDB(),
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
console.log('Bot is running!')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### With Webhook Signature Verification (Recommended for Production)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const provider = createProvider(GoHighLevelProvider, {
|
|
135
|
+
clientId: process.env.GHL_CLIENT_ID,
|
|
136
|
+
clientSecret: process.env.GHL_CLIENT_SECRET,
|
|
137
|
+
locationId: process.env.GHL_LOCATION_ID,
|
|
138
|
+
channelType: 'WhatsApp',
|
|
139
|
+
redirectUri: process.env.GHL_REDIRECT_URI,
|
|
140
|
+
webhookSecret: process.env.GHL_WEBHOOK_SECRET, // HMAC SHA256 verification
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### With Pre-existing Tokens
|
|
145
|
+
|
|
146
|
+
If you already have access tokens (e.g., from a previous session):
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const provider = createProvider(GoHighLevelProvider, {
|
|
150
|
+
clientId: process.env.GHL_CLIENT_ID,
|
|
151
|
+
clientSecret: process.env.GHL_CLIENT_SECRET,
|
|
152
|
+
locationId: process.env.GHL_LOCATION_ID,
|
|
153
|
+
channelType: 'WhatsApp',
|
|
154
|
+
accessToken: 'your_existing_access_token',
|
|
155
|
+
refreshToken: 'your_existing_refresh_token',
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Webhook Configuration in GoHighLevel
|
|
160
|
+
|
|
161
|
+
### Step 1: Get Your Webhook URL
|
|
162
|
+
|
|
163
|
+
Once your bot is running, your webhook URL will be:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
https://your-domain.com/webhook
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For local development with ngrok:
|
|
170
|
+
```bash
|
|
171
|
+
ngrok http 3000
|
|
172
|
+
# Use the ngrok URL: https://abc123.ngrok.io/webhook
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Step 2: Configure Webhook in GHL
|
|
176
|
+
|
|
177
|
+
1. Go to **Settings > Integrations > Webhooks** in your GHL sub-account
|
|
178
|
+
2. Click **"Add Webhook"**
|
|
179
|
+
3. Configure:
|
|
180
|
+
- **Webhook URL**: `https://your-domain.com/webhook`
|
|
181
|
+
- **Events**: See table below
|
|
182
|
+
4. (Optional) Set a **Webhook Secret** for HMAC verification
|
|
183
|
+
|
|
184
|
+
### Webhook Events
|
|
185
|
+
|
|
186
|
+
#### Required Event
|
|
187
|
+
|
|
188
|
+
| Event | Scope Required | Description |
|
|
189
|
+
|-------|----------------|-------------|
|
|
190
|
+
| **InboundMessage** | `conversations/message.readonly` | **REQUIRED** - Receives incoming messages from users |
|
|
191
|
+
|
|
192
|
+
#### Optional Events (Recommended)
|
|
193
|
+
|
|
194
|
+
| Event | Scope Required | Description |
|
|
195
|
+
|-------|----------------|-------------|
|
|
196
|
+
| OutboundMessage | `conversations/message.readonly` | Track outbound messages |
|
|
197
|
+
| ConversationUnreadUpdate | `conversations.readonly` | Know when there are unread conversations |
|
|
198
|
+
|
|
199
|
+
### Step 3: Verify Webhook is Working
|
|
200
|
+
|
|
201
|
+
Send a test message to your GHL number/channel. You should see the bot respond.
|
|
202
|
+
|
|
203
|
+
## OAuth2 Authorization Flow
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
+--------+ +---------------+
|
|
207
|
+
| |---(1) Authorization Request-->| GHL OAuth |
|
|
208
|
+
| User | | Server |
|
|
209
|
+
| |<--(2) Authorization Code------| |
|
|
210
|
+
+--------+ +---------------+
|
|
211
|
+
| |
|
|
212
|
+
| |
|
|
213
|
+
v v
|
|
214
|
+
+--------+ +---------------+
|
|
215
|
+
| |---(3) Exchange Code---------->| GHL OAuth |
|
|
216
|
+
| Bot | | Server |
|
|
217
|
+
| Server |<--(4) Access + Refresh Token--| |
|
|
218
|
+
+--------+ +---------------+
|
|
219
|
+
|
|
|
220
|
+
| (5) Auto-refresh before expiry
|
|
221
|
+
v
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### First-Time Authorization
|
|
225
|
+
|
|
226
|
+
1. Start your bot server
|
|
227
|
+
2. If no valid token exists, the provider will emit a `notice` event with the authorization URL
|
|
228
|
+
3. Visit the URL and authorize the app
|
|
229
|
+
4. GHL redirects to `/oauth/callback` with an authorization code
|
|
230
|
+
5. The provider exchanges the code for access/refresh tokens
|
|
231
|
+
6. Tokens are automatically refreshed 5 minutes before expiry
|
|
232
|
+
|
|
233
|
+
## Configuration Options
|
|
234
|
+
|
|
235
|
+
| Option | Type | Required | Default | Description |
|
|
236
|
+
|--------|------|----------|---------|-------------|
|
|
237
|
+
| `clientId` | string | Yes | - | OAuth2 Client ID from GHL Marketplace |
|
|
238
|
+
| `clientSecret` | string | Yes | - | OAuth2 Client Secret from GHL Marketplace |
|
|
239
|
+
| `locationId` | string | Yes | - | GHL Location/Sub-account ID |
|
|
240
|
+
| `channelType` | string | No | `'SMS'` | Channel type: SMS, WhatsApp, Email, Live_Chat, Facebook, Instagram, Custom |
|
|
241
|
+
| `redirectUri` | string | No | - | OAuth2 callback URL |
|
|
242
|
+
| `webhookSecret` | string | No | - | Secret for HMAC SHA256 webhook verification |
|
|
243
|
+
| `accessToken` | string | No | - | Pre-existing access token |
|
|
244
|
+
| `refreshToken` | string | No | - | Pre-existing refresh token |
|
|
245
|
+
| `conversationProviderId` | string | No | - | Custom conversation provider ID |
|
|
246
|
+
| `versionId` | string | No | - | App version ID for OAuth authorization URL |
|
|
247
|
+
| `port` | number | No | `3000` | HTTP server port |
|
|
248
|
+
| `apiVersion` | string | No | `'2021-07-28'` | GHL API version |
|
|
249
|
+
|
|
250
|
+
## Exposed Endpoints
|
|
251
|
+
|
|
252
|
+
| Method | Path | Description |
|
|
253
|
+
|--------|------|-------------|
|
|
254
|
+
| GET | `/` | Health check - returns "running ok" |
|
|
255
|
+
| GET | `/oauth/callback` | OAuth2 authorization callback |
|
|
256
|
+
| POST | `/webhook` | Incoming messages from GHL |
|
|
257
|
+
|
|
258
|
+
## Handling Files/Media
|
|
259
|
+
|
|
260
|
+
### Saving Received Files
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const mediaFlow = addKeyword(['_event_media_'])
|
|
264
|
+
.addAction(async (ctx, { provider }) => {
|
|
265
|
+
// Save the received file
|
|
266
|
+
const filePath = await provider.saveFile(ctx, {
|
|
267
|
+
path: './downloads'
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
console.log('File saved to:', filePath)
|
|
271
|
+
})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Sending Media
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const sendMediaFlow = addKeyword('send photo')
|
|
278
|
+
.addAnswer('Here is your image!', {
|
|
279
|
+
media: 'https://example.com/image.jpg'
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Sending Local Files
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
const sendLocalFile = addKeyword('send document')
|
|
287
|
+
.addAnswer('Here is the document!', {
|
|
288
|
+
media: './files/document.pdf'
|
|
289
|
+
})
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Provider Events
|
|
293
|
+
|
|
294
|
+
Listen to provider events for monitoring and debugging:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
provider.on('ready', () => {
|
|
298
|
+
console.log('Provider is ready and connected!')
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
provider.on('message', (ctx) => {
|
|
302
|
+
console.log('Message received:', ctx.body, 'from:', ctx.from)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
provider.on('auth_failure', (payload) => {
|
|
306
|
+
console.error('Authentication failed:', payload)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
provider.on('notice', ({ title, instructions }) => {
|
|
310
|
+
console.log(`[${title}]`, instructions.join('\n'))
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
provider.on('tokens_updated', (tokens) => {
|
|
314
|
+
// Optionally persist tokens for later use
|
|
315
|
+
console.log('Tokens updated, new expiry:', tokens.expires_in)
|
|
316
|
+
})
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Sending Messages Programmatically
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// Send text message
|
|
323
|
+
await provider.sendMessage('contact_phone_number', 'Hello!')
|
|
324
|
+
|
|
325
|
+
// Send with buttons (rendered as numbered list)
|
|
326
|
+
await provider.sendMessage('contact_phone_number', 'Choose an option:', {
|
|
327
|
+
buttons: [
|
|
328
|
+
{ body: 'Option 1' },
|
|
329
|
+
{ body: 'Option 2' },
|
|
330
|
+
{ body: 'Option 3' },
|
|
331
|
+
]
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
// Send media
|
|
335
|
+
await provider.sendMessage('contact_phone_number', 'Check this out!', {
|
|
336
|
+
media: 'https://example.com/image.png'
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Troubleshooting
|
|
341
|
+
|
|
342
|
+
### Error: "clientId and clientSecret are required"
|
|
343
|
+
|
|
344
|
+
**Cause**: Missing OAuth2 credentials.
|
|
345
|
+
|
|
346
|
+
**Solution**: Ensure you've set `GHL_CLIENT_ID` and `GHL_CLIENT_SECRET` environment variables.
|
|
347
|
+
|
|
348
|
+
### Error: "locationId is required"
|
|
349
|
+
|
|
350
|
+
**Cause**: Missing GHL sub-account location ID.
|
|
351
|
+
|
|
352
|
+
**Solution**: Set the `GHL_LOCATION_ID` environment variable with your sub-account's location ID.
|
|
353
|
+
|
|
354
|
+
### Error: "Contact not found for phone: XXX"
|
|
355
|
+
|
|
356
|
+
**Cause**: The phone number doesn't exist as a contact in GHL.
|
|
357
|
+
|
|
358
|
+
**Solution**:
|
|
359
|
+
1. Ensure the contact exists in your GHL sub-account
|
|
360
|
+
2. Check the phone number format (should be digits only, e.g., `1234567890`)
|
|
361
|
+
|
|
362
|
+
### Error: "Invalid webhook signature"
|
|
363
|
+
|
|
364
|
+
**Cause**: Webhook signature verification failed.
|
|
365
|
+
|
|
366
|
+
**Solution**:
|
|
367
|
+
1. Ensure `webhookSecret` matches the secret configured in GHL
|
|
368
|
+
2. Check that the webhook is sending the signature in the correct header
|
|
369
|
+
|
|
370
|
+
### Error: "Missing webhook signature"
|
|
371
|
+
|
|
372
|
+
**Cause**: Webhook secret is configured but GHL isn't sending a signature.
|
|
373
|
+
|
|
374
|
+
**Solution**:
|
|
375
|
+
1. Configure the webhook secret in GHL's webhook settings
|
|
376
|
+
2. Or remove `webhookSecret` from your provider config if you don't need verification
|
|
377
|
+
|
|
378
|
+
### Authorization URL Not Working
|
|
379
|
+
|
|
380
|
+
**Cause**: Incorrect redirect URI configuration.
|
|
381
|
+
|
|
382
|
+
**Solution**:
|
|
383
|
+
1. Ensure `redirectUri` matches exactly what's configured in GHL Marketplace
|
|
384
|
+
2. For local development, use `http://localhost:PORT/oauth/callback`
|
|
385
|
+
|
|
386
|
+
## Complete Example
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { createBot, createProvider, createFlow, addKeyword, EVENTS } from '@builderbot/bot'
|
|
390
|
+
import { GoHighLevelProvider } from '@builderbot/provider-gohighlevel'
|
|
391
|
+
import { MemoryDB } from '@builderbot/bot'
|
|
392
|
+
|
|
393
|
+
// Environment variables
|
|
394
|
+
const config = {
|
|
395
|
+
clientId: process.env.GHL_CLIENT_ID,
|
|
396
|
+
clientSecret: process.env.GHL_CLIENT_SECRET,
|
|
397
|
+
locationId: process.env.GHL_LOCATION_ID,
|
|
398
|
+
channelType: 'WhatsApp' as const,
|
|
399
|
+
redirectUri: process.env.GHL_REDIRECT_URI,
|
|
400
|
+
webhookSecret: process.env.GHL_WEBHOOK_SECRET,
|
|
401
|
+
port: 3000,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Create provider
|
|
405
|
+
const provider = createProvider(GoHighLevelProvider, config)
|
|
406
|
+
|
|
407
|
+
// Flows
|
|
408
|
+
const welcomeFlow = addKeyword(['hello', 'hi', 'hola'])
|
|
409
|
+
.addAnswer('Welcome to our service!')
|
|
410
|
+
.addAnswer('How can I help you today?', {
|
|
411
|
+
buttons: [
|
|
412
|
+
{ body: 'Sales' },
|
|
413
|
+
{ body: 'Support' },
|
|
414
|
+
{ body: 'Information' },
|
|
415
|
+
]
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
const salesFlow = addKeyword(['sales', '1'])
|
|
419
|
+
.addAnswer('Our sales team will contact you shortly!')
|
|
420
|
+
|
|
421
|
+
const supportFlow = addKeyword(['support', '2'])
|
|
422
|
+
.addAnswer('Please describe your issue and we will help you.')
|
|
423
|
+
|
|
424
|
+
const mediaFlow = addKeyword([EVENTS.MEDIA])
|
|
425
|
+
.addAction(async (ctx, { provider, flowDynamic }) => {
|
|
426
|
+
const filePath = await provider.saveFile(ctx, { path: './uploads' })
|
|
427
|
+
await flowDynamic(`File received and saved: ${filePath}`)
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
// Main
|
|
431
|
+
const main = async () => {
|
|
432
|
+
const bot = await createBot({
|
|
433
|
+
flow: createFlow([welcomeFlow, salesFlow, supportFlow, mediaFlow]),
|
|
434
|
+
provider,
|
|
435
|
+
database: new MemoryDB(),
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
// Event listeners
|
|
439
|
+
provider.on('ready', () => {
|
|
440
|
+
console.log('GoHighLevel bot is ready!')
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
provider.on('notice', ({ title, instructions }) => {
|
|
444
|
+
console.log(`[${title}]`)
|
|
445
|
+
instructions.forEach(i => console.log(` - ${i}`))
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
console.log(`Server running on port ${config.port}`)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
main().catch(console.error)
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Useful Links
|
|
455
|
+
|
|
456
|
+
- [GoHighLevel API Documentation](https://highlevel.stoplight.io/docs/integrations)
|
|
457
|
+
- [GHL Marketplace](https://marketplace.gohighlevel.com)
|
|
458
|
+
- [BuilderBot Documentation](https://builderbot.app)
|
|
459
|
+
- [BuilderBot GitHub](https://github.com/codigoencasa/builderbot)
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
ISC
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import type polka from 'polka';
|
|
3
|
+
import type Queue from 'queue-promise';
|
|
4
|
+
import type { TokenManager } from '../utils/tokenManager';
|
|
5
|
+
import type { GHLMessage } from '~/types';
|
|
6
|
+
/**
|
|
7
|
+
* Core vendor class handling OAuth callbacks and webhook processing
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare class GoHighLevelCoreVendor extends EventEmitter {
|
|
11
|
+
queue: Queue;
|
|
12
|
+
tokenManager: TokenManager;
|
|
13
|
+
webhookSecret?: string;
|
|
14
|
+
constructor(_queue: Queue, _tokenManager: TokenManager, webhookSecret?: string);
|
|
15
|
+
indexHome: polka.Middleware;
|
|
16
|
+
oauthCallback: polka.Middleware;
|
|
17
|
+
incomingMsg: polka.Middleware;
|
|
18
|
+
processMessage: (message: GHLMessage) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/gohighlevel/core.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,KAAK,MAAM,eAAe,CAAA;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAGzD,OAAO,KAAK,EAA2C,UAAU,EAAE,MAAM,SAAS,CAAA;AAElF;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACnD,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,YAAY,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;gBAEV,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,MAAM;IAOvE,SAAS,EAAE,KAAK,CAAC,UAAU,CAEjC;IAEM,aAAa,EAAE,KAAK,CAAC,UAAU,CA+CrC;IAEM,WAAW,EAAE,KAAK,CAAC,UAAU,CAoEnC;IAEM,cAAc,GAAI,SAAS,UAAU,KAAG,OAAO,CAAC,IAAI,CAAC,CAS3D;CACJ"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ProviderClass } from '@builderbot/bot';
|
|
2
|
+
import type { Vendor } from '@builderbot/bot/dist/provider/interface/provider';
|
|
3
|
+
import type { BotContext, Button, SendOptions } from '@builderbot/bot/dist/types';
|
|
4
|
+
import Queue from 'queue-promise';
|
|
5
|
+
import { ChannelLister } from '../utils/channelLister';
|
|
6
|
+
import { ContactResolver } from '../utils/contactResolver';
|
|
7
|
+
import { TokenManager } from '../utils/tokenManager';
|
|
8
|
+
import type { GoHighLevelInterface } from '~/interface/gohighlevel';
|
|
9
|
+
import type { GHLGlobalVendorArgs, GHLMessage, GHLSendMessageBody, SaveFileOptions } from '~/types';
|
|
10
|
+
/**
|
|
11
|
+
* GoHighLevel Provider for BuilderBot
|
|
12
|
+
* @description Integrates with GoHighLevel CRM to send/receive messages via SMS, WhatsApp, Email, etc.
|
|
13
|
+
* @see https://builderbot.app/en/providers/gohighlevel
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const provider = createProvider(GoHighLevelProvider, {
|
|
17
|
+
* clientId: 'YOUR_CLIENT_ID',
|
|
18
|
+
* clientSecret: 'YOUR_CLIENT_SECRET',
|
|
19
|
+
* locationId: 'YOUR_LOCATION_ID',
|
|
20
|
+
* channelType: 'WhatsApp',
|
|
21
|
+
* accessToken: 'OPTIONAL_TOKEN',
|
|
22
|
+
* refreshToken: 'OPTIONAL_REFRESH_TOKEN',
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class GoHighLevelProvider extends ProviderClass<GoHighLevelInterface> implements GoHighLevelInterface {
|
|
27
|
+
vendor: Vendor<any>;
|
|
28
|
+
queue: Queue;
|
|
29
|
+
tokenManager: TokenManager;
|
|
30
|
+
contactResolver: ContactResolver;
|
|
31
|
+
channelLister: ChannelLister;
|
|
32
|
+
private isReady;
|
|
33
|
+
/** Cache to store the channel type per contactId for auto-reply on same channel */
|
|
34
|
+
private contactChannelCache;
|
|
35
|
+
globalVendorArgs: GHLGlobalVendorArgs;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new GoHighLevel provider instance
|
|
38
|
+
* @param args - Configuration options for the provider
|
|
39
|
+
* @throws Error if clientId, clientSecret, or locationId are missing
|
|
40
|
+
*/
|
|
41
|
+
constructor(args: GHLGlobalVendorArgs);
|
|
42
|
+
protected beforeHttpServerInit(): void;
|
|
43
|
+
protected afterHttpServerInit(): Promise<void>;
|
|
44
|
+
private showAuthorizationUrl;
|
|
45
|
+
private emitTokensNotice;
|
|
46
|
+
private emitReadyNotice;
|
|
47
|
+
private listAvailableChannels;
|
|
48
|
+
protected initVendor(): Promise<any>;
|
|
49
|
+
/** Stops the provider and cleans up resources */
|
|
50
|
+
stop(): Promise<void>;
|
|
51
|
+
/** Returns the OAuth authorization URL for GoHighLevel */
|
|
52
|
+
getAuthorizationUrl(): string;
|
|
53
|
+
/**
|
|
54
|
+
* Downloads and saves a file from an incoming message
|
|
55
|
+
* @param ctx - Message context containing file URL
|
|
56
|
+
* @param options - Save options (path)
|
|
57
|
+
* @returns Path to saved file or 'ERROR'
|
|
58
|
+
*/
|
|
59
|
+
saveFile: (ctx: Partial<GHLMessage & BotContext>, options?: SaveFileOptions) => Promise<string>;
|
|
60
|
+
busEvents: () => {
|
|
61
|
+
event: string;
|
|
62
|
+
func: (payload: any) => void;
|
|
63
|
+
}[];
|
|
64
|
+
/**
|
|
65
|
+
* Resolves a phone number to a GHL contact ID
|
|
66
|
+
* @param phone - Phone number to resolve
|
|
67
|
+
* @returns Contact ID or null if not found
|
|
68
|
+
*/
|
|
69
|
+
resolveContactId: (phone: string) => Promise<string | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a string looks like a GHL contactId rather than a phone number
|
|
72
|
+
* ContactIds are alphanumeric strings, while phones are mostly digits
|
|
73
|
+
* @param value - String to check
|
|
74
|
+
* @returns true if the value appears to be a contactId
|
|
75
|
+
*/
|
|
76
|
+
private isContactId;
|
|
77
|
+
/**
|
|
78
|
+
* Gets the channel type to use for a contact
|
|
79
|
+
* Returns the cached channel (from last incoming message) or falls back to global config
|
|
80
|
+
* @param contactId - The contact ID to look up
|
|
81
|
+
* @returns The channel type to use for sending messages
|
|
82
|
+
*/
|
|
83
|
+
private getChannelForContact;
|
|
84
|
+
/**
|
|
85
|
+
* Sends a text message to a contact
|
|
86
|
+
* @param to - Recipient phone number or contactId
|
|
87
|
+
* @param message - Text message content
|
|
88
|
+
*/
|
|
89
|
+
sendText: (to: string, message: string) => Promise<any>;
|
|
90
|
+
/**
|
|
91
|
+
* Sends a media message (image, audio, video, document)
|
|
92
|
+
* @param to - Recipient phone number or contactId
|
|
93
|
+
* @param text - Optional caption text
|
|
94
|
+
* @param mediaInput - URL or path to media file
|
|
95
|
+
*/
|
|
96
|
+
sendMedia: (to: string, text: string, mediaInput: string) => Promise<any>;
|
|
97
|
+
/**
|
|
98
|
+
* Sends a message with buttons (rendered as numbered list)
|
|
99
|
+
* @param to - Recipient phone number
|
|
100
|
+
* @param buttons - Array of button objects
|
|
101
|
+
* @param text - Message text
|
|
102
|
+
*/
|
|
103
|
+
sendButtons: (to: string, buttons: Button[], text: string) => Promise<any>;
|
|
104
|
+
/**
|
|
105
|
+
* Sends a message with optional media or buttons
|
|
106
|
+
* @param to - Recipient phone number or contactId
|
|
107
|
+
* @param message - Message text
|
|
108
|
+
* @param options - Optional send options (media, buttons)
|
|
109
|
+
*/
|
|
110
|
+
sendMessage: (to: string, message: string, options?: SendOptions) => Promise<any>;
|
|
111
|
+
sendMessageGHL: (body: GHLSendMessageBody) => Promise<any>;
|
|
112
|
+
sendMessageToApi: (body: GHLSendMessageBody) => Promise<any>;
|
|
113
|
+
}
|
|
114
|
+
export { GoHighLevelProvider };
|
|
115
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/gohighlevel/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAS,MAAM,iBAAiB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kDAAkD,CAAA;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAMjF,OAAO,KAAK,MAAM,eAAe,CAAA;AAGjC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AACnE,OAAO,KAAK,EAAkB,mBAAmB,EAAE,UAAU,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAInH;;;;;;;;;;;;;;;GAeG;AACH,cAAM,mBAAoB,SAAQ,aAAa,CAAC,oBAAoB,CAAE,YAAW,oBAAoB;IAC1F,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;IACnB,KAAK,EAAE,KAAK,CAAc;IAC1B,YAAY,EAAE,YAAY,CAAA;IAC1B,eAAe,EAAE,eAAe,CAAA;IAChC,aAAa,EAAE,aAAa,CAAA;IACnC,OAAO,CAAC,OAAO,CAAiB;IAChC,mFAAmF;IACnF,OAAO,CAAC,mBAAmB,CAAyC;IAE7D,gBAAgB,EAAE,mBAAmB,CAS3C;IAED;;;;OAIG;gBACS,IAAI,EAAE,mBAAmB;IA2CrC,SAAS,CAAC,oBAAoB,IAAI,IAAI;cAItB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAqDpD,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,gBAAgB;YAYV,eAAe;YA0Bf,qBAAqB;IA6BnC,SAAS,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;IA6BpC,iDAAiD;IACpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,0DAA0D;IACnD,mBAAmB,IAAI,MAAM;IAapC;;;;;OAKG;IACH,QAAQ,GAAU,KAAK,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,EAAE,UAAS,eAAoB,KAAG,OAAO,CAAC,MAAM,CAAC,CAiBvG;IAED,SAAS;;wBAGe,GAAG;QAwC1B;IAED;;;;OAIG;IACH,gBAAgB,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG/D;IAED;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAOnB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;;;OAIG;IACH,QAAQ,GAAU,IAAI,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,GAAG,CAAC,CAsB3D;IAED;;;;;OAKG;IACH,SAAS,GAAU,IAAI,MAAM,EAAE,MAAM,MAAW,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,GAAG,CAAC,CA2BlF;IAED;;;;;OAKG;IACH,WAAW,GAAU,IAAI,MAAM,EAAE,SAAS,MAAM,EAAO,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,GAAG,CAAC,CAInF;IAED;;;;;OAKG;IACH,WAAW,GAAU,IAAI,MAAM,EAAE,SAAS,MAAM,EAAE,UAAU,WAAW,KAAG,OAAO,CAAC,GAAG,CAAC,CASrF;IAED,cAAc,GAAI,MAAM,kBAAkB,KAAG,OAAO,CAAC,GAAG,CAAC,CAWxD;IAED,gBAAgB,GAAU,MAAM,kBAAkB,KAAG,OAAO,CAAC,GAAG,CAAC,CAUhE;CACJ;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAA"}
|