@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 +220 -17
- package/README.ru.md +203 -5
- package/dist/core/base-adapter.d.ts +4 -5
- package/dist/core/base-adapter.js +13 -20
- package/dist/core/green-api.client.d.ts +1 -0
- package/dist/core/green-api.client.js +12 -1
- package/dist/core/guard.d.ts +2 -1
- package/dist/core/guard.js +4 -1
- package/dist/core/logger.d.ts +128 -0
- package/dist/core/logger.js +217 -0
- package/dist/core/storage-provider.d.ts +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/types/types.d.ts +16 -2
- package/package.json +1 -1
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<
|
|
144
|
-
|
|
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:
|
|
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:
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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.
|
|
195
|
+
### 5. GreenApiLogger
|
|
196
196
|
|
|
197
|
-
|
|
198
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
|
@@ -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
|
|
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) {
|
package/dist/core/guard.d.ts
CHANGED
|
@@ -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.
|
package/dist/core/guard.js
CHANGED
|
@@ -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(
|
|
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
|
|
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);
|
package/dist/types/types.d.ts
CHANGED
|
@@ -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 {};
|