@green-api/greenapi-integration 0.4.0 → 0.6.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 CHANGED
@@ -39,10 +39,12 @@ npm install @green-api/greenapi-integration
39
39
  The foundation of your integration. Handles message & instance management, and platform-specific logic.
40
40
 
41
41
  ```typescript
42
- abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage> {
42
+ abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TUser extends BaseUser = BaseUser, TInstance extends Instance = Instance> {
43
+ private readonly gaLogger = GreenApiLogger.getInstance(this.constructor.name);
44
+
43
45
  public constructor(
44
46
  transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>,
45
- storage: StorageProvider
47
+ storage: StorageProvider<TUser, TInstance>,
46
48
  );
47
49
 
48
50
  public abstract createPlatformClient(params: any): Promise<any>;
@@ -58,8 +60,7 @@ When extending BaseAdapter, your implementation has access to several methods:
58
60
  ##### Webhook Handling
59
61
 
60
62
  These webhook handling methods call your message transformation methods automatically, without the need to use them
61
- directly in
62
- your code.
63
+ directly in your code.
63
64
 
64
65
  ```typescript
65
66
  // Handle webhooks from your platform
@@ -140,18 +141,23 @@ abstract class MessageTransformer<TPlatformWebhook, TPlatformMessage> {
140
141
  Interface for data persistence operations.
141
142
 
142
143
  ```typescript
143
- abstract class StorageProvider<TUser extends BaseUser = BaseUser, TInstance extends BaseInstance = Instance> {
144
- abstract createInstance(instance: BaseInstance, userId: bigint | number): Promise<TInstance>;
144
+ abstract class StorageProvider<
145
+ TUser extends BaseUser = BaseUser,
146
+ TInstance extends Instance = Instance,
147
+ TUserCreate extends Record<string, any> = any,
148
+ TUserUpdate extends Record<string, any> = any
149
+ > {
150
+ abstract createInstance(instance: Instance): Promise<TInstance>;
145
151
 
146
152
  abstract getInstance(idInstance: number | bigint): Promise<TInstance | null>;
147
153
 
148
154
  abstract removeInstance(instanceId: number | bigint): Promise<TInstance>;
149
155
 
150
- abstract createUser(data: any): Promise<TUser>;
156
+ abstract createUser(data: TUserCreate): Promise<TUser>;
151
157
 
152
158
  abstract findUser(identifier: string): Promise<TUser | null>;
153
159
 
154
- abstract updateUser(identifier: string, data: any): Promise<TUser>;
160
+ abstract updateUser(identifier: string, data: Partial<TUserUpdate>): Promise<TUser>;
155
161
  }
156
162
  ```
157
163
 
@@ -161,6 +167,8 @@ Handles webhook authentication for incoming GREEN-API requests.
161
167
 
162
168
  ```typescript
163
169
  abstract class BaseGreenApiAuthGuard<T extends BaseRequest = BaseRequest> {
170
+ private readonly gaLogger = GreenApiLogger.getInstance(this.constructor.name);
171
+
164
172
  constructor(protected storage: StorageProvider);
165
173
 
166
174
  // Validates incoming webhook requests
@@ -193,7 +201,196 @@ app.post('/webhook', async (req, res) => {
193
201
  });
194
202
  ```
195
203
 
196
- ### 5. GreenApiClient
204
+ ### 5. GreenApiLogger
205
+
206
+ A structured JSON logger with colored output. Provides consistent logging format across your application with
207
+ proper error handling and serialization support.
208
+
209
+ ```typescript
210
+ const logger = GreenApiLogger.getInstance("YourComponent");
211
+
212
+ // Basic logging
213
+ logger.debug("Debug message", {someContext: "value"});
214
+ logger.info("Info message", {userId: 123});
215
+ logger.warn("Warning message", {alert: true});
216
+ logger.error("Error occurred", {errorCode: 500});
217
+ logger.fatal("Fatal error", {critical: true});
218
+
219
+ // Error logging with full context
220
+ try {
221
+ await someOperation();
222
+ } catch (error) {
223
+ logger.logErrorResponse(error, "Operation failed", {
224
+ operationId: "123",
225
+ additionalInfo: "some context"
226
+ });
227
+ }
228
+ ```
229
+
230
+ #### Features
231
+
232
+ - Structured JSON logging with consistent format
233
+ - Colored output based on log level (debug=cyan, info=green, warn=yellow, error=red, fatal=magenta)
234
+ - Built-in error handling with stack trace formatting
235
+ - Automatic serialization
236
+ - Framework agnostic - works with any Node.js application
237
+ - Special handling for Axios errors with detailed request/response info
238
+
239
+ #### Log Levels
240
+
241
+ - `debug` - Detailed information for debugging
242
+ - `info` - General information about system operation
243
+ - `warn` - Warning messages for potentially harmful situations
244
+ - `error` - Error messages for serious problems
245
+ - `fatal` - Critical errors that require immediate attention
246
+ - `log` - Alternative to info (for compatibility)
247
+
248
+ #### Output Format
249
+
250
+ ```json
251
+ {
252
+ "timestamp": "30/01/2025, 04:34:49",
253
+ "level": "error",
254
+ "context": "CoreService",
255
+ "message": "Operation failed",
256
+ "error": "Failed to process request",
257
+ "stack": [
258
+ "Error: Failed to process request",
259
+ " at CoreService.process (/app/service.js:123:45)",
260
+ " at async Router.handle (/app/router.js:67:89)"
261
+ ],
262
+ "additionalContext": {
263
+ "requestId": "abc-123",
264
+ "userId": "user_456"
265
+ }
266
+ }
267
+ ```
268
+
269
+ #### Error Handling
270
+
271
+ ```typescript
272
+ // Axios error handling
273
+ try {
274
+ await apiRequest();
275
+ } catch (error) {
276
+ logger.logErrorResponse(error, "API Request failed", {
277
+ endpoint: "/users",
278
+ method: "POST"
279
+ });
280
+ }
281
+ ```
282
+
283
+ // Will output detailed API error info:
284
+
285
+ ```
286
+ {
287
+ "timestamp": "30/01/2025, 04:34:49",
288
+ "level": "error",
289
+ "context": "ApiService",
290
+ "message": "API Request failed - API Error:",
291
+ "status": 400,
292
+ "statusText": "Bad Request",
293
+ "data": { "error": "Invalid input" },
294
+ "url": "https://api.example.com/users",
295
+ "method": "POST",
296
+ "endpoint": "/users"
297
+ }
298
+ ```
299
+
300
+ #### Using with Frameworks
301
+
302
+ The logger is framework-agnostic but can be easily integrated with any framework:
303
+
304
+ ```typescript
305
+ // NestJS example
306
+ const app = await NestFactory.create(AppModule, {
307
+ logger: GreenApiLogger.getInstance("NestJS")
308
+ });
309
+
310
+ // Express example
311
+ app.use((err, req, res, next) => {
312
+ const logger = GreenApiLogger.getInstance("Express");
313
+ logger.error("Request failed", {
314
+ path: req.path,
315
+ method: req.method,
316
+ error: err.message
317
+ });
318
+ next(err);
319
+ });
320
+ ```
321
+
322
+ #### Methods
323
+
324
+ ##### Basic Logging Methods
325
+
326
+ - `debug(message: string, context?: Record<string, any>)`: Log debug level message
327
+ - `info(message: string, context?: Record<string, any>)`: Log info level message
328
+ - `warn(message: string, context?: Record<string, any>)`: Log warning level message
329
+ - `error(message: string, context?: Record<string, any>)`: Log error level message
330
+ - `fatal(message: string, context?: Record<string, any>)`: Log fatal level message
331
+ - `log(message: string, context?: string)`: Alternative to info method
332
+
333
+ ##### Special Methods
334
+
335
+ - `logErrorResponse(error: any, context: string, additionalContext?: Record<string, any>)`:
336
+ Enhanced error logging with special handling for Axios errors and stack traces
337
+
338
+ ##### Utility Methods
339
+
340
+ - `getInstance(context: string = "Global"): GreenApiLogger`: Get or create logger instance for specified context
341
+
342
+ #### Best Practices
343
+
344
+ 1. **Use Consistent Context Names**
345
+
346
+ ```typescript
347
+ // In your component/service
348
+ private readonly logger = GreenApiLogger.getInstance(YourService.name);
349
+ ```
350
+
351
+ 2. **Include Relevant Context**
352
+
353
+ ```typescript
354
+ logger.info("User action completed", {
355
+ userId: user.id,
356
+ action: "profile_update",
357
+ duration: timeTaken
358
+ });
359
+ ```
360
+
361
+ 3. **Proper Error Handling**
362
+
363
+ ```typescript
364
+ try {
365
+ await complexOperation();
366
+ } catch (error) {
367
+ logger.logErrorResponse(error, "Complex operation failed", {
368
+ operationId: id,
369
+ parameters: params
370
+ });
371
+ }
372
+ ```
373
+
374
+ 4. **Use Appropriate Log Levels**
375
+
376
+ ```typescript
377
+ // Debug for detailed information
378
+ logger.debug("Processing chunk", {chunkId: 123, size: 1024});
379
+
380
+ // Info for general operation
381
+ logger.info("User logged in", {userId: 456});
382
+
383
+ // Warn for potential issues
384
+ logger.warn("High memory usage", {memoryUsage: "85%"});
385
+
386
+ // Error for actual problems
387
+ logger.error("Database connection failed", {dbHost: "primary"});
388
+
389
+ // Fatal for critical issues
390
+ logger.fatal("System shutdown required", {reason: "data corruption"});
391
+ ```
392
+
393
+ ### 6. GreenApiClient
197
394
 
198
395
  Direct interface to GREEN-API methods.
199
396
 
@@ -324,12 +521,16 @@ export class YourStorage extends StorageProvider {
324
521
  this.db = new PrismaClient();
325
522
  }
326
523
 
327
- async createInstance(instance: Instance, userId: bigint) {
524
+ async findUserByEmail(email: string) {
525
+ return this.db.user.findUnique({where: {email}});
526
+ }
527
+
528
+ async createInstance(instance: Instance) {
328
529
  return this.db.instance.create({
329
530
  data: {
330
531
  idInstance: instance.idInstance,
331
532
  apiTokenInstance: instance.apiTokenInstance,
332
- userId,
533
+ userId: instance.userId,
333
534
  settings: instance.settings || {},
334
535
  },
335
536
  });
@@ -427,6 +628,7 @@ router.post('/instance', async (req, res) => {
427
628
  throw new BadRequestError('Required fields missing');
428
629
  }
429
630
 
631
+ const user = await storage.findUserByEmail(userEmail);
430
632
  const instance = await adapter.createInstance({
431
633
  idInstance: Number(idInstance),
432
634
  apiTokenInstance,
@@ -434,8 +636,9 @@ router.post('/instance', async (req, res) => {
434
636
  webhookUrl: `${process.env.APP_URL}/webhook/green-api`,
435
637
  webhookUrlToken: `token_${Date.now()}`,
436
638
  incomingWebhook: 'yes'
437
- }
438
- }, userEmail);
639
+ },
640
+ userId: user.id
641
+ });
439
642
 
440
643
  res.status(200).json({
441
644
  status: 'ok',
@@ -845,10 +1048,10 @@ isValidSettingValue('webhookUrl', 'https://example.com') // Returns true
845
1048
 
846
1049
  // Clean settings
847
1050
  const input = {
848
- webhookUrl: 'https://example.com',
849
- outgoingWebhook: 'yes',
850
- invalidKey: 'value',
851
- delaySendMessagesMilliseconds: 'invalid'
1051
+ webhookUrl: 'https://example.com',
1052
+ outgoingWebhook: 'yes',
1053
+ invalidKey: 'value',
1054
+ delaySendMessagesMilliseconds: 'invalid'
852
1055
  }
853
1056
  validateAndCleanSettings(input) // Returns { webhookUrl: 'https://example.com', outgoingWebhook: 'yes' }
854
1057
  ```
package/README.ru.md CHANGED
@@ -192,10 +192,202 @@ app.post('/webhook', async (req, res) => {
192
192
  });
193
193
  ```
194
194
 
195
- ### 5. GreenApiClient
195
+ ### 5. GreenApiLogger
196
196
 
197
- Прямой интерфейс к эндпоинтам GREEN-API. Хотя большинство операций должны выполняться через BaseAdapter, GreenApiClient
198
- может использоваться напрямую для операций, непокрытых в `BaseAdapter`.
197
+ Структурированный JSON-логгер с цветным выводом. Обеспечивает единый формат логирования в вашем приложении с корректной
198
+ обработкой ошибок и сериализацией.
199
+
200
+ ```typescript
201
+ const logger = GreenApiLogger.getInstance("YourComponent");
202
+
203
+ // Базовое логирование
204
+ logger.debug("Debug message", {someContext: "value"});
205
+ logger.info("Info message", {userId: 123});
206
+ logger.warn("Warning message", {alert: true});
207
+ logger.error("Error occurred", {errorCode: 500});
208
+ logger.fatal("Fatal error", {critical: true});
209
+
210
+ // Логирование ошибок с контекстом
211
+ try {
212
+ await someOperation();
213
+ } catch (error) {
214
+ logger.logErrorResponse(error, "Operation failed", {
215
+ operationId: "123",
216
+ additionalInfo: "some context"
217
+ });
218
+ }
219
+ ```
220
+
221
+ #### Возможности
222
+
223
+ - Структурированное JSON-логирование в едином формате
224
+ - Цветной вывод в зависимости от уровня лога (debug=голубой, info=зеленый, warn=желтый, error=красный, fatal=пурпурный)
225
+ - Встроенная обработка ошибок с форматированием стека вызовов
226
+ - Автоматическая сериализация
227
+ - Независимость от фреймворка - работает с любым Node.js приложением
228
+ - Специальная обработка ошибок Axios с подробной информацией о запросе/ответе
229
+
230
+ #### Уровни логирования
231
+
232
+ - `debug` - Подробная информация для отладки
233
+ - `info` - Общая информация о работе системы
234
+ - `warn` - Предупреждения о потенциально опасных ситуациях
235
+ - `error` - Сообщения об ошибках
236
+ - `fatal` - Критические ошибки, требующие немедленного внимания
237
+ - `log` - Альтернатива info (для совместимости)
238
+
239
+ #### Формат вывода
240
+
241
+ ```json
242
+ {
243
+ "timestamp": "30/01/2025, 04:34:49",
244
+ "level": "error",
245
+ "context": "CoreService",
246
+ "message": "Operation failed",
247
+ "error": "Failed to process request",
248
+ "stack": [
249
+ "Error: Failed to process request",
250
+ " at CoreService.process (/app/service.js:123:45)",
251
+ " at async Router.handle (/app/router.js:67:89)"
252
+ ],
253
+ "additionalContext": {
254
+ "requestId": "abc-123",
255
+ "userId": "user_456"
256
+ }
257
+ }
258
+ ```
259
+
260
+ #### Обработка ошибок
261
+
262
+ ```typescript
263
+ // Обработка ошибок Axios
264
+ try {
265
+ await apiRequest();
266
+ } catch (error) {
267
+ logger.logErrorResponse(error, "API Request failed", {
268
+ endpoint: "/users",
269
+ method: "POST"
270
+ });
271
+ }
272
+ ```
273
+
274
+ // Получим подробную информацию об ошибке API:
275
+
276
+ ```json
277
+ {
278
+ "timestamp": "30/01/2025, 04:34:49",
279
+ "level": "error",
280
+ "context": "ApiService",
281
+ "message": "API Request failed - API Error:",
282
+ "status": 400,
283
+ "statusText": "Bad Request",
284
+ "data": {
285
+ "error": "Invalid input"
286
+ },
287
+ "url": "https://api.example.com/users",
288
+ "method": "POST",
289
+ "endpoint": "/users"
290
+ }
291
+ ```
292
+
293
+ #### Использование с фреймворками
294
+
295
+ Логгер независим от фреймворков, но легко интегрируется с любым из них:
296
+
297
+ ```typescript
298
+ // Пример с NestJS
299
+ const app = await NestFactory.create(AppModule, {
300
+ logger: GreenApiLogger.getInstance("NestJS")
301
+ });
302
+
303
+ // Пример с Express
304
+ app.use((err, req, res, next) => {
305
+ const logger = GreenApiLogger.getInstance("Express");
306
+ logger.error("Request failed", {
307
+ path: req.path,
308
+ method: req.method,
309
+ error: err.message
310
+ });
311
+ next(err);
312
+ });
313
+ ```
314
+
315
+ #### Методы
316
+
317
+ ##### Основные методы логирования
318
+
319
+ - `debug(message: string, context?: Record<string, any>)`: Логирование отладочной информации
320
+ - `info(message: string, context?: Record<string, any>)`: Логирование информационных сообщений
321
+ - `warn(message: string, context?: Record<string, any>)`: Логирование предупреждений
322
+ - `error(message: string, context?: Record<string, any>)`: Логирование ошибок
323
+ - `fatal(message: string, context?: Record<string, any>)`: Логирование критических ошибок
324
+ - `log(message: string, context?: string)`: Альтернатива методу info
325
+
326
+ ##### Специальные методы
327
+
328
+ - `logErrorResponse(error: any, context: string, additionalContext?: Record<string, any>)`:
329
+ Расширенное логирование ошибок со специальной обработкой ошибок Axios и стека вызовов
330
+
331
+ ##### Вспомогательные методы
332
+
333
+ - `getInstance(context: string = "Global"): GreenApiLogger`: Получение или создание экземпляра логгера для указанного
334
+ контекста
335
+
336
+ #### Лучшие практики
337
+
338
+ 1. **Используйте последовательные имена контекста**
339
+
340
+ ```typescript
341
+ // В вашем компоненте/сервисе
342
+ private readonly
343
+ logger = GreenApiLogger.getInstance(YourService.name);
344
+ ```
345
+
346
+ 2. **Включайте релевантный контекст**
347
+
348
+ ```typescript
349
+ logger.info("User action completed", {
350
+ userId: user.id,
351
+ action: "profile_update",
352
+ duration: timeTaken
353
+ });
354
+ ```
355
+
356
+ 3. **Правильная обработка ошибок**
357
+
358
+ ```typescript
359
+ try {
360
+ await complexOperation();
361
+ } catch (error) {
362
+ logger.logErrorResponse(error, "Complex operation failed", {
363
+ operationId: id,
364
+ parameters: params
365
+ });
366
+ }
367
+ ```
368
+
369
+ 4. **Используйте соответствующие уровни логирования**
370
+
371
+ ```typescript
372
+ // Debug для детальной информации
373
+ logger.debug("Processing chunk", {chunkId: 123, size: 1024});
374
+
375
+ // Info для общей информации о работе
376
+ logger.info("User logged in", {userId: 456});
377
+
378
+ // Warn для потенциальных проблем
379
+ logger.warn("High memory usage", {memoryUsage: "85%"});
380
+
381
+ // Error для реальных проблем
382
+ logger.error("Database connection failed", {dbHost: "primary"});
383
+
384
+ // Fatal для критических проблем
385
+ logger.fatal("System shutdown required", {reason: "data corruption"});
386
+ ```
387
+
388
+ ### 6. GreenApiClient
389
+
390
+ Прямой интерфейс к методам GREEN-API.
199
391
 
200
392
  ```typescript
201
393
  const client = new GreenApiClient({
@@ -209,7 +401,7 @@ await client.getAuthorizationCode(phoneNumber);
209
401
  await client.getQR();
210
402
  ```
211
403
 
212
- ## Руководство разработчика
404
+ ## Руководство для разработчика
213
405
 
214
406
  Это руководство проведет вас через процесс создания вашей первой интеграции с WhatsApp шлюзом GREEN-API.
215
407
 
@@ -615,7 +807,13 @@ interface SimplePlatformMessage {
615
807
  ### simple-transformer.ts
616
808
 
617
809
  ```typescript
618
- import { MessageTransformer, Message, GreenApiWebhook, formatPhoneNumber, IntegrationError } from '@green-api/greenapi-integration';
810
+ import {
811
+ MessageTransformer,
812
+ Message,
813
+ GreenApiWebhook,
814
+ formatPhoneNumber,
815
+ IntegrationError
816
+ } from '@green-api/greenapi-integration';
619
817
 
620
818
  export class SimpleTransformer extends MessageTransformer<SimplePlatformWebhook, SimplePlatformMessage> {
621
819
  toPlatformMessage(webhook: GreenApiWebhook): SimplePlatformMessage {
@@ -2,6 +2,7 @@ import { GreenApiClient } from "./green-api.client";
2
2
  import { MessageTransformer } from "./message-transformer";
3
3
  import { BaseUser, ForwardMessagesResponse, GreenApiWebhook, Instance, SendResponse, StateInstanceWebhook, WebhookType } from "../types/types";
4
4
  import { StorageProvider } from "./storage-provider";
5
+ import { GreenApiLogger } from "./logger";
5
6
  /**
6
7
  * Base adapter for platform integrations with GREEN-API.
7
8
  * This class handles the core integration logic between your platform and GREEN-API's WhatsApp gateway.
@@ -29,13 +30,14 @@ import { StorageProvider } from "./storage-provider";
29
30
  export declare abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TUser extends BaseUser = BaseUser, TInstance extends Instance = Instance> {
30
31
  protected transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>;
31
32
  protected storage: StorageProvider<TUser, TInstance>;
33
+ protected readonly gaLogger: GreenApiLogger;
32
34
  /**
33
35
  * Creates an instance of BaseAdapter.
34
36
  *
35
37
  * @param transformer - Message transformer for converting between platform and GREEN-API formats
36
38
  * @param storage - Storage provider for user and instance data
37
39
  */
38
- constructor(transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>, storage: StorageProvider<TUser, TInstance>);
40
+ protected constructor(transformer: MessageTransformer<TPlatformWebhook, TPlatformMessage>, storage: StorageProvider<TUser, TInstance>);
39
41
  /**
40
42
  * Sends a message to your platform. This method must be implemented to define how
41
43
  * messages are sent to your specific platform.
@@ -120,12 +122,11 @@ export declare abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TU
120
122
  * Creates a new instance with specified settings.
121
123
  *
122
124
  * @param instance - The instance configuration
123
- * @param userCred - User credentials
124
125
  * @returns Promise resolving to the created instance
125
126
  * @throws {NotFoundError} If user is not found
126
127
  * @throws {IntegrationError} If instance creation fails
127
128
  */
128
- createInstance(instance: Instance, userCred: any): Promise<TInstance>;
129
+ createInstance(instance: Instance): Promise<TInstance>;
129
130
  /**
130
131
  * Removes an instance by ID.
131
132
  *
@@ -157,8 +158,6 @@ export declare abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TU
157
158
  *
158
159
  * @param userCred - User credentials
159
160
  * @param data - User data
160
- * @throws {BadRequestError} If user already exists
161
- * @throws {IntegrationError} If creation fails
162
161
  */
163
162
  createUser(userCred: any, data: any): Promise<TUser>;
164
163
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseAdapter = void 0;
4
4
  const green_api_client_1 = require("./green-api.client");
5
5
  const errors_1 = require("./errors");
6
+ const logger_1 = require("./logger");
6
7
  /**
7
8
  * Base adapter for platform integrations with GREEN-API.
8
9
  * This class handles the core integration logic between your platform and GREEN-API's WhatsApp gateway.
@@ -37,6 +38,7 @@ class BaseAdapter {
37
38
  constructor(transformer, storage) {
38
39
  this.transformer = transformer;
39
40
  this.storage = storage;
41
+ this.gaLogger = logger_1.GreenApiLogger.getInstance(this.constructor.name);
40
42
  }
41
43
  /**
42
44
  * Handles instance state change webhooks from GREEN-API.
@@ -86,6 +88,7 @@ class BaseAdapter {
86
88
  */
87
89
  async handlePlatformWebhook(message, idInstance) {
88
90
  try {
91
+ this.gaLogger.info("Handling platform webhook", { platformWebhook: message, idInstance });
89
92
  const instance = await this.storage.getInstance(idInstance);
90
93
  if (!instance) {
91
94
  throw new errors_1.IntegrationError("Instance not found", "INSTANCE_NOT_FOUND", 404);
@@ -124,10 +127,15 @@ class BaseAdapter {
124
127
  * @throws {IntegrationError} If webhook handling fails
125
128
  */
126
129
  async handleGreenApiWebhook(webhook, allowedTypes) {
127
- if (!allowedTypes.includes(webhook.typeWebhook)) {
128
- return;
129
- }
130
130
  try {
131
+ this.gaLogger.info("Handling GREEN-API webhook", { webhook, allowedTypes });
132
+ if (!allowedTypes.includes(webhook.typeWebhook)) {
133
+ this.gaLogger.warn(`Skipping GREEN-API webhook because the ${webhook.typeWebhook} is not allowed`, {
134
+ webhook,
135
+ allowedTypes,
136
+ });
137
+ return;
138
+ }
131
139
  if (webhook.typeWebhook === "stateInstanceChanged") {
132
140
  await this.handleStateInstanceWebhook(webhook);
133
141
  }
@@ -148,17 +156,12 @@ class BaseAdapter {
148
156
  * Creates a new instance with specified settings.
149
157
  *
150
158
  * @param instance - The instance configuration
151
- * @param userCred - User credentials
152
159
  * @returns Promise resolving to the created instance
153
160
  * @throws {NotFoundError} If user is not found
154
161
  * @throws {IntegrationError} If instance creation fails
155
162
  */
156
- async createInstance(instance, userCred) {
163
+ async createInstance(instance) {
157
164
  try {
158
- const user = await this.storage.findUser(userCred);
159
- if (!user) {
160
- throw new errors_1.NotFoundError("No user with such credentials");
161
- }
162
165
  const client = this.createGreenApiClient(instance);
163
166
  try {
164
167
  await client.getSettings();
@@ -166,7 +169,7 @@ class BaseAdapter {
166
169
  catch (error) {
167
170
  throw new errors_1.IntegrationError(`Failed to get settings for instance ${instance.idInstance}: ${error.message}`, "INTEGRATION_ERROR");
168
171
  }
169
- const createdInstance = await this.storage.createInstance(instance, user.id);
172
+ const createdInstance = await this.storage.createInstance(instance);
170
173
  if (instance.settings) {
171
174
  await client.setSettings(instance.settings);
172
175
  }
@@ -185,10 +188,6 @@ class BaseAdapter {
185
188
  */
186
189
  async removeInstance(idInstance) {
187
190
  try {
188
- const instance = await this.storage.getInstance(idInstance);
189
- if (!instance) {
190
- throw new errors_1.NotFoundError("No instance with such ID");
191
- }
192
191
  return this.storage.removeInstance(idInstance);
193
192
  }
194
193
  catch (error) {
@@ -232,14 +231,8 @@ class BaseAdapter {
232
231
  *
233
232
  * @param userCred - User credentials
234
233
  * @param data - User data
235
- * @throws {BadRequestError} If user already exists
236
- * @throws {IntegrationError} If creation fails
237
234
  */
238
235
  async createUser(userCred, data) {
239
- const existingUser = await this.storage.findUser(userCred);
240
- if (existingUser) {
241
- throw new errors_1.BadRequestError("User already created");
242
- }
243
236
  try {
244
237
  return this.storage.createUser(data);
245
238
  }
@@ -23,6 +23,7 @@ export declare class GreenApiClient {
23
23
  private instance;
24
24
  private client;
25
25
  private readonly baseUrl;
26
+ private readonly gaLogger;
26
27
  /**
27
28
  * Creates a GREEN-API client instance.
28
29
  *
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GreenApiClient = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
+ const errors_1 = require("./errors");
9
+ const logger_1 = require("./logger");
8
10
  /**
9
11
  * Client for direct interaction with GREEN-API's WhatsApp gateway.
10
12
  * Provides methods for sending messages, managing instances, and handling files.
@@ -34,6 +36,7 @@ class GreenApiClient {
34
36
  constructor(instance) {
35
37
  this.instance = instance;
36
38
  this.baseUrl = "https://api.green-api.com";
39
+ this.gaLogger = logger_1.GreenApiLogger.getInstance(GreenApiClient.name);
37
40
  this.client = axios_1.default.create({
38
41
  baseURL: this.buildUrl(),
39
42
  });
@@ -46,6 +49,14 @@ class GreenApiClient {
46
49
  }
47
50
  async makeRequest(method, endpoint, data, queryParams, config) {
48
51
  try {
52
+ this.gaLogger.info("Making a request", {
53
+ idInstance: this.instance.idInstance,
54
+ endpoint,
55
+ method,
56
+ data,
57
+ queryParams,
58
+ config,
59
+ });
49
60
  const url = this.buildEndpoint(endpoint) + (queryParams ? "?" + new URLSearchParams(Object.entries(queryParams).map(([key, value]) => [key, value.toString()])).toString() : "");
50
61
  const response = await (method === "get"
51
62
  ? this.client.get(url, config)
@@ -53,7 +64,7 @@ class GreenApiClient {
53
64
  return response.data;
54
65
  }
55
66
  catch (error) {
56
- throw new Error(`Failed to ${endpoint.replace(/([A-Z])/g, " $1").toLowerCase()}: ${error.message}. ${JSON.stringify(error.response?.data)}`);
67
+ throw new errors_1.IntegrationError(`Failed to ${endpoint.replace(/([A-Z])/g, " $1").toLowerCase()}: ${error.message}. ${JSON.stringify(error.response?.data)}`, "INTEGRATION_ERROR");
57
68
  }
58
69
  }
59
70
  async makeFileUploadRequest(endpoint, formData, headers) {
@@ -31,12 +31,13 @@ import { StorageProvider } from "./storage-provider";
31
31
  */
32
32
  export declare abstract class BaseGreenApiAuthGuard<T extends BaseRequest = BaseRequest> {
33
33
  protected storage: StorageProvider;
34
+ private readonly gaLogger;
34
35
  /**
35
36
  * Creates an instance of BaseGreenApiAuthGuard.
36
37
  *
37
38
  * @param storage - Storage provider for accessing instance data
38
39
  */
39
- constructor(storage: StorageProvider);
40
+ protected constructor(storage: StorageProvider);
40
41
  /**
41
42
  * Validates an incoming webhook request.
42
43
  * Checks for presence of authorization token and validates it against instance settings.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseGreenApiAuthGuard = void 0;
4
4
  const errors_1 = require("./errors");
5
+ const logger_1 = require("./logger");
5
6
  /**
6
7
  * Base authentication guard for validating incoming GREEN-API webhooks.
7
8
  * Ensures that webhooks are authenticated and come from valid instances.
@@ -39,6 +40,7 @@ class BaseGreenApiAuthGuard {
39
40
  */
40
41
  constructor(storage) {
41
42
  this.storage = storage;
43
+ this.gaLogger = logger_1.GreenApiLogger.getInstance(this.constructor.name);
42
44
  }
43
45
  /**
44
46
  * Validates an incoming webhook request.
@@ -56,13 +58,14 @@ class BaseGreenApiAuthGuard {
56
58
  if (!token) {
57
59
  throw new errors_1.AuthenticationError("Authentication header is missing");
58
60
  }
61
+ this.gaLogger.info("Request from GREEN-API", { body: request.body });
59
62
  const idInstance = request.body?.instanceData?.idInstance;
60
63
  if (!idInstance) {
61
64
  throw new errors_1.AuthenticationError("Invalid webhook format");
62
65
  }
63
66
  const instance = await this.storage.getInstance(idInstance);
64
67
  if (!instance) {
65
- throw new errors_1.AuthenticationError("No instance with such ID");
68
+ throw new errors_1.AuthenticationError(`No instance with such ID ${idInstance}`);
66
69
  }
67
70
  if (instance.settings?.webhookUrlToken !== token.split(" ")[1]) {
68
71
  throw new errors_1.AuthenticationError("Invalid token");
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Logger for GREEN-API integration library.
3
+ * Provides structured JSON logging with colored output and error handling.
4
+ * Uses singleton pattern to maintain consistent logging instances across the application.
5
+ *
6
+ * @category Core
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const logger = GreenApiLogger.getInstance("MyComponent");
11
+ *
12
+ * // Basic logging
13
+ * logger.info("Operation successful", { userId: 123 });
14
+ *
15
+ * // Error logging
16
+ * try {
17
+ * // ... some code
18
+ * } catch (error) {
19
+ * logger.logErrorResponse(error, "Failed to process request");
20
+ * }
21
+ * ```
22
+ */
23
+ export declare class GreenApiLogger {
24
+ private readonly context;
25
+ private static instances;
26
+ private readonly colors;
27
+ /**
28
+ * Private constructor to enforce singleton pattern
29
+ * @param context - The context (usually component name) for this logger instance
30
+ */
31
+ private constructor();
32
+ /**
33
+ * Gets a logger instance for the specified context.
34
+ * Creates a new instance if one doesn't exist, otherwise returns existing instance.
35
+ *
36
+ * @param context - The context for the logger (default: "Global")
37
+ * @returns Logger instance for the specified context
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const logger = GreenApiLogger.getInstance("MyService");
42
+ * ```
43
+ */
44
+ static getInstance(context?: string): GreenApiLogger;
45
+ /**
46
+ * Formats timestamp in locale-specific format
47
+ * @returns Formatted timestamp string
48
+ * @private
49
+ */
50
+ private formatTimestamp;
51
+ /**
52
+ * Sanitizes values for JSON serialization.
53
+ * Handles special cases like Error objects and BigInt values.
54
+ *
55
+ * @param value - Value to sanitize
56
+ * @returns Sanitized value safe for JSON serialization
57
+ * @private
58
+ */
59
+ private sanitizeValue;
60
+ /**
61
+ * Creates and outputs a log entry
62
+ * @param level - Log level
63
+ * @param message - Log message
64
+ * @param additionalContext - Additional context data
65
+ * @private
66
+ */
67
+ private logEntry;
68
+ /**
69
+ * Logs a debug message
70
+ * @param message - Debug message
71
+ * @param context - Additional context data
72
+ */
73
+ debug(message: string, context?: Record<string, any>): void;
74
+ /**
75
+ * Logs an info message
76
+ * @param message - Info message
77
+ * @param context - Additional context data
78
+ */
79
+ info(message: string, context?: Record<string, any>): void;
80
+ /**
81
+ * Logs a warning message
82
+ * @param message - Warning message
83
+ * @param context - Additional context data
84
+ */
85
+ warn(message: string, context?: Record<string, any>): void;
86
+ /**
87
+ * Logs an error message
88
+ * @param message - Error message
89
+ * @param context - Additional context data
90
+ */
91
+ error(message: string, context?: Record<string, any>): void;
92
+ /**
93
+ * Alternative method for logging info messages
94
+ * @param message - Log message
95
+ * @param context - Context string
96
+ */
97
+ log(message: string, context?: string): void;
98
+ /**
99
+ * Logs a verbose debug message
100
+ * @param message - Debug message
101
+ * @param context - Context string
102
+ */
103
+ verbose(message: string, context?: string): void;
104
+ /**
105
+ * Logs a fatal error message
106
+ * @param message - Fatal error message
107
+ * @param context - Additional context data
108
+ */
109
+ fatal(message: string, context?: Record<string, any>): void;
110
+ /**
111
+ * Logs detailed error information, handling both Axios errors and regular errors.
112
+ * Particularly useful for API errors and exceptions.
113
+ *
114
+ * @param error - Error object (Axios error or regular error)
115
+ * @param context - Error context description
116
+ * @param additionalContext - Additional context data
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * try {
121
+ * await api.request();
122
+ * } catch (error) {
123
+ * logger.logErrorResponse(error, "API Request failed", { requestId: "123" });
124
+ * }
125
+ * ```
126
+ */
127
+ logErrorResponse(error: any, context: string, additionalContext?: Record<string, any>): void;
128
+ }
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GreenApiLogger = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ /**
9
+ * Logger for GREEN-API integration library.
10
+ * Provides structured JSON logging with colored output and error handling.
11
+ * Uses singleton pattern to maintain consistent logging instances across the application.
12
+ *
13
+ * @category Core
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const logger = GreenApiLogger.getInstance("MyComponent");
18
+ *
19
+ * // Basic logging
20
+ * logger.info("Operation successful", { userId: 123 });
21
+ *
22
+ * // Error logging
23
+ * try {
24
+ * // ... some code
25
+ * } catch (error) {
26
+ * logger.logErrorResponse(error, "Failed to process request");
27
+ * }
28
+ * ```
29
+ */
30
+ class GreenApiLogger {
31
+ /**
32
+ * Private constructor to enforce singleton pattern
33
+ * @param context - The context (usually component name) for this logger instance
34
+ */
35
+ constructor(context) {
36
+ this.context = context;
37
+ this.colors = {
38
+ log: "\x1b[32m", // Same as info (green)
39
+ debug: "\x1b[36m", // Cyan
40
+ info: "\x1b[32m", // Green
41
+ warn: "\x1b[33m", // Yellow
42
+ error: "\x1b[31m", // Red
43
+ fatal: "\x1b[35m", // Magenta/Purple for fatal
44
+ reset: "\x1b[0m", // Reset
45
+ };
46
+ }
47
+ /**
48
+ * Gets a logger instance for the specified context.
49
+ * Creates a new instance if one doesn't exist, otherwise returns existing instance.
50
+ *
51
+ * @param context - The context for the logger (default: "Global")
52
+ * @returns Logger instance for the specified context
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const logger = GreenApiLogger.getInstance("MyService");
57
+ * ```
58
+ */
59
+ static getInstance(context = "Global") {
60
+ if (!GreenApiLogger.instances.has(context)) {
61
+ GreenApiLogger.instances.set(context, new GreenApiLogger(context));
62
+ }
63
+ return GreenApiLogger.instances.get(context);
64
+ }
65
+ /**
66
+ * Formats timestamp in locale-specific format
67
+ * @returns Formatted timestamp string
68
+ * @private
69
+ */
70
+ formatTimestamp() {
71
+ return new Date().toLocaleString("en-GB");
72
+ }
73
+ /**
74
+ * Sanitizes values for JSON serialization.
75
+ * Handles special cases like Error objects and BigInt values.
76
+ *
77
+ * @param value - Value to sanitize
78
+ * @returns Sanitized value safe for JSON serialization
79
+ * @private
80
+ */
81
+ sanitizeValue(value) {
82
+ if (value instanceof Error) {
83
+ return {
84
+ message: value.message,
85
+ stack: value.stack?.split("\n").map(line => line.trim()),
86
+ };
87
+ }
88
+ if (typeof value === "bigint") {
89
+ return value.toString();
90
+ }
91
+ if (Array.isArray(value)) {
92
+ return value.map(item => this.sanitizeValue(item));
93
+ }
94
+ if (value && typeof value === "object") {
95
+ return Object.fromEntries(Object.entries(value).map(([key, val]) => [key, this.sanitizeValue(val)]));
96
+ }
97
+ return value;
98
+ }
99
+ /**
100
+ * Creates and outputs a log entry
101
+ * @param level - Log level
102
+ * @param message - Log message
103
+ * @param additionalContext - Additional context data
104
+ * @private
105
+ */
106
+ logEntry(level, message, additionalContext = {}) {
107
+ const entry = {
108
+ timestamp: this.formatTimestamp(),
109
+ level,
110
+ context: this.context,
111
+ message,
112
+ ...this.sanitizeValue(additionalContext),
113
+ };
114
+ const color = this.colors[level];
115
+ const jsonString = JSON.stringify(entry);
116
+ console.log(`${color}${jsonString}${this.colors.reset}`);
117
+ }
118
+ /**
119
+ * Logs a debug message
120
+ * @param message - Debug message
121
+ * @param context - Additional context data
122
+ */
123
+ debug(message, context = {}) {
124
+ this.logEntry("debug", message, context);
125
+ }
126
+ /**
127
+ * Logs an info message
128
+ * @param message - Info message
129
+ * @param context - Additional context data
130
+ */
131
+ info(message, context = {}) {
132
+ this.logEntry("info", message, context);
133
+ }
134
+ /**
135
+ * Logs a warning message
136
+ * @param message - Warning message
137
+ * @param context - Additional context data
138
+ */
139
+ warn(message, context = {}) {
140
+ this.logEntry("warn", message, context);
141
+ }
142
+ /**
143
+ * Logs an error message
144
+ * @param message - Error message
145
+ * @param context - Additional context data
146
+ */
147
+ error(message, context = {}) {
148
+ this.logEntry("error", message, context);
149
+ }
150
+ /**
151
+ * Alternative method for logging info messages
152
+ * @param message - Log message
153
+ * @param context - Context string
154
+ */
155
+ log(message, context) {
156
+ this.logEntry("info", message, { context });
157
+ }
158
+ /**
159
+ * Logs a verbose debug message
160
+ * @param message - Debug message
161
+ * @param context - Context string
162
+ */
163
+ verbose(message, context) {
164
+ this.logEntry("debug", message, { context, level: "verbose" });
165
+ }
166
+ /**
167
+ * Logs a fatal error message
168
+ * @param message - Fatal error message
169
+ * @param context - Additional context data
170
+ */
171
+ fatal(message, context = {}) {
172
+ this.logEntry("error", message, context);
173
+ }
174
+ /**
175
+ * Logs detailed error information, handling both Axios errors and regular errors.
176
+ * Particularly useful for API errors and exceptions.
177
+ *
178
+ * @param error - Error object (Axios error or regular error)
179
+ * @param context - Error context description
180
+ * @param additionalContext - Additional context data
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * try {
185
+ * await api.request();
186
+ * } catch (error) {
187
+ * logger.logErrorResponse(error, "API Request failed", { requestId: "123" });
188
+ * }
189
+ * ```
190
+ */
191
+ logErrorResponse(error, context, additionalContext = {}) {
192
+ if (axios_1.default.isAxiosError(error)) {
193
+ const axiosError = error;
194
+ this.error(`${context} - API Error:`, {
195
+ status: axiosError.response?.status,
196
+ statusText: axiosError.response?.statusText,
197
+ data: axiosError.response?.data,
198
+ url: axiosError.config?.url,
199
+ method: axiosError.config?.method,
200
+ headers: axiosError.response?.headers,
201
+ timestamp: new Date().toISOString(),
202
+ ...additionalContext,
203
+ });
204
+ }
205
+ else {
206
+ const errorObject = {
207
+ message: error instanceof Error ? error.message : String(error),
208
+ stack: error instanceof Error ? error.stack?.split("\n").map(line => line.trim()) : undefined,
209
+ timestamp: new Date().toISOString(),
210
+ ...additionalContext,
211
+ };
212
+ this.error(`${context} - Non-API Error:`, errorObject);
213
+ }
214
+ }
215
+ }
216
+ exports.GreenApiLogger = GreenApiLogger;
217
+ GreenApiLogger.instances = new Map();
@@ -25,10 +25,9 @@ export declare abstract class StorageProvider<TUser extends BaseUser = BaseUser,
25
25
  * Creates a new instance in storage.
26
26
  *
27
27
  * @param instance - The instance data to store
28
- * @param userId - ID of the user who owns this instance
29
28
  * @returns Promise resolving to the created instance
30
29
  */
31
- abstract createInstance(instance: Instance, userId: bigint | number): Promise<TInstance>;
30
+ abstract createInstance(instance: Instance): Promise<TInstance>;
32
31
  /**
33
32
  * Retrieves an instance by its ID.
34
33
  *
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export { GreenApiClient } from "./core/green-api.client";
4
4
  export { MessageTransformer } from "./core/message-transformer";
5
5
  export { BaseGreenApiAuthGuard } from "./core/guard";
6
6
  export { StorageProvider } from "./core/storage-provider";
7
+ export { GreenApiLogger } from "./core/logger";
7
8
  export * from "./utils/helpers";
8
9
  export * from "./core/errors";
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.StorageProvider = exports.BaseGreenApiAuthGuard = exports.MessageTransformer = exports.GreenApiClient = exports.BaseAdapter = void 0;
17
+ exports.GreenApiLogger = exports.StorageProvider = exports.BaseGreenApiAuthGuard = exports.MessageTransformer = exports.GreenApiClient = exports.BaseAdapter = void 0;
18
18
  __exportStar(require("./types/types"), exports);
19
19
  var base_adapter_1 = require("./core/base-adapter");
20
20
  Object.defineProperty(exports, "BaseAdapter", { enumerable: true, get: function () { return base_adapter_1.BaseAdapter; } });
@@ -26,5 +26,7 @@ var guard_1 = require("./core/guard");
26
26
  Object.defineProperty(exports, "BaseGreenApiAuthGuard", { enumerable: true, get: function () { return guard_1.BaseGreenApiAuthGuard; } });
27
27
  var storage_provider_1 = require("./core/storage-provider");
28
28
  Object.defineProperty(exports, "StorageProvider", { enumerable: true, get: function () { return storage_provider_1.StorageProvider; } });
29
+ var logger_1 = require("./core/logger");
30
+ Object.defineProperty(exports, "GreenApiLogger", { enumerable: true, get: function () { return logger_1.GreenApiLogger; } });
29
31
  __exportStar(require("./utils/helpers"), exports);
30
32
  __exportStar(require("./core/errors"), exports);
@@ -180,12 +180,18 @@ export interface ContactMessageData extends ForwardableMessage {
180
180
  displayName: string;
181
181
  vcard: string;
182
182
  }
183
+ export interface ContactsArrayMessageData extends ForwardableMessage {
184
+ contacts: Array<{
185
+ displayName: string;
186
+ vcard: string;
187
+ }>;
188
+ }
183
189
  export interface PollMessageData {
184
190
  name: string;
185
191
  options: PollOption[];
186
192
  multipleAnswers: boolean;
187
193
  }
188
- type QuotedMessage = {
194
+ export type QuotedMessage = {
189
195
  stanzaId: string;
190
196
  participant: string;
191
197
  typeMessage: MessageType;
@@ -198,6 +204,12 @@ type QuotedMessage = {
198
204
  displayName: string;
199
205
  vcard: string;
200
206
  };
207
+ } | {
208
+ typeMessage: "contactsArrayMessage";
209
+ contacts: Array<{
210
+ displayName: string;
211
+ vcard: string;
212
+ }>;
201
213
  } | {
202
214
  typeMessage: "locationMessage";
203
215
  location: {
@@ -274,6 +286,9 @@ export type WebhookMessageData = {
274
286
  } | {
275
287
  typeMessage: "pollUpdateMessage";
276
288
  pollMessageData: PollUpdateMessageData;
289
+ } | {
290
+ typeMessage: "contactsArrayMessage";
291
+ messageData: ContactsArrayMessageData;
277
292
  };
278
293
  export interface MessageWebhook {
279
294
  typeWebhook: "incomingMessageReceived" | "outgoingMessageReceived" | "outgoingAPIMessageReceived";
@@ -527,4 +542,3 @@ export interface BaseUser {
527
542
  id: number | bigint;
528
543
  [key: string]: any;
529
544
  }
530
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@green-api/greenapi-integration",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "GREEN-API Integration library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",