@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.ru.md
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
# Универсальная интеграционная платформа для GREEN-API
|
|
2
|
+
|
|
3
|
+
## Поддержка
|
|
4
|
+
|
|
5
|
+
[](mailto:support@greenapi.com)
|
|
6
|
+
[](https://t.me/greenapi_support_bot)
|
|
7
|
+
[](https://wa.me/77273122366)
|
|
8
|
+
|
|
9
|
+
## Руководства и новости
|
|
10
|
+
|
|
11
|
+
[](https://www.youtube.com/@green-api)
|
|
12
|
+
[](https://t.me/green_api)
|
|
13
|
+
[](https://whatsapp.com/channel/0029VaLj6J4LNSa2B5Jx6s3h)
|
|
14
|
+
|
|
15
|
+
- [Documentation in English](./README.md)
|
|
16
|
+
|
|
17
|
+
Гибкая интеграционная платформа, разработанная для упрощения процесса подключения WhatsApp шлюза GREEN-API к различным
|
|
18
|
+
сторонним сервисам.
|
|
19
|
+
|
|
20
|
+
## Содержание
|
|
21
|
+
|
|
22
|
+
- [Установка](#установка)
|
|
23
|
+
- [Основные компоненты](#основные-компоненты)
|
|
24
|
+
- [Руководство разработчика](#руководство-разработчика)
|
|
25
|
+
- [Рабочий пример](#рабочий-пример)
|
|
26
|
+
- [Реальные примеры](#реальные-примеры)
|
|
27
|
+
- [Лучшие практики](#лучшие-практики)
|
|
28
|
+
|
|
29
|
+
## Установка
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @green-api/greenapi-integration
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Основные компоненты
|
|
36
|
+
|
|
37
|
+
### 1. BaseAdapter
|
|
38
|
+
|
|
39
|
+
Основа вашей интеграции. Управляет сообщениями и инстансами, а также логикой взаимодействия с платформой.
|
|
40
|
+
BaseAdapter внутренне использует GreenApiClient для всех общих операций, поэтому в большинстве случаев вам не нужно
|
|
41
|
+
использовать
|
|
42
|
+
методы GreenApiClient напрямую.
|
|
43
|
+
|
|
44
|
+
**Когда использовать BaseAdapter, а когда GreenApiClient**:
|
|
45
|
+
|
|
46
|
+
✅ Используйте методы BaseAdapter для всех стандартных операций (отправка сообщений, обработка вебхуков, управление
|
|
47
|
+
инстансами)
|
|
48
|
+
|
|
49
|
+
⚠️ Используйте GreenApiClient напрямую только для специальных операций, не покрытых BaseAdapter (например,
|
|
50
|
+
setProfilePicture, getAuthorizationCode)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage> {
|
|
54
|
+
public constructor(
|
|
55
|
+
transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>,
|
|
56
|
+
storage: StorageProvider
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
public abstract createPlatformClient(params: any): Promise<any>;
|
|
60
|
+
|
|
61
|
+
public abstract sendToPlatform(message: TPlatformMessage, instance: TInstance): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Пример правильного использования:**
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// ✅ ПРАВИЛЬНО: Использование BaseAdapter для стандартных операций
|
|
69
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
70
|
+
await adapter.handlePlatformWebhook(webhook, instanceId);
|
|
71
|
+
await adapter.createInstance(instance, settings, userCred);
|
|
72
|
+
await adapter.sendMessage(transformedWebhook);
|
|
73
|
+
|
|
74
|
+
// ⚠️ ТОЛЬКО ПРИ НЕОБХОДИМОСТИ: Прямое использование GreenApiClient для специальных операций
|
|
75
|
+
const client = new GreenApiClient(instance);
|
|
76
|
+
await client.setProfilePicture(fileBlob);
|
|
77
|
+
await client.getAuthorizationCode(phoneNumber);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. MessageTransformer
|
|
81
|
+
|
|
82
|
+
Преобразовывает форматы сообщений между GREEN-API и вашей платформой.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
abstract class MessageTransformer<TPlatformWebhook, TPlatformMessage> {
|
|
86
|
+
abstract toPlatformMessage(webhook: IncomingGreenApiWebhook): TPlatformMessage;
|
|
87
|
+
|
|
88
|
+
abstract toGreenApiMessage(message: TPlatformWebhook): Message;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. StorageProvider
|
|
93
|
+
|
|
94
|
+
Интерфейс для операций с хранением данных.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
abstract class StorageProvider<TUser extends BaseUser = BaseUser, TInstance extends BaseInstance = Instance> {
|
|
98
|
+
abstract createInstance(instance: BaseInstance, userId: bigint | number, settings?: Settings): Promise<TInstance>;
|
|
99
|
+
|
|
100
|
+
abstract getInstance(idInstance: number | bigint): Promise<TInstance | null>;
|
|
101
|
+
|
|
102
|
+
abstract removeInstance(instanceId: number | bigint): Promise<TInstance>;
|
|
103
|
+
|
|
104
|
+
abstract createUser(data: any): Promise<TUser>;
|
|
105
|
+
|
|
106
|
+
abstract findUser(identifier: string): Promise<TUser | null>;
|
|
107
|
+
|
|
108
|
+
abstract updateUser(identifier: string, data: any): Promise<TUser>;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 4. BaseGreenApiAuthGuard
|
|
113
|
+
|
|
114
|
+
Аутентифицирует входящие вебхуки от GREEN-API.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
abstract class BaseGreenApiAuthGuard<T extends BaseRequest = BaseRequest> {
|
|
118
|
+
constructor(protected storage: StorageProvider);
|
|
119
|
+
|
|
120
|
+
// Валидация входящих вебхуков
|
|
121
|
+
async validateRequest(request: T): Promise<boolean>;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Пример использования `BaseGreenApiAuthGuard`:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
class YourAuthGuard extends BaseGreenApiAuthGuard<YourRequest> {
|
|
129
|
+
constructor(storage: StorageProvider) {
|
|
130
|
+
super(storage);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Использование с Express
|
|
135
|
+
app.post('/webhook', async (req, res) => {
|
|
136
|
+
const guard = new YourAuthGuard(storage);
|
|
137
|
+
try {
|
|
138
|
+
await guard.validateRequest(req);
|
|
139
|
+
// Обработка вебхука ...
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error instanceof AuthenticationError) {
|
|
142
|
+
res.status(401).json({error: error.message});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
res.status(500).json({error: 'Internal server error'});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 5. GreenApiClient
|
|
151
|
+
|
|
152
|
+
Прямой интерфейс к эндпоинтам GREEN-API. Хотя большинство операций должны выполняться через BaseAdapter, GreenApiClient
|
|
153
|
+
может использоваться напрямую для операций, непокрытых в `BaseAdapter`.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const client = new GreenApiClient({
|
|
157
|
+
idInstance: 'your_instance_id',
|
|
158
|
+
apiTokenInstance: 'your_token'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Примеры специальных операций:
|
|
162
|
+
await client.setProfilePicture(fileBlob);
|
|
163
|
+
await client.getAuthorizationCode(phoneNumber);
|
|
164
|
+
await client.getQR();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Руководство разработчика
|
|
168
|
+
|
|
169
|
+
### Структура проекта
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
your-integration/
|
|
173
|
+
├── src/
|
|
174
|
+
│ ├── core/
|
|
175
|
+
│ │ ├── adapter.ts # Адаптер платформы
|
|
176
|
+
│ │ ├── transformer.ts # Преобразователь сообщений
|
|
177
|
+
│ │ ├── storage.ts # Реализация хранилища
|
|
178
|
+
│ │ └── router.ts # Эндпоинты для вебхуков
|
|
179
|
+
│ ├── types/
|
|
180
|
+
│ │ └── types.ts # Типы
|
|
181
|
+
│ └── main.ts # Точка запуска приложения
|
|
182
|
+
├── package.json
|
|
183
|
+
└── tsconfig.json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Этапы реализации
|
|
187
|
+
|
|
188
|
+
1. **Определение типов платформы**
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// types/types.ts
|
|
192
|
+
export interface YourPlatformWebhook {
|
|
193
|
+
id: string;
|
|
194
|
+
from: string;
|
|
195
|
+
message: string;
|
|
196
|
+
timestamp: number;
|
|
197
|
+
// Добавьте другие поля, специфичные для вашей платформы
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface YourPlatformMessage {
|
|
201
|
+
recipient: string;
|
|
202
|
+
content: string;
|
|
203
|
+
// Добавьте другие поля, специфичные для вашей платформы
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
2. **Создание преобразователя сообщений**
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// core/transformer.ts
|
|
211
|
+
import { MessageTransformer, Message, IncomingGreenApiWebhook } from '@green-api/greenapi-integration';
|
|
212
|
+
import { YourPlatformWebhook, YourPlatformMessage } from '../types/types';
|
|
213
|
+
|
|
214
|
+
export class YourTransformer extends MessageTransformer<YourPlatformWebhook, YourPlatformMessage> {
|
|
215
|
+
toPlatformMessage(webhook: IncomingGreenApiWebhook): YourPlatformMessage {
|
|
216
|
+
// Преобразование вебхука GREEN-API в формат вашей платформы
|
|
217
|
+
return {
|
|
218
|
+
recipient: webhook.senderData.sender,
|
|
219
|
+
content: webhook.messageData.textMessageData?.textMessage || '',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
toGreenApiMessage(message: YourPlatformWebhook): Message {
|
|
224
|
+
// Преобразование вебхука вашей платформы в формат GREEN-API
|
|
225
|
+
return {
|
|
226
|
+
type: 'text',
|
|
227
|
+
chatId: message.from,
|
|
228
|
+
message: message.message,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
3. **Реализация хранилища**
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// core/storage.ts
|
|
238
|
+
import { StorageProvider, BaseUser, BaseInstance, Settings } from '@green-api/greenapi-integration';
|
|
239
|
+
import { PrismaClient } from '@prisma/client'; // Or your database client
|
|
240
|
+
|
|
241
|
+
export class YourStorage extends StorageProvider {
|
|
242
|
+
private db: PrismaClient;
|
|
243
|
+
|
|
244
|
+
constructor() {
|
|
245
|
+
this.db = new PrismaClient();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async createInstance(instance: BaseInstance, userId: bigint, settings?: Settings) {
|
|
249
|
+
return this.db.instance.create({
|
|
250
|
+
data: {
|
|
251
|
+
idInstance: instance.idInstance,
|
|
252
|
+
apiTokenInstance: instance.apiTokenInstance,
|
|
253
|
+
userId,
|
|
254
|
+
settings: settings || {},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Остальные методы
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
4. **Создание адаптера платформы**
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// core/adapter.ts
|
|
267
|
+
import { BaseAdapter, BaseInstance } from '@green-api/greenapi-integration';
|
|
268
|
+
import { YourPlatformClient } from 'your-platform-sdk';
|
|
269
|
+
import { YourPlatformWebhook, YourPlatformMessage } from '../types/types';
|
|
270
|
+
|
|
271
|
+
export class YourAdapter extends BaseAdapter<YourPlatformWebhook, YourPlatformMessage> {
|
|
272
|
+
async createPlatformClient(config: { apiKey: string, apiUrl: string }) {
|
|
273
|
+
return new YourPlatformClient({
|
|
274
|
+
baseUrl: config.apiUrl,
|
|
275
|
+
apiKey: config.apiKey,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async sendToPlatform(message: YourPlatformMessage, instance: BaseInstance) {
|
|
280
|
+
const client = await this.createPlatformClient(instance.config);
|
|
281
|
+
await client.sendMessage(message);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
5. **Реализация контроллера вебхуков**
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// core/webhook.ts
|
|
290
|
+
import express from 'express';
|
|
291
|
+
import { YourAdapter } from '../core/adapter';
|
|
292
|
+
import { YourTransformer } from '../core/transformer';
|
|
293
|
+
import { YourStorage } from '../core/storage';
|
|
294
|
+
|
|
295
|
+
const router = express.Router();
|
|
296
|
+
const storage = new YourStorage();
|
|
297
|
+
const transformer = new YourTransformer();
|
|
298
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
299
|
+
|
|
300
|
+
class WebhookGuard extends BaseGreenApiAuthGuard {
|
|
301
|
+
constructor(storage: StorageProvider) {
|
|
302
|
+
super(storage);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const guard = new WebhookGuard(storage);
|
|
307
|
+
|
|
308
|
+
// Эндпоинты для вебхуков
|
|
309
|
+
router.post('/green-api', async (req, res) => {
|
|
310
|
+
try {
|
|
311
|
+
// Проверка вебхука
|
|
312
|
+
await guard.validateRequest(req);
|
|
313
|
+
|
|
314
|
+
// Обработка вебхука после проверки.
|
|
315
|
+
// В списке вторым параметром укажите типы вебхуков, которые необходимо обработать
|
|
316
|
+
await adapter.handleGreenApiWebhook(req.body, ['incomingMessageReceived']);
|
|
317
|
+
res.status(200).json({status: 'ok'});
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (error instanceof AuthenticationError) {
|
|
320
|
+
res.status(401).json({error: 'Ошибка аутентификации'});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
console.error('Ошибка обработки вебхука:', error);
|
|
324
|
+
res.status(500).json({error: 'Внутренняя ошибка сервера'});
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
router.post('/platform', async (req, res) => {
|
|
329
|
+
try {
|
|
330
|
+
const instanceId = req.query.instanceId;
|
|
331
|
+
await adapter.handlePlatformWebhook(req.body, instanceId);
|
|
332
|
+
res.status(200).json({status: 'ok'});
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error('Ошибка обработки вебхука платформы:', error);
|
|
335
|
+
res.status(500).json({error: 'Внутренняя ошибка сервера'});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
router.post('/instance', async (req, res) => {
|
|
340
|
+
try {
|
|
341
|
+
const {idInstance, apiTokenInstance, userEmail} = req.body;
|
|
342
|
+
|
|
343
|
+
if (!idInstance || !apiTokenInstance || !userEmail) {
|
|
344
|
+
throw new BadRequestError('Отсутствуют обязательные поля');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const instance = await adapter.createInstance({
|
|
348
|
+
idInstance: Number(idInstance),
|
|
349
|
+
apiTokenInstance
|
|
350
|
+
}, {
|
|
351
|
+
webhookUrl: `${process.env.APP_URL}/webhook/green-api`,
|
|
352
|
+
webhookUrlToken: `token_${Date.now()}`, // В продакшене используйте безопасный генератор токенов
|
|
353
|
+
incomingWebhook: 'yes'
|
|
354
|
+
}, userEmail);
|
|
355
|
+
|
|
356
|
+
res.status(200).json({
|
|
357
|
+
status: 'ok',
|
|
358
|
+
data: instance,
|
|
359
|
+
message: 'Инстанс успешно создан. Подождите 2 минуты для применения настроек.'
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Ошибка создания инстанса:', error);
|
|
364
|
+
res.status(500).json({error: 'Не удалось создать инстанс'});
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
export default router;
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
6. **Создание точки входа приложения**
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// main.ts
|
|
375
|
+
import express from 'express';
|
|
376
|
+
import bodyParser from 'body-parser';
|
|
377
|
+
import dotenv from 'dotenv';
|
|
378
|
+
import webhookRouter from './controllers/webhook';
|
|
379
|
+
import { YourAdapter } from './core/adapter';
|
|
380
|
+
import { YourTransformer } from './core/transformer';
|
|
381
|
+
import { YourStorage } from './core/storage';
|
|
382
|
+
|
|
383
|
+
// Загрузка переменных окружения
|
|
384
|
+
dotenv.config();
|
|
385
|
+
|
|
386
|
+
async function bootstrap() {
|
|
387
|
+
// Инициализация компонентов
|
|
388
|
+
const storage = new YourStorage();
|
|
389
|
+
const transformer = new YourTransformer();
|
|
390
|
+
const adapter = new YourAdapter(transformer, storage);
|
|
391
|
+
|
|
392
|
+
// Создание Express приложения
|
|
393
|
+
const app = express();
|
|
394
|
+
app.use(bodyParser.json());
|
|
395
|
+
|
|
396
|
+
// Настройка маршрутов для вебхуков
|
|
397
|
+
app.use('/webhook', webhookRouter);
|
|
398
|
+
|
|
399
|
+
// Запуск сервера
|
|
400
|
+
const port = process.env.PORT || 3000;
|
|
401
|
+
app.listen(port, () => {
|
|
402
|
+
console.log(`Сервер запущен на порту ${port}`);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
console.log('Интеграционная платформа готова!');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Обработка ошибок
|
|
409
|
+
bootstrap();
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Или с NestJS:
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// main.ts
|
|
416
|
+
import { NestFactory } from '@nestjs/core';
|
|
417
|
+
import { AppModule } from './app.module';
|
|
418
|
+
import helmet from 'helmet';
|
|
419
|
+
|
|
420
|
+
async function bootstrap() {
|
|
421
|
+
const app = await NestFactory.create(AppModule);
|
|
422
|
+
app.setGlobalPrefix('api');
|
|
423
|
+
app.use(helmet());
|
|
424
|
+
await app.listen(process.env.PORT ?? 3000);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
bootstrap();
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Сборка приложения
|
|
431
|
+
|
|
432
|
+
1. **Подготовка package.json**
|
|
433
|
+
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"name": "greenapi-integration-yourplatform",
|
|
437
|
+
"version": "1.0.0",
|
|
438
|
+
"main": "dist/index.js",
|
|
439
|
+
"types": "dist/index.d.ts",
|
|
440
|
+
"scripts": {
|
|
441
|
+
"build": "tsc",
|
|
442
|
+
"prepublishOnly": "npm run build"
|
|
443
|
+
},
|
|
444
|
+
"dependencies": {
|
|
445
|
+
"@green-api/greenapi-integration": "^1.0.0",
|
|
446
|
+
"@prisma/client": "^5.0.0",
|
|
447
|
+
"express": "^4.18.2"
|
|
448
|
+
// другие зависимости
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
2. **Сборка**
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
npm run build
|
|
457
|
+
npm publish
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Рабочий пример
|
|
461
|
+
|
|
462
|
+
В директории `/examples/custom-adapter` вы найдете полный рабочий пример, демонстрирующий:
|
|
463
|
+
|
|
464
|
+
- Двустороннюю передачу сообщений между WhatsApp и пользовательской платформой
|
|
465
|
+
- Обработку вебхуков
|
|
466
|
+
- Настройку и конфигурацию инстанса
|
|
467
|
+
- Преобразование сообщений
|
|
468
|
+
- Обработку ошибок
|
|
469
|
+
|
|
470
|
+
### Запуск примера
|
|
471
|
+
|
|
472
|
+
1. Клонируйте репозиторий
|
|
473
|
+
2. Обновите .env данными ваших инстансов GREEN-API:
|
|
474
|
+
|
|
475
|
+
```env
|
|
476
|
+
VISITOR_ID_INSTANCE=your_visitor_instance_id
|
|
477
|
+
VISITOR_API_TOKEN=your_visitor_instance_token
|
|
478
|
+
AGENT_ID_INSTANCE=your_agent_instance_id
|
|
479
|
+
AGENT_API_TOKEN=your_agent_instance_token
|
|
480
|
+
AGENT_PHONE_NUMBER=your_agent_phone_number
|
|
481
|
+
WEBHOOK_URL=your_webhook_url
|
|
482
|
+
PORT=3000
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
3. Установите зависимости и запустите:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
cd examples/custom-adapter
|
|
489
|
+
npm install
|
|
490
|
+
npm start
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
# Полная реализация примера
|
|
494
|
+
|
|
495
|
+
### Структура проекта
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
examples/
|
|
499
|
+
└── custom-adapter/
|
|
500
|
+
├── src/
|
|
501
|
+
│ ├── main.ts
|
|
502
|
+
│ ├── simple-adapter.ts
|
|
503
|
+
│ ├── simple-transformer.ts
|
|
504
|
+
│ ├── simple-storage.ts
|
|
505
|
+
│ └── types.ts
|
|
506
|
+
├── .env
|
|
507
|
+
├── package.json
|
|
508
|
+
└── tsconfig.json
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### types.ts
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
interface SimplePlatformWebhook {
|
|
515
|
+
messageId: string;
|
|
516
|
+
from: string;
|
|
517
|
+
text: string;
|
|
518
|
+
timestamp: number;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
interface SimplePlatformMessage {
|
|
522
|
+
to: string;
|
|
523
|
+
content: string;
|
|
524
|
+
replyTo?: string;
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### simple-transformer.ts
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
import { MessageTransformer, Message, IncomingGreenApiWebhook, formatPhoneNumber } from 'greenapi-integration';
|
|
532
|
+
|
|
533
|
+
export class SimpleTransformer extends MessageTransformer<SimplePlatformWebhook, SimplePlatformMessage> {
|
|
534
|
+
toPlatformMessage(webhook: IncomingGreenApiWebhook): SimplePlatformMessage {
|
|
535
|
+
if (webhook.messageData.typeMessage !== 'extendedTextMessage') {
|
|
536
|
+
throw new Error('Поддерживаются только текстовые сообщения');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
to: webhook.senderData.sender,
|
|
541
|
+
content: webhook.messageData.extendedTextMessageData?.text || '',
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
toGreenApiMessage(message: SimplePlatformWebhook): Message {
|
|
546
|
+
return {
|
|
547
|
+
type: 'text',
|
|
548
|
+
chatId: formatPhoneNumber(message.from),
|
|
549
|
+
message: message.text,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### simple-storage.ts
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
import { StorageProvider, BaseUser, BaseInstance, Settings } from 'greenapi-integration';
|
|
559
|
+
|
|
560
|
+
export class SimpleStorage extends StorageProvider {
|
|
561
|
+
private users: Map<string, BaseUser> = new Map();
|
|
562
|
+
private instances: Map<number, BaseInstance> = new Map();
|
|
563
|
+
|
|
564
|
+
async createInstance(instance: BaseInstance, userId: bigint, settings?: Settings): Promise<BaseInstance> {
|
|
565
|
+
this.instances.set(Number(instance.idInstance), {
|
|
566
|
+
...instance,
|
|
567
|
+
settings: settings || {}
|
|
568
|
+
});
|
|
569
|
+
return instance;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async getInstance(idInstance: number): Promise<BaseInstance | null> {
|
|
573
|
+
return this.instances.get(idInstance) || null;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async removeInstance(instanceId: number): Promise<BaseInstance> {
|
|
577
|
+
const instance = this.instances.get(instanceId);
|
|
578
|
+
if (!instance) throw new Error('Инстанс не найден');
|
|
579
|
+
this.instances.delete(instanceId);
|
|
580
|
+
return instance;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async createUser(data: any): Promise<BaseUser> {
|
|
584
|
+
const user = {id: Date.now(), ...data};
|
|
585
|
+
this.users.set(data.email, user);
|
|
586
|
+
return user;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async findUser(identifier: string): Promise<BaseUser | null> {
|
|
590
|
+
return this.users.get(identifier) || null;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async updateUser(identifier: string, data: any): Promise<BaseUser> {
|
|
594
|
+
const user = await this.findUser(identifier);
|
|
595
|
+
if (!user) throw new Error('Пользователь не найден');
|
|
596
|
+
const updated = {...user, ...data};
|
|
597
|
+
this.users.set(identifier, updated);
|
|
598
|
+
return updated;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### simple-adapter.ts
|
|
604
|
+
|
|
605
|
+
```typescript
|
|
606
|
+
import { BaseAdapter, BaseInstance } from "greenapi-integration";
|
|
607
|
+
import axios from 'axios';
|
|
608
|
+
|
|
609
|
+
export class SimpleAdapter extends BaseAdapter<SimplePlatformWebhook, SimplePlatformMessage> {
|
|
610
|
+
async createPlatformClient(config: { apiKey: string, apiUrl: string }) {
|
|
611
|
+
return axios.create({
|
|
612
|
+
baseURL: config.apiUrl,
|
|
613
|
+
headers: {
|
|
614
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
615
|
+
'Content-Type': 'application/json'
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async sendToPlatform(message: SimplePlatformMessage, instance: BaseInstance): Promise<void> {
|
|
621
|
+
// В реальной реализации мы бы отправляли сообщение на платформу
|
|
622
|
+
// Для демонстрации просто логируем и симулируем ответ
|
|
623
|
+
console.log('Платформа получила сообщение:', message);
|
|
624
|
+
|
|
625
|
+
// Симулируем обработку и ответ платформы
|
|
626
|
+
setTimeout(() => {
|
|
627
|
+
console.log('Обработка платформой завершена, отправляем ответ...');
|
|
628
|
+
this.simulatePlatformResponse(message, instance.idInstance);
|
|
629
|
+
}, 1000);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private async simulatePlatformResponse(originalMessage: SimplePlatformMessage, idInstance: number | bigint) {
|
|
633
|
+
const platformWebhook: SimplePlatformWebhook = {
|
|
634
|
+
messageId: `resp_${Date.now()}`,
|
|
635
|
+
from: originalMessage.to.replace('@c.us', ''),
|
|
636
|
+
text: `Спасибо за ваше сообщение: "${originalMessage.content}". Это автоматический ответ.`,
|
|
637
|
+
timestamp: Date.now()
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
await this.handlePlatformWebhook(platformWebhook, idInstance);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### main.ts
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
import express from "express";
|
|
649
|
+
import bodyParser from "body-parser";
|
|
650
|
+
import { formatPhoneNumber, GreenApiClient } from "greenapi-integration";
|
|
651
|
+
import { SimpleTransformer } from "./simple-transformer";
|
|
652
|
+
import { SimpleStorage } from "./simple-storage";
|
|
653
|
+
import { SimpleAdapter } from "./simple-adapter";
|
|
654
|
+
import * as dotenv from "dotenv";
|
|
655
|
+
|
|
656
|
+
dotenv.config();
|
|
657
|
+
|
|
658
|
+
async function main() {
|
|
659
|
+
// Инициализация компонентов
|
|
660
|
+
const transformer = new SimpleTransformer();
|
|
661
|
+
const storage = new SimpleStorage();
|
|
662
|
+
const adapter = new SimpleAdapter(transformer, storage);
|
|
663
|
+
|
|
664
|
+
// Конфигурация обоих инстансов
|
|
665
|
+
const visitorInstance = {
|
|
666
|
+
idInstance: Number(process.env.VISITOR_ID_INSTANCE),
|
|
667
|
+
apiTokenInstance: process.env.VISITOR_API_TOKEN!,
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
const agentInstance = {
|
|
671
|
+
idInstance: Number(process.env.AGENT_ID_INSTANCE),
|
|
672
|
+
apiTokenInstance: process.env.AGENT_API_TOKEN!,
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// Создание клиента GREEN-API для посетителя (для отправки начального сообщения)
|
|
676
|
+
const visitorClient = new GreenApiClient(visitorInstance);
|
|
677
|
+
|
|
678
|
+
// Настройка инстанса агента
|
|
679
|
+
console.log("Настройка инстанса агента...");
|
|
680
|
+
const user = await adapter.createUser("agent@example.com", {
|
|
681
|
+
email: "agent@example.com",
|
|
682
|
+
name: "Agent",
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
const instance = await adapter.createInstance(agentInstance, {
|
|
686
|
+
webhookUrl: process.env.WEBHOOK_URL + "/webhook/green-api",
|
|
687
|
+
webhookUrlToken: "your-secure-token",
|
|
688
|
+
incomingWebhook: "yes",
|
|
689
|
+
}, user.email);
|
|
690
|
+
|
|
691
|
+
console.log("Ожидание 2 минуты для применения настроек...");
|
|
692
|
+
await new Promise(resolve => setTimeout(resolve, 120000));
|
|
693
|
+
console.log("Инстанс готов!");
|
|
694
|
+
|
|
695
|
+
// Настройка веб-сервера
|
|
696
|
+
const app = express();
|
|
697
|
+
app.use(bodyParser.json());
|
|
698
|
+
|
|
699
|
+
// Обработка вебхуков от GREEN-API
|
|
700
|
+
app.post("/webhook/green-api", async (req, res) => {
|
|
701
|
+
try {
|
|
702
|
+
console.log("Получен вебхук от GREEN-API:", req.body);
|
|
703
|
+
await adapter.handleGreenApiWebhook(req.body, ["incomingMessageReceived"]);
|
|
704
|
+
res.status(200).json({status: "ok"});
|
|
705
|
+
} catch (error) {
|
|
706
|
+
console.error("Ошибка обработки вебхука:", error);
|
|
707
|
+
res.status(500).json({error: "Внутренняя ошибка сервера"});
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Запуск сервера
|
|
712
|
+
const port = process.env.PORT || 3000;
|
|
713
|
+
app.listen(port, () => {
|
|
714
|
+
console.log(`Сервер вебхуков запущен на порту ${port}`);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// Отправка начального сообщения от посетителя
|
|
718
|
+
console.log("Отправка начального сообщения от посетителя...");
|
|
719
|
+
await visitorClient.sendMessage({
|
|
720
|
+
chatId: formatPhoneNumber(process.env.AGENT_PHONE_NUMBER!),
|
|
721
|
+
message: "Здравствуйте! Это тестовое сообщение от посетителя.",
|
|
722
|
+
type: "text",
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
console.log("Начальное сообщение отправлено! Проверьте WhatsApp агента для просмотра ответа.");
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
main().catch(console.error);
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### .env
|
|
732
|
+
|
|
733
|
+
```env
|
|
734
|
+
VISITOR_ID_INSTANCE=your_visitor_instance_id
|
|
735
|
+
VISITOR_API_TOKEN=your_visitor_instance_token
|
|
736
|
+
AGENT_ID_INSTANCE=your_agent_instance_id
|
|
737
|
+
AGENT_API_TOKEN=your_agent_instance_token
|
|
738
|
+
AGENT_PHONE_NUMBER=your_agent_phone_number
|
|
739
|
+
WEBHOOK_URL=your_webhook_url
|
|
740
|
+
PORT=3000
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
## Реальные примеры
|
|
744
|
+
|
|
745
|
+
Для полных примеров реальных интеграций, смотрите:
|
|
746
|
+
|
|
747
|
+
- [Интеграция с Rocket.Chat](link-to-rocket-chat-repo)
|
|
748
|
+
|
|
749
|
+
## Лучшие практики
|
|
750
|
+
|
|
751
|
+
1. **Преобразование сообщений**:
|
|
752
|
+
- Обрабатывайте только релевантные типы сообщений
|
|
753
|
+
|
|
754
|
+
2. **Безопасность**:
|
|
755
|
+
- Проверяйте все входящие вебхуки
|
|
756
|
+
- Используйте безопасные вебхук-токены
|
|
757
|
+
- Реализуйте ограничение частоты запросов
|
|
758
|
+
- Используйте HTTPS для всех эндпоинтов
|
|
759
|
+
|
|
760
|
+
## Утилиты
|
|
761
|
+
|
|
762
|
+
Платформа предоставляет несколько вспомогательных функций:
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
// Форматирование телефонных номеров для GREEN-API
|
|
766
|
+
formatPhoneNumber('1234567890') // Возвращает '1234567890@c.us'
|
|
767
|
+
|
|
768
|
+
// Генерация безопасных случайных токенов
|
|
769
|
+
generateRandomToken(32) // Возвращает 32-символьный случайный токен
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
## Лицензия
|
|
773
|
+
|
|
774
|
+
MIT
|