@green-api/greenapi-integration 0.1.0
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 +771 -0
- package/README.ru.md +774 -0
- package/dist/core/base-adapter.d.ts +23 -0
- package/dist/core/base-adapter.js +135 -0
- package/dist/core/errors.d.ts +15 -0
- package/dist/core/errors.js +31 -0
- package/dist/core/green-api.client.d.ts +28 -0
- package/dist/core/green-api.client.js +150 -0
- package/dist/core/guard.d.ts +7 -0
- package/dist/core/guard.js +28 -0
- package/dist/core/message-transformer.d.ts +5 -0
- package/dist/core/message-transformer.js +6 -0
- package/dist/core/storage-provider.d.ts +9 -0
- package/dist/core/storage-provider.js +6 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +29 -0
- package/dist/types/types.d.ts +178 -0
- package/dist/types/types.js +2 -0
- package/dist/utils/helpers.d.ts +2 -0
- package/dist/utils/helpers.js +45 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
# Universal Integration Platform for GREEN-API
|
|
2
|
+
|
|
3
|
+
## Support links
|
|
4
|
+
|
|
5
|
+
[](mailto:support@greenapi.com)
|
|
6
|
+
[](https://t.me/greenapi_support_eng_bot)
|
|
7
|
+
[](https://wa.me/77273122366)
|
|
8
|
+
|
|
9
|
+
## Guides & News
|
|
10
|
+
|
|
11
|
+
[](https://www.youtube.com/@greenapi-en)
|
|
12
|
+
[](https://t.me/green_api)
|
|
13
|
+
[](https://whatsapp.com/channel/0029VaLj6J4LNSa2B5Jx6s3h)
|
|
14
|
+
|
|
15
|
+
- [Документация на русском языке](./README.ru.md)
|
|
16
|
+
|
|
17
|
+
A flexible integration platform designed to simplify the process of connecting GREEN-API's WhatsApp gateway with various
|
|
18
|
+
third-party services.
|
|
19
|
+
|
|
20
|
+
## Table of Contents
|
|
21
|
+
|
|
22
|
+
- [Installation](#installation)
|
|
23
|
+
- [Core Components](#core-components)
|
|
24
|
+
- [Developer Guide](#developer-guide)
|
|
25
|
+
- [Working Example](#working-example)
|
|
26
|
+
- [Real-World Examples](#real-world-examples)
|
|
27
|
+
- [Best Practices](#best-practices)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @green-api/greenapi-integration
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Core Components
|
|
36
|
+
|
|
37
|
+
### 1. BaseAdapter
|
|
38
|
+
|
|
39
|
+
The foundation of your integration. Handles message & instance management, and platform-specific logic.
|
|
40
|
+
The `BaseAdapter` internally uses `GreenApiClient` for all common operations, so in most cases, you don't need to use
|
|
41
|
+
GreenApiClient methods directly.
|
|
42
|
+
|
|
43
|
+
**When to use `BaseAdapter` vs `GreenApiClient`**:
|
|
44
|
+
|
|
45
|
+
✅ Use `BaseAdapter` methods for all standard operations (sending messages, handling webhooks, managing instances)
|
|
46
|
+
|
|
47
|
+
⚠️ Use `GreenApiClient` directly only for specialized operations not covered by BaseAdapter (like setProfilePicture,
|
|
48
|
+
getAuthorizationCode)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage> {
|
|
52
|
+
public constructor(
|
|
53
|
+
transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>,
|
|
54
|
+
storage: StorageProvider
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
public abstract createPlatformClient(params: any): Promise<any>;
|
|
58
|
+
|
|
59
|
+
public abstract sendToPlatform(message: TPlatformMessage, instance: TInstance): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example of proper usage:**
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// ✅ CORRECT: Using BaseAdapter for standard operations
|
|
67
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
68
|
+
await adapter.handlePlatformWebhook(webhook, instanceId);
|
|
69
|
+
await adapter.createInstance(instance, settings, userCred);
|
|
70
|
+
await adapter.sendMessage(transformedWebhook);
|
|
71
|
+
|
|
72
|
+
// ⚠️ ONLY IF NEEDED: Direct GreenApiClient usage for specialized operations
|
|
73
|
+
const client = new GreenApiClient(instance);
|
|
74
|
+
await client.setProfilePicture(fileBlob);
|
|
75
|
+
await client.getAuthorizationCode(phoneNumber);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. MessageTransformer
|
|
79
|
+
|
|
80
|
+
Handles message format conversion between GREEN-API and your platform.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
abstract class MessageTransformer<TPlatformWebhook, TPlatformMessage> {
|
|
84
|
+
abstract toPlatformMessage(webhook: IncomingGreenApiWebhook): TPlatformMessage;
|
|
85
|
+
|
|
86
|
+
abstract toGreenApiMessage(message: TPlatformWebhook): Message;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. StorageProvider
|
|
91
|
+
|
|
92
|
+
Interface for data persistence operations.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
abstract class StorageProvider<TUser extends BaseUser = BaseUser, TInstance extends BaseInstance = Instance> {
|
|
96
|
+
abstract createInstance(instance: BaseInstance, userId: bigint | number, settings?: Settings): Promise<TInstance>;
|
|
97
|
+
|
|
98
|
+
abstract getInstance(idInstance: number | bigint): Promise<TInstance | null>;
|
|
99
|
+
|
|
100
|
+
abstract removeInstance(instanceId: number | bigint): Promise<TInstance>;
|
|
101
|
+
|
|
102
|
+
abstract createUser(data: any): Promise<TUser>;
|
|
103
|
+
|
|
104
|
+
abstract findUser(identifier: string): Promise<TUser | null>;
|
|
105
|
+
|
|
106
|
+
abstract updateUser(identifier: string, data: any): Promise<TUser>;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 4. BaseGreenApiAuthGuard
|
|
111
|
+
|
|
112
|
+
Handles webhook authentication for incoming GREEN-API requests.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
abstract class BaseGreenApiAuthGuard<T extends BaseRequest = BaseRequest> {
|
|
116
|
+
constructor(protected storage: StorageProvider);
|
|
117
|
+
|
|
118
|
+
// Validates incoming webhook requests
|
|
119
|
+
async validateRequest(request: T): Promise<boolean>;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Example implementation of `BaseGreenApiAuthGuard`:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
class YourAuthGuard extends BaseGreenApiAuthGuard<YourRequest> {
|
|
127
|
+
constructor(storage: StorageProvider) {
|
|
128
|
+
super(storage);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Using with Express
|
|
133
|
+
app.post('/webhook', async (req, res) => {
|
|
134
|
+
const guard = new YourAuthGuard(storage);
|
|
135
|
+
try {
|
|
136
|
+
await guard.validateRequest(req);
|
|
137
|
+
// Process webhook
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof AuthenticationError) {
|
|
140
|
+
res.status(401).json({error: error.message});
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
res.status(500).json({error: 'Internal server error'});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 5. GreenApiClient
|
|
149
|
+
|
|
150
|
+
Direct interface to GREEN-API endpoints. While most operations should be handled through BaseAdapter, GreenApiClient can
|
|
151
|
+
be used directly for specialized operations.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const client = new GreenApiClient({
|
|
155
|
+
idInstance: 'your_instance_id',
|
|
156
|
+
apiTokenInstance: 'your_token'
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Examples of specialized operations:
|
|
160
|
+
await client.setProfilePicture(fileBlob);
|
|
161
|
+
await client.getAuthorizationCode(phoneNumber);
|
|
162
|
+
await client.getQR();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Developer Guide
|
|
166
|
+
|
|
167
|
+
### Project Structure
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
your-integration/
|
|
171
|
+
├── src/
|
|
172
|
+
│ ├── core/
|
|
173
|
+
│ │ ├── adapter.ts # Your platform adapter
|
|
174
|
+
│ │ ├── transformer.ts # Message transformer
|
|
175
|
+
│ │ ├── storage.ts # Data storage implementation
|
|
176
|
+
│ │ └── router.ts # Webhook endpoints
|
|
177
|
+
│ ├── types/
|
|
178
|
+
│ │ └── types.ts # Platform-specific types
|
|
179
|
+
│ └── main.ts # Main exports
|
|
180
|
+
├── package.json
|
|
181
|
+
└── tsconfig.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Implementation Steps
|
|
185
|
+
|
|
186
|
+
1. **Define Platform Types**
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// types/types.ts
|
|
190
|
+
export interface YourPlatformWebhook {
|
|
191
|
+
id: string;
|
|
192
|
+
from: string;
|
|
193
|
+
message: string;
|
|
194
|
+
timestamp: number;
|
|
195
|
+
// Add other platform-specific fields
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface YourPlatformMessage {
|
|
199
|
+
recipient: string;
|
|
200
|
+
content: string;
|
|
201
|
+
// Add other platform-specific fields
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
2. **Create Message Transformer**
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// core/transformer.ts
|
|
209
|
+
import { MessageTransformer, Message, IncomingGreenApiWebhook } from '@green-api/greenapi-integration';
|
|
210
|
+
import { YourPlatformWebhook, YourPlatformMessage } from '../types/types';
|
|
211
|
+
|
|
212
|
+
export class YourTransformer extends MessageTransformer<YourPlatformWebhook, YourPlatformMessage> {
|
|
213
|
+
toPlatformMessage(webhook: IncomingGreenApiWebhook): YourPlatformMessage {
|
|
214
|
+
// Transform GREEN-API webhook to your platform format
|
|
215
|
+
return {
|
|
216
|
+
recipient: webhook.senderData.sender,
|
|
217
|
+
content: webhook.messageData.textMessageData?.textMessage || '',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
toGreenApiMessage(message: YourPlatformWebhook): Message {
|
|
222
|
+
// Transform your platform webhook to GREEN-API format
|
|
223
|
+
return {
|
|
224
|
+
type: 'text',
|
|
225
|
+
chatId: message.from,
|
|
226
|
+
message: message.message,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
3. **Implement Storage**
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// core/storage.ts
|
|
236
|
+
import { StorageProvider, BaseUser, BaseInstance, Settings } from '@green-api/greenapi-integration';
|
|
237
|
+
import { PrismaClient } from '@prisma/client'; // Or your database client
|
|
238
|
+
|
|
239
|
+
export class YourStorage extends StorageProvider {
|
|
240
|
+
private db: PrismaClient;
|
|
241
|
+
|
|
242
|
+
constructor() {
|
|
243
|
+
this.db = new PrismaClient();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async createInstance(instance: BaseInstance, userId: bigint, settings?: Settings) {
|
|
247
|
+
return this.db.instance.create({
|
|
248
|
+
data: {
|
|
249
|
+
idInstance: instance.idInstance,
|
|
250
|
+
apiTokenInstance: instance.apiTokenInstance,
|
|
251
|
+
userId,
|
|
252
|
+
settings: settings || {},
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Implement other required methods
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
4. **Create Platform Adapter**
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// core/adapter.ts
|
|
265
|
+
import { BaseAdapter, BaseInstance } from '@green-api/greenapi-integration';
|
|
266
|
+
import { YourPlatformClient } from 'your-platform-sdk';
|
|
267
|
+
import { YourPlatformWebhook, YourPlatformMessage } from '../types/types';
|
|
268
|
+
|
|
269
|
+
export class YourAdapter extends BaseAdapter<YourPlatformWebhook, YourPlatformMessage> {
|
|
270
|
+
async createPlatformClient(config: { apiKey: string, apiUrl: string }) {
|
|
271
|
+
return new YourPlatformClient({
|
|
272
|
+
baseUrl: config.apiUrl,
|
|
273
|
+
apiKey: config.apiKey,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async sendToPlatform(message: YourPlatformMessage, instance: BaseInstance) {
|
|
278
|
+
const client = await this.createPlatformClient(instance.config);
|
|
279
|
+
await client.sendMessage(message);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
5. **Implement Webhook Controller**
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// core/webhook.ts
|
|
288
|
+
import express from 'express';
|
|
289
|
+
import { YourAdapter } from '../core/adapter';
|
|
290
|
+
import { YourTransformer } from '../core/transformer';
|
|
291
|
+
import { YourStorage } from '../core/storage';
|
|
292
|
+
|
|
293
|
+
const router = express.Router();
|
|
294
|
+
const storage = new YourStorage();
|
|
295
|
+
const transformer = new YourTransformer();
|
|
296
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
297
|
+
|
|
298
|
+
class WebhookGuard extends BaseGreenApiAuthGuard {
|
|
299
|
+
constructor(storage: StorageProvider) {
|
|
300
|
+
super(storage);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const guard = new WebhookGuard(storage);
|
|
305
|
+
|
|
306
|
+
// Webhook endpoints
|
|
307
|
+
router.post('/green-api', async (req, res) => {
|
|
308
|
+
try {
|
|
309
|
+
// Validate webhook first
|
|
310
|
+
await guard.validateRequest(req);
|
|
311
|
+
|
|
312
|
+
// Process webhook if validation passed
|
|
313
|
+
// As the second parameter, specfify the types of webhooks to be processed (otherwise skipped)
|
|
314
|
+
await adapter.handleGreenApiWebhook(req.body, ['incomingMessageReceived']);
|
|
315
|
+
res.status(200).json({status: 'ok'});
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error instanceof AuthenticationError) {
|
|
318
|
+
res.status(401).json({error: error.message});
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
console.error('Webhook error:', error);
|
|
322
|
+
res.status(500).json({error: 'Internal server error'});
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
router.post('/platform', async (req, res) => {
|
|
327
|
+
try {
|
|
328
|
+
const instanceId = req.query.instanceId;
|
|
329
|
+
await adapter.handlePlatformWebhook(req.body, instanceId);
|
|
330
|
+
res.status(200).json({status: 'ok'});
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error('Platform webhook error:', error);
|
|
333
|
+
res.status(500).json({error: 'Internal server error'});
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
router.post('/instance', async (req, res) => {
|
|
338
|
+
try {
|
|
339
|
+
const {idInstance, apiTokenInstance, userEmail} = req.body;
|
|
340
|
+
|
|
341
|
+
if (!idInstance || !apiTokenInstance || !userEmail) {
|
|
342
|
+
throw new BadRequestError('Required fields missing');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const instance = await adapter.createInstance({
|
|
346
|
+
idInstance: Number(idInstance),
|
|
347
|
+
apiTokenInstance
|
|
348
|
+
}, {
|
|
349
|
+
webhookUrl: `${process.env.APP_URL}/webhook/green-api`,
|
|
350
|
+
webhookUrlToken: `token_${Date.now()}`, // In production, use a secure token generator
|
|
351
|
+
incomingWebhook: 'yes'
|
|
352
|
+
}, userEmail);
|
|
353
|
+
|
|
354
|
+
res.status(200).json({
|
|
355
|
+
status: 'ok',
|
|
356
|
+
data: instance,
|
|
357
|
+
message: 'Instance created successfully. Please wait 2 minutes for settings to apply.'
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('Instance creation error:', error);
|
|
362
|
+
res.status(500).json({error: 'Failed to create instance'});
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
export default router;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
6. **Create Application Entry Point**
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
// main.ts
|
|
373
|
+
import express from 'express';
|
|
374
|
+
import bodyParser from 'body-parser';
|
|
375
|
+
import dotenv from 'dotenv';
|
|
376
|
+
import webhookRouter from './controllers/webhook';
|
|
377
|
+
import { YourAdapter } from './core/adapter';
|
|
378
|
+
import { YourTransformer } from './core/transformer';
|
|
379
|
+
import { YourStorage } from './core/storage';
|
|
380
|
+
|
|
381
|
+
// Load environment variables
|
|
382
|
+
dotenv.config();
|
|
383
|
+
|
|
384
|
+
async function bootstrap() {
|
|
385
|
+
// Initialize components
|
|
386
|
+
const storage = new YourStorage();
|
|
387
|
+
const transformer = new YourTransformer();
|
|
388
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
389
|
+
|
|
390
|
+
// Create Express application
|
|
391
|
+
const app = express();
|
|
392
|
+
app.use(bodyParser.json());
|
|
393
|
+
|
|
394
|
+
// Set up webhook routes
|
|
395
|
+
app.use('/webhook', webhookRouter);
|
|
396
|
+
|
|
397
|
+
// Start server
|
|
398
|
+
const port = process.env.PORT || 3000;
|
|
399
|
+
app.listen(port, () => {
|
|
400
|
+
console.log(`Server running on port ${port}`);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
console.log('Integration platform ready!');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Handle errors
|
|
407
|
+
bootstrap();
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Or with NestJS:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// main.ts
|
|
414
|
+
import { NestFactory } from '@nestjs/core';
|
|
415
|
+
import { AppModule } from './app.module';
|
|
416
|
+
import helmet from 'helmet';
|
|
417
|
+
|
|
418
|
+
async function bootstrap() {
|
|
419
|
+
const app = await NestFactory.create(AppModule);
|
|
420
|
+
app.setGlobalPrefix('api');
|
|
421
|
+
app.use(helmet());
|
|
422
|
+
await app.listen(process.env.PORT ?? 3000);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
bootstrap();
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Publishing Your Integration
|
|
429
|
+
|
|
430
|
+
1. **Prepare package.json**
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"name": "greenapi-integration-yourplatform",
|
|
435
|
+
"version": "1.0.0",
|
|
436
|
+
"main": "dist/index.js",
|
|
437
|
+
"types": "dist/index.d.ts",
|
|
438
|
+
"scripts": {
|
|
439
|
+
"build": "tsc",
|
|
440
|
+
"prepublishOnly": "npm run build"
|
|
441
|
+
},
|
|
442
|
+
"dependencies": {
|
|
443
|
+
"@green-api/greenapi-integration": "^1.0.0",
|
|
444
|
+
"@prisma/client": "^5.0.0",
|
|
445
|
+
"express": "^4.18.2"
|
|
446
|
+
// other dependencies
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
2. **Build and Publish**
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
npm run build
|
|
455
|
+
npm publish
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Working Example
|
|
459
|
+
|
|
460
|
+
Check out the `/examples/custom-adapter` directory for a complete working example showing:
|
|
461
|
+
|
|
462
|
+
- Two-way message flow between WhatsApp and a custom platform
|
|
463
|
+
- Webhook handling
|
|
464
|
+
- Instance setup and configuration
|
|
465
|
+
- Message transformation
|
|
466
|
+
- Error handling
|
|
467
|
+
|
|
468
|
+
### Running the Example
|
|
469
|
+
|
|
470
|
+
1. Clone the repository
|
|
471
|
+
2. Update .env with your GREEN-API credentials:
|
|
472
|
+
|
|
473
|
+
```env
|
|
474
|
+
VISITOR_ID_INSTANCE=your_visitor_instance_id
|
|
475
|
+
VISITOR_API_TOKEN=your_visitor_instance_token
|
|
476
|
+
AGENT_ID_INSTANCE=your_agent_instance_id
|
|
477
|
+
AGENT_API_TOKEN=your_agent_instance_token
|
|
478
|
+
AGENT_PHONE_NUMBER=your_agent_phone_number
|
|
479
|
+
WEBHOOK_URL=your_webhook_url
|
|
480
|
+
PORT=3000
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
3. Install dependencies and run:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
cd examples/custom-adapter
|
|
487
|
+
npm install
|
|
488
|
+
npm start
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## Complete Example Implementation
|
|
492
|
+
|
|
493
|
+
### Project Structure
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
examples/
|
|
497
|
+
└── custom-adapter/
|
|
498
|
+
├── src/
|
|
499
|
+
│ ├── main.ts
|
|
500
|
+
│ ├── simple-adapter.ts
|
|
501
|
+
│ ├── simple-transformer.ts
|
|
502
|
+
│ ├── simple-storage.ts
|
|
503
|
+
│ └── types.ts
|
|
504
|
+
├── .env
|
|
505
|
+
└── package.json
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### types.ts
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
interface SimplePlatformWebhook {
|
|
512
|
+
messageId: string;
|
|
513
|
+
from: string;
|
|
514
|
+
text: string;
|
|
515
|
+
timestamp: number;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
interface SimplePlatformMessage {
|
|
519
|
+
to: string;
|
|
520
|
+
content: string;
|
|
521
|
+
replyTo?: string;
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### simple-transformer.ts
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
import { MessageTransformer, Message, IncomingGreenApiWebhook, formatPhoneNumber } from 'greenapi-integration';
|
|
529
|
+
|
|
530
|
+
export class SimpleTransformer extends MessageTransformer<SimplePlatformWebhook, SimplePlatformMessage> {
|
|
531
|
+
toPlatformMessage(webhook: IncomingGreenApiWebhook): SimplePlatformMessage {
|
|
532
|
+
if (webhook.messageData.typeMessage !== 'extendedTextMessage') {
|
|
533
|
+
throw new Error('Only text messages are supported');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
to: webhook.senderData.sender,
|
|
538
|
+
content: webhook.messageData.extendedTextMessageData?.text || '',
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
toGreenApiMessage(message: SimplePlatformWebhook): Message {
|
|
543
|
+
return {
|
|
544
|
+
type: 'text',
|
|
545
|
+
chatId: formatPhoneNumber(message.from),
|
|
546
|
+
message: message.text,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### simple-storage.ts
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
import { StorageProvider, BaseUser, BaseInstance, Settings } from 'greenapi-integration';
|
|
556
|
+
|
|
557
|
+
export class SimpleStorage extends StorageProvider {
|
|
558
|
+
private users: Map<string, BaseUser> = new Map();
|
|
559
|
+
private instances: Map<number, BaseInstance> = new Map();
|
|
560
|
+
|
|
561
|
+
async createInstance(instance: BaseInstance, userId: bigint, settings?: Settings): Promise<BaseInstance> {
|
|
562
|
+
this.instances.set(Number(instance.idInstance), {
|
|
563
|
+
...instance,
|
|
564
|
+
settings: settings || {}
|
|
565
|
+
});
|
|
566
|
+
return instance;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async getInstance(idInstance: number): Promise<BaseInstance | null> {
|
|
570
|
+
return this.instances.get(idInstance) || null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async removeInstance(instanceId: number): Promise<BaseInstance> {
|
|
574
|
+
const instance = this.instances.get(instanceId);
|
|
575
|
+
if (!instance) throw new Error('Instance not found');
|
|
576
|
+
this.instances.delete(instanceId);
|
|
577
|
+
return instance;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async createUser(data: any): Promise<BaseUser> {
|
|
581
|
+
const user = {id: Date.now(), ...data};
|
|
582
|
+
this.users.set(data.email, user);
|
|
583
|
+
return user;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async findUser(identifier: string): Promise<BaseUser | null> {
|
|
587
|
+
return this.users.get(identifier) || null;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async updateUser(identifier: string, data: any): Promise<BaseUser> {
|
|
591
|
+
const user = await this.findUser(identifier);
|
|
592
|
+
if (!user) throw new Error('User not found');
|
|
593
|
+
const updated = {...user, ...data};
|
|
594
|
+
this.users.set(identifier, updated);
|
|
595
|
+
return updated;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### simple-adapter.ts
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
import { BaseAdapter, BaseInstance } from "greenapi-integration";
|
|
604
|
+
import axios from 'axios';
|
|
605
|
+
|
|
606
|
+
export class SimpleAdapter extends BaseAdapter<SimplePlatformWebhook, SimplePlatformMessage> {
|
|
607
|
+
async createPlatformClient(config: { apiKey: string, apiUrl: string }) {
|
|
608
|
+
return axios.create({
|
|
609
|
+
baseURL: config.apiUrl,
|
|
610
|
+
headers: {
|
|
611
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
612
|
+
'Content-Type': 'application/json'
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
async sendToPlatform(message: SimplePlatformMessage, instance: BaseInstance): Promise<void> {
|
|
618
|
+
// In a real implementation, we would send to the platform
|
|
619
|
+
// For demo, we'll just log and simulate a response
|
|
620
|
+
console.log('Platform received message:', message);
|
|
621
|
+
|
|
622
|
+
// Simulate platform processing and responding
|
|
623
|
+
setTimeout(() => {
|
|
624
|
+
console.log('Platform processing complete, sending response...');
|
|
625
|
+
this.simulatePlatformResponse(message, instance.idInstance);
|
|
626
|
+
}, 1000);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
private async simulatePlatformResponse(originalMessage: SimplePlatformMessage, idInstance: number | bigint) {
|
|
630
|
+
const platformWebhook: SimplePlatformWebhook = {
|
|
631
|
+
messageId: `resp_${Date.now()}`,
|
|
632
|
+
from: originalMessage.to.replace('@c.us', ''),
|
|
633
|
+
text: `Thanks for your message: "${originalMessage.content}". This is an automated response.`,
|
|
634
|
+
timestamp: Date.now()
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
await this.handlePlatformWebhook(platformWebhook, idInstance);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### main.ts
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
import express from "express";
|
|
646
|
+
import bodyParser from "body-parser";
|
|
647
|
+
import { formatPhoneNumber, GreenApiClient } from "greenapi-integration";
|
|
648
|
+
import { SimpleTransformer } from "./simple-transformer";
|
|
649
|
+
import { SimpleStorage } from "./simple-storage";
|
|
650
|
+
import { SimpleAdapter } from "./simple-adapter";
|
|
651
|
+
import * as dotenv from "dotenv";
|
|
652
|
+
|
|
653
|
+
dotenv.config();
|
|
654
|
+
|
|
655
|
+
async function main() {
|
|
656
|
+
// Initialize components
|
|
657
|
+
const transformer = new SimpleTransformer();
|
|
658
|
+
const storage = new SimpleStorage();
|
|
659
|
+
const adapter = new SimpleAdapter(transformer, storage);
|
|
660
|
+
|
|
661
|
+
// Configuration for both instances
|
|
662
|
+
const visitorInstance = {
|
|
663
|
+
idInstance: Number(process.env.VISITOR_ID_INSTANCE),
|
|
664
|
+
apiTokenInstance: process.env.VISITOR_API_TOKEN!,
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const agentInstance = {
|
|
668
|
+
idInstance: Number(process.env.AGENT_ID_INSTANCE),
|
|
669
|
+
apiTokenInstance: process.env.AGENT_API_TOKEN!,
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// Create visitor's GREEN-API client (for sending initial message)
|
|
673
|
+
const visitorClient = new GreenApiClient(visitorInstance);
|
|
674
|
+
|
|
675
|
+
// Set up agent instance
|
|
676
|
+
console.log("Setting up agent instance...");
|
|
677
|
+
const user = await adapter.createUser("agent@example.com", {
|
|
678
|
+
email: "agent@example.com",
|
|
679
|
+
name: "Agent",
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
const instance = await adapter.createInstance(agentInstance, {
|
|
683
|
+
webhookUrl: process.env.WEBHOOK_URL + "/webhook/green-api",
|
|
684
|
+
webhookUrlToken: "your-secure-token",
|
|
685
|
+
incomingWebhook: "yes",
|
|
686
|
+
}, user.email);
|
|
687
|
+
|
|
688
|
+
console.log("Waiting 2 minutes for settings to apply...");
|
|
689
|
+
await new Promise(resolve => setTimeout(resolve, 120000));
|
|
690
|
+
console.log("Instance ready!");
|
|
691
|
+
|
|
692
|
+
// Set up webhook server
|
|
693
|
+
const app = express();
|
|
694
|
+
app.use(bodyParser.json());
|
|
695
|
+
|
|
696
|
+
// Handle GREEN-API webhooks
|
|
697
|
+
app.post("/webhook/green-api", async (req, res) => {
|
|
698
|
+
try {
|
|
699
|
+
console.log("Received webhook from GREEN-API:", req.body);
|
|
700
|
+
await adapter.handleGreenApiWebhook(req.body, ["incomingMessageReceived"]);
|
|
701
|
+
res.status(200).json({status: "ok"});
|
|
702
|
+
} catch (error) {
|
|
703
|
+
console.error("Error handling webhook:", error);
|
|
704
|
+
res.status(500).json({error: "Internal server error"});
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// Start the server
|
|
709
|
+
const port = process.env.PORT || 3000;
|
|
710
|
+
app.listen(port, () => {
|
|
711
|
+
console.log(`Webhook server listening on port ${port}`);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Send initial message from visitor
|
|
715
|
+
console.log("Sending initial message from visitor...");
|
|
716
|
+
await visitorClient.sendMessage({
|
|
717
|
+
chatId: formatPhoneNumber(process.env.AGENT_PHONE_NUMBER!),
|
|
718
|
+
message: "Hello! This is a test message from a visitor.",
|
|
719
|
+
type: "text",
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
console.log("Initial message sent! Check the agent WhatsApp app to see the response.");
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
main().catch(console.error);
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
### .env
|
|
729
|
+
|
|
730
|
+
```env
|
|
731
|
+
VISITOR_ID_INSTANCE=your_visitor_instance_id
|
|
732
|
+
VISITOR_API_TOKEN=your_visitor_instance_token
|
|
733
|
+
AGENT_ID_INSTANCE=your_agent_instance_id
|
|
734
|
+
AGENT_API_TOKEN=your_agent_instance_token
|
|
735
|
+
AGENT_PHONE_NUMBER=your_agent_phone_number
|
|
736
|
+
WEBHOOK_URL=your_webhook_url
|
|
737
|
+
PORT=3000
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
## Real-World Examples
|
|
741
|
+
|
|
742
|
+
For complete real-world integration examples, check out:
|
|
743
|
+
|
|
744
|
+
- [Rocket.Chat Integration](link-to-rocket-chat-repo)
|
|
745
|
+
|
|
746
|
+
## Best Practices
|
|
747
|
+
|
|
748
|
+
1. **Message Transformation**:
|
|
749
|
+
- Handle only relevant message types
|
|
750
|
+
|
|
751
|
+
2. **Security**:
|
|
752
|
+
- Validate all incoming webhooks
|
|
753
|
+
- Use secure tokens
|
|
754
|
+
- Implement rate limiting
|
|
755
|
+
- Use HTTPS for all endpoints
|
|
756
|
+
|
|
757
|
+
## Utilities
|
|
758
|
+
|
|
759
|
+
The platform provides several utility functions:
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
// Format phone numbers for GREEN-API
|
|
763
|
+
formatPhoneNumber('1234567890') // Returns '1234567890@c.us'
|
|
764
|
+
|
|
765
|
+
// Generate secure random tokens
|
|
766
|
+
generateRandomToken(32) // Returns a 32-character random token
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
## License
|
|
770
|
+
|
|
771
|
+
MIT
|