From 69c10596442980d8d93ec84cbfb0b345e172fd59 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 6 Jul 2023 13:55:14 -0300 Subject: [PATCH 01/15] feat: Route to send status broadcast --- CHANGELOG.md | 6 ++++ package.json | 2 +- src/validate/validate.schema.ts | 28 ++++++++++++++++ .../controllers/sendMessage.controller.ts | 5 +++ src/whatsapp/dto/sendMessage.dto.ts | 11 +++++++ src/whatsapp/routers/sendMessage.router.ts | 12 +++++++ src/whatsapp/services/whatsapp.service.ts | 33 +++++++++++++++++-- 7 files changed, 94 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7061bc15..06c8e4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.1.4 (homolog) + +### Features + +* Route to send status broadcast + # 1.1.3 (2023-07-06 11:43) ### Features diff --git a/package.json b/package.json index 0e73166f..5c50a877 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,10 @@ }, "homepage": "https://github.com/DavidsonGomes/evolution-api#readme", "dependencies": { + "@whiskeysockets/baileys": "github:EvolutionAPI/Baileys", "@adiwajshing/keyed-db": "^0.2.4", "@ffmpeg-installer/ffmpeg": "^1.1.0", "@hapi/boom": "^10.0.1", - "@whiskeysockets/baileys": "^6.3.0", "axios": "^1.3.5", "class-validator": "^0.13.2", "compression": "^1.7.4", diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 66388634..9a8fae2d 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -189,6 +189,34 @@ export const pollMessageSchema: JSONSchema7 = { required: ['pollMessage', 'number'], }; +export const statusMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + statusMessage: { + type: 'object', + properties: { + text: { type: 'string' }, + backgroundColor: { type: 'string' }, + font: { type: 'integer', minimum: 0, maximum: 5 }, + statusJidList: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + pattern: '^\\d+', + description: '"statusJidList" must be an array of numeric strings', + }, + }, + }, + required: ['text', 'backgroundColor', 'font', 'statusJidList'], + ...isNotEmpty('text', 'backgroundColor', 'font', 'statusJidList'), + }, + }, + required: ['statusMessage'], +}; + export const mediaMessageSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts index b985d5e7..8e70c26c 100644 --- a/src/whatsapp/controllers/sendMessage.controller.ts +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -11,6 +11,7 @@ import { SendMediaDto, SendPollDto, SendReactionDto, + SendStatusDto, SendStickerDto, SendTextDto, } from '../dto/sendMessage.dto'; @@ -80,6 +81,10 @@ export class SendMessageController { return await this.waMonitor.waInstances[instanceName].pollMessage(data); } + public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto) { + return await this.waMonitor.waInstances[instanceName].statusMessage(data); + } + public async sendLinkPreview({ instanceName }: InstanceDto, data: SendLinkPreviewDto) { return await this.waMonitor.waInstances[instanceName].linkPreview(data); } diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index 0d3f7183..1b9708fa 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -32,6 +32,13 @@ class linkPreviewMessage { text: string; } +class StatusMessage { + text: string; + backgroundColor: string; + font: number; + statusJidList: string[]; +} + class PollMessage { name: string; selectableCount: number; @@ -46,6 +53,10 @@ export class SendLinkPreviewDto extends Metadata { linkPreview: linkPreviewMessage; } +export class SendStatusDto extends Metadata { + statusMessage: StatusMessage; +} + export class SendPollDto extends Metadata { pollMessage: PollMessage; } diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index b2400f7f..92b01557 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -9,6 +9,7 @@ import { mediaMessageSchema, pollMessageSchema, reactionMessageSchema, + statusMessageSchema, stickerMessageSchema, textMessageSchema, } from '../../validate/validate.schema'; @@ -22,6 +23,7 @@ import { SendMediaDto, SendPollDto, SendReactionDto, + SendStatusDto, SendStickerDto, SendTextDto, } from '../dto/sendMessage.dto'; @@ -124,6 +126,16 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) + .post(this.routerPath('sendStatus'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: statusMessageSchema, + ClassRef: SendStatusDto, + execute: (instance, data) => sendMessageController.sendStatus(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) .post(this.routerPath('sendLinkPreview'), ...guards, async (req, res) => { const response = await this.dataValidate({ request: req, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 4f30f72c..4ad8ce39 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -77,6 +77,7 @@ import { SendPollDto, SendLinkPreviewDto, SendStickerDto, + SendStatusDto, } from '../dto/sendMessage.dto'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import { @@ -1098,6 +1099,11 @@ export class WAStartupService { return number; } + if (number.includes('@broadcast')) { + this.logger.verbose('Number already contains @broadcast'); + return number; + } + const formattedBRNumber = this.formatBRNumber(number); if (formattedBRNumber !== number) { this.logger.verbose( @@ -1152,7 +1158,7 @@ export class WAStartupService { const jid = this.createJid(number); const isWA = (await this.whatsappNumber({ numbers: [jid] }))[0]; - if (!isWA.exists && !isJidGroup(isWA.jid)) { + if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) { throw new BadRequestException(isWA); } @@ -1229,7 +1235,8 @@ export class WAStartupService { !message['audio'] && !message['poll'] && !message['linkPreview'] && - !message['sticker'] + !message['sticker'] && + !message['status'] ) { if (!message['audio']) { this.logger.verbose('Sending message'); @@ -1258,6 +1265,21 @@ export class WAStartupService { ); } + if (message['status']) { + this.logger.verbose('Sending message'); + return await this.client.sendMessage( + sender, + { + text: message['status'].text, + } as unknown as AnyMessageContent, + { + backgroundColor: message['status'].backgroundColor, + font: message['status'].font, + statusJidList: message['status'].statusJidList, + } as unknown as MiscMessageGenerationOptions, + ); + } + this.logger.verbose('Sending message'); return await this.client.sendMessage( sender, @@ -1335,6 +1357,13 @@ export class WAStartupService { ); } + public async statusMessage(data: SendStatusDto) { + this.logger.verbose('Sending status message'); + return await this.sendMessageWithTyping('status@broadcast', { + status: data.statusMessage, + }); + } + private async prepareMediaMessage(mediaMessage: MediaMessage) { try { this.logger.verbose('Preparing media message'); From 9604f5c317348bcf2386c24b9066a0d214772115 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 6 Jul 2023 13:57:13 -0300 Subject: [PATCH 02/15] feat: Route to send status broadcast --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5c50a877..1feb80af 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/DavidsonGomes/evolution-api.git" + "url": "git+https://github.com/EvolutionAPI/evolution-api.git" }, "keywords": [ "chat", @@ -36,9 +36,9 @@ }, "license": "GPL-3.0", "bugs": { - "url": "https://github.com/DavidsonGomes/evolution-api/issues" + "url": "https://github.com/EvolutionAPI/evolution-api/issues" }, - "homepage": "https://github.com/DavidsonGomes/evolution-api#readme", + "homepage": "https://github.com/EvolutionAPI/evolution-api#readme", "dependencies": { "@whiskeysockets/baileys": "github:EvolutionAPI/Baileys", "@adiwajshing/keyed-db": "^0.2.4", From 4bf4b4a04585ed3d69dbbcdf462fa51b488f1198 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 6 Jul 2023 16:03:48 -0300 Subject: [PATCH 03/15] feat: Route to send status broadcast --- src/validate/validate.schema.ts | 8 +- src/whatsapp/dto/sendMessage.dto.ts | 10 ++- src/whatsapp/services/whatsapp.service.ts | 97 ++++++++++++++++++++--- 3 files changed, 99 insertions(+), 16 deletions(-) diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 9a8fae2d..8527ee32 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -196,7 +196,9 @@ export const statusMessageSchema: JSONSchema7 = { statusMessage: { type: 'object', properties: { - text: { type: 'string' }, + type: { type: 'string', enum: ['text', 'image', 'audio', 'video'] }, + content: { type: 'string' }, + caption: { type: 'string' }, backgroundColor: { type: 'string' }, font: { type: 'integer', minimum: 0, maximum: 5 }, statusJidList: { @@ -210,8 +212,8 @@ export const statusMessageSchema: JSONSchema7 = { }, }, }, - required: ['text', 'backgroundColor', 'font', 'statusJidList'], - ...isNotEmpty('text', 'backgroundColor', 'font', 'statusJidList'), + required: ['type', 'content', 'statusJidList'], + ...isNotEmpty('type', 'content', 'statusJidList'), }, }, required: ['statusMessage'], diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index 1b9708fa..61d4379c 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -32,11 +32,13 @@ class linkPreviewMessage { text: string; } -class StatusMessage { - text: string; - backgroundColor: string; - font: number; +export class StatusMessage { + type: string; + content: string; statusJidList: string[]; + caption?: string; + backgroundColor?: string; + font?: number; } class PollMessage { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 4ad8ce39..9f551569 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -78,6 +78,7 @@ import { SendLinkPreviewDto, SendStickerDto, SendStatusDto, + StatusMessage, } from '../dto/sendMessage.dto'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import { @@ -1236,7 +1237,7 @@ export class WAStartupService { !message['poll'] && !message['linkPreview'] && !message['sticker'] && - !message['status'] + !sender.includes('@broadcast') ) { if (!message['audio']) { this.logger.verbose('Sending message'); @@ -1265,17 +1266,16 @@ export class WAStartupService { ); } - if (message['status']) { + if (sender.includes('@broadcast')) { this.logger.verbose('Sending message'); + console.log(message['status']); return await this.client.sendMessage( sender, + message['status'].content as unknown as AnyMessageContent, { - text: message['status'].text, - } as unknown as AnyMessageContent, - { - backgroundColor: message['status'].backgroundColor, - font: message['status'].font, - statusJidList: message['status'].statusJidList, + backgroundColor: message['status'].option.backgroundColor, + font: message['status'].option.font, + statusJidList: message['status'].option.statusJidList, } as unknown as MiscMessageGenerationOptions, ); } @@ -1357,10 +1357,89 @@ export class WAStartupService { ); } + private async formatStatusMessage(status: StatusMessage) { + if (!status.type) { + throw new BadRequestException('Type is required'); + } + + if (!status.content) { + throw new BadRequestException('Content is required'); + } + + if ( + !status.statusJidList || + !Array.isArray(status.statusJidList) || + !status.statusJidList.length + ) { + throw new BadRequestException('Status jid list is required'); + } + + if (status.type === 'text') { + if (!status.backgroundColor) { + throw new BadRequestException('Background color is required'); + } + + if (!status.font) { + throw new BadRequestException('Font is required'); + } + + return { + content: { + text: status.content, + }, + option: { + backgroundColor: status.backgroundColor, + font: status.font, + statusJidList: status.statusJidList, + }, + }; + } + if (status.type === 'image') { + return { + content: { + image: { + url: status.content, + }, + caption: status.caption, + }, + option: { + statusJidList: status.statusJidList, + }, + }; + } + if (status.type === 'video') { + return { + content: { + video: { + url: status.content, + }, + caption: status.caption, + }, + option: { + statusJidList: status.statusJidList, + }, + }; + } + if (status.type === 'audio') { + return { + content: { + audio: { + url: status.content, + }, + }, + option: { + statusJidList: status.statusJidList, + }, + }; + } + + throw new BadRequestException('Type not found'); + } + public async statusMessage(data: SendStatusDto) { this.logger.verbose('Sending status message'); return await this.sendMessageWithTyping('status@broadcast', { - status: data.statusMessage, + status: await this.formatStatusMessage(data.statusMessage), }); } From 24712c4c2d943293fe4cb87051eea0fa238e4bf5 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 6 Jul 2023 16:39:58 -0300 Subject: [PATCH 04/15] feat: Route to send status broadcast --- .gitignore | 1 + Dockerfile | 1 + package.json | 3 ++- src/whatsapp/services/whatsapp.service.ts | 30 ++++++++++++++++------- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 10508850..6e059b6c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ yarn-error.log* lerna-debug.log* /docker-compose-data +/docker-data # Package /yarn.lock diff --git a/Dockerfile b/Dockerfile index 28e330e8..6fedce79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ ENV DATABASE_SAVE_DATA_CHATS=$DATABASE_SAVE_DATA_CHATS ENV REDIS_ENABLED=$REDIS_ENABLED ENV REDIS_URI=$REDIS_URI +ENV REDIS_PREFIX_KEY=$REDIS_PREFIX_KEY ENV WEBHOOK_GLOBAL_URL=$WEBHOOK_GLOBAL_URL ENV WEBHOOK_GLOBAL_ENABLED=$WEBHOOK_GLOBAL_ENABLED diff --git a/package.json b/package.json index 1feb80af..039c396f 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,10 @@ }, "homepage": "https://github.com/EvolutionAPI/evolution-api#readme", "dependencies": { - "@whiskeysockets/baileys": "github:EvolutionAPI/Baileys", "@adiwajshing/keyed-db": "^0.2.4", "@ffmpeg-installer/ffmpeg": "^1.1.0", "@hapi/boom": "^10.0.1", + "@whiskeysockets/baileys": "github:EvolutionAPI/Baileys", "axios": "^1.3.5", "class-validator": "^0.13.2", "compression": "^1.7.4", @@ -65,6 +65,7 @@ "node-cache": "^5.1.2", "node-mime-types": "^1.1.0", "pino": "^8.11.0", + "proxy-agent": "^6.2.1", "qrcode": "^1.5.1", "qrcode-terminal": "^0.12.0", "redis": "^4.6.5", diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 9f551569..692f0e33 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -119,6 +119,7 @@ import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-st import sharp from 'sharp'; import { RedisCache } from '../../db/redis.client'; import { Log } from '../../config/env.config'; +import ProxyAgent from 'proxy-agent'; export class WAStartupService { constructor( @@ -1421,16 +1422,27 @@ export class WAStartupService { }; } if (status.type === 'audio') { - return { - content: { - audio: { - url: status.content, + const convert = await this.processAudio(status.content, 'status@broadcast'); + if (typeof convert === 'string') { + const audio = fs.readFileSync(convert).toString('base64'); + + const result = { + content: { + audio: Buffer.from(audio, 'base64'), + ptt: true, + mimetype: 'audio/mp4', }, - }, - option: { - statusJidList: status.statusJidList, - }, - }; + option: { + statusJidList: status.statusJidList, + }, + }; + + fs.unlinkSync(convert); + + return result; + } else { + throw new InternalServerErrorException(convert); + } } throw new BadRequestException('Type not found'); From 27aa0add4e75f83bcac2fb2c06d339a4346adf18 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 7 Jul 2023 13:12:51 -0300 Subject: [PATCH 05/15] fix: Adjusted set in webhook to go empty when enabled false --- CHANGELOG.md | 6 +++++- Docker/redis/docker-compose.yaml | 10 ---------- src/db/redis.client.ts | 4 +++- src/validate/validate.schema.ts | 4 ++-- src/whatsapp/controllers/webhook.controller.ts | 8 +++++++- src/whatsapp/services/monitor.service.ts | 3 +++ 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c8e4e6..a5162a32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ ### Features -* Route to send status broadcast +* Route to send status broadcast¼ + +### Fixed + +* Adjusted set in webhook to go empty when enabled false # 1.1.3 (2023-07-06 11:43) diff --git a/Docker/redis/docker-compose.yaml b/Docker/redis/docker-compose.yaml index f0524630..55e73847 100644 --- a/Docker/redis/docker-compose.yaml +++ b/Docker/redis/docker-compose.yaml @@ -7,16 +7,6 @@ networks: services: redis: image: redis:latest - command: > - redis-server - --port 6379 - --appendonly yes - --save 900 1 - --save 300 10 - --save 60 10000 - --appendfsync everysec - volumes: - - evolution_redis:/data container_name: redis ports: - 6379:6379 diff --git a/src/db/redis.client.ts b/src/db/redis.client.ts index f5725ae0..50e7efcb 100644 --- a/src/db/redis.client.ts +++ b/src/db/redis.client.ts @@ -104,9 +104,11 @@ export class RedisCache { public async delAll(hash?: string) { try { this.logger.verbose('instance delAll: ' + hash); - return await this.client.del( + const result = await this.client.del( hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName, ); + + return result; } catch (error) { this.logger.error(error); } diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 8527ee32..e9a45679 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -30,7 +30,7 @@ export const instanceNameSchema: JSONSchema7 = { webhook_by_events: { type: 'boolean' }, events: { type: 'array', - minItems: 1, + minItems: 0, items: { type: 'string', enum: [ @@ -825,7 +825,7 @@ export const webhookSchema: JSONSchema7 = { enabled: { type: 'boolean', enum: [true, false] }, events: { type: 'array', - minItems: 1, + minItems: 0, items: { type: 'string', enum: [ diff --git a/src/whatsapp/controllers/webhook.controller.ts b/src/whatsapp/controllers/webhook.controller.ts index ec6dbb3d..e8141dc7 100644 --- a/src/whatsapp/controllers/webhook.controller.ts +++ b/src/whatsapp/controllers/webhook.controller.ts @@ -8,9 +8,15 @@ export class WebhookController { constructor(private readonly webhookService: WebhookService) {} public async createWebhook(instance: InstanceDto, data: WebhookDto) { - if (!isURL(data.url, { require_tld: false })) { + if (data.enabled && !isURL(data.url, { require_tld: false })) { throw new BadRequestException('Invalid "url" property'); } + + if (!data.enabled) { + data.url = ''; + data.events = []; + } + return this.webhookService.create(instance, data); } diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 73430e6f..9fe5c99a 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -216,6 +216,8 @@ export class WAMonitoringService { rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); } + public async clearStoreFiles(instanceName: string) {} + public async loadInstance() { this.logger.verbose('load instances'); const set = async (name: string) => { @@ -300,6 +302,7 @@ export class WAMonitoringService { try { this.logger.verbose('request cleaning up instance: ' + instanceName); this.cleaningUp(instanceName); + this.clearStoreFiles(instanceName); } finally { this.logger.warn(`Instance "${instanceName}" - REMOVED`); } From 5bd7dd3022ee4f5faf640b18a140536faf717ca7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 7 Jul 2023 13:14:53 -0300 Subject: [PATCH 06/15] fix: Adjusted set in webhook to go empty when enabled false --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5162a32..14c6f9b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -* Route to send status broadcast¼ +* Route to send status broadcast ### Fixed From a7be7c3e19f6d7bda3f6aab6c056d2912e8ce1e3 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 7 Jul 2023 15:55:33 -0300 Subject: [PATCH 07/15] fix: Adjust in store files --- CHANGELOG.md | 1 + package.json | 2 +- src/config/path.config.ts | 1 + src/whatsapp/abstract/abstract.repository.ts | 4 +-- src/whatsapp/repository/chat.repository.ts | 8 +++-- src/whatsapp/repository/contact.repository.ts | 8 +++-- src/whatsapp/repository/message.repository.ts | 8 +++-- .../repository/messageUp.repository.ts | 8 +++-- src/whatsapp/services/monitor.service.ts | 23 +++++++++++-- src/whatsapp/services/whatsapp.service.ts | 33 +++++++++++++++---- 10 files changed, 75 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14c6f9b3..40aa77fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * Adjusted set in webhook to go empty when enabled false +* Adjust in store files # 1.1.3 (2023-07-06 11:43) diff --git a/package.json b/package.json index 039c396f..dd1b8743 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.1.3", + "version": "1.1.4", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { diff --git a/src/config/path.config.ts b/src/config/path.config.ts index 3fb00e49..0d9b6d3c 100644 --- a/src/config/path.config.ts +++ b/src/config/path.config.ts @@ -4,3 +4,4 @@ export const ROOT_DIR = process.cwd(); export const INSTANCE_DIR = join(ROOT_DIR, 'instances'); export const SRC_DIR = join(ROOT_DIR, 'src'); export const AUTH_DIR = join(ROOT_DIR, 'store', 'auth'); +export const STORE_DIR = join(ROOT_DIR, 'store'); diff --git a/src/whatsapp/abstract/abstract.repository.ts b/src/whatsapp/abstract/abstract.repository.ts index a3afac2f..8e56148e 100644 --- a/src/whatsapp/abstract/abstract.repository.ts +++ b/src/whatsapp/abstract/abstract.repository.ts @@ -6,7 +6,7 @@ import { ROOT_DIR } from '../../config/path.config'; export type IInsert = { insertCount: number }; export interface IRepository { - insert(data: any, saveDb?: boolean): Promise; + insert(data: any, instanceName: string, saveDb?: boolean): Promise; find(query: any): Promise; delete(query: any, force?: boolean): Promise; @@ -45,7 +45,7 @@ export abstract class Repository implements IRepository { } }; - public insert(data: any, saveDb = false): Promise { + public insert(data: any, instanceName: string, saveDb = false): Promise { throw new Error('Method not implemented.'); } public find(query: any): Promise { diff --git a/src/whatsapp/repository/chat.repository.ts b/src/whatsapp/repository/chat.repository.ts index d57a9ea2..f717ae2f 100644 --- a/src/whatsapp/repository/chat.repository.ts +++ b/src/whatsapp/repository/chat.repository.ts @@ -16,7 +16,11 @@ export class ChatRepository extends Repository { super(configService); } - public async insert(data: ChatRaw[], saveDb = false): Promise { + public async insert( + data: ChatRaw[], + instanceName: string, + saveDb = false, + ): Promise { if (data.length === 0) { return; } @@ -32,7 +36,7 @@ export class ChatRepository extends Repository { if (store.CHATS) { data.forEach((chat) => { this.writeStore({ - path: join(this.storePath, 'chats', chat.owner), + path: join(this.storePath, 'chats', instanceName), fileName: chat.id, data: chat, }); diff --git a/src/whatsapp/repository/contact.repository.ts b/src/whatsapp/repository/contact.repository.ts index 26dfc25c..59d8947f 100644 --- a/src/whatsapp/repository/contact.repository.ts +++ b/src/whatsapp/repository/contact.repository.ts @@ -16,7 +16,11 @@ export class ContactRepository extends Repository { super(configService); } - public async insert(data: ContactRaw[], saveDb = false): Promise { + public async insert( + data: ContactRaw[], + instanceName: string, + saveDb = false, + ): Promise { if (data.length === 0) { return; } @@ -32,7 +36,7 @@ export class ContactRepository extends Repository { if (store.CONTACTS) { data.forEach((contact) => { this.writeStore({ - path: join(this.storePath, 'contacts', contact.owner), + path: join(this.storePath, 'contacts', instanceName), fileName: contact.id, data: contact, }); diff --git a/src/whatsapp/repository/message.repository.ts b/src/whatsapp/repository/message.repository.ts index eeb207af..4da56e8a 100644 --- a/src/whatsapp/repository/message.repository.ts +++ b/src/whatsapp/repository/message.repository.ts @@ -17,7 +17,11 @@ export class MessageRepository extends Repository { super(configService); } - public async insert(data: MessageRaw[], saveDb = false): Promise { + public async insert( + data: MessageRaw[], + instanceName: string, + saveDb = false, + ): Promise { if (!Array.isArray(data) || data.length === 0) { return; } @@ -52,7 +56,7 @@ export class MessageRepository extends Repository { if (store.MESSAGES) { data.forEach((msg) => this.writeStore({ - path: join(this.storePath, 'messages', msg.owner), + path: join(this.storePath, 'messages', instanceName), fileName: msg.key.id, data: msg, }), diff --git a/src/whatsapp/repository/messageUp.repository.ts b/src/whatsapp/repository/messageUp.repository.ts index e40fe1c9..f9a42ee0 100644 --- a/src/whatsapp/repository/messageUp.repository.ts +++ b/src/whatsapp/repository/messageUp.repository.ts @@ -17,7 +17,11 @@ export class MessageUpRepository extends Repository { super(configService); } - public async insert(data: MessageUpdateRaw[], saveDb?: boolean): Promise { + public async insert( + data: MessageUpdateRaw[], + instanceName: string, + saveDb?: boolean, + ): Promise { if (data.length === 0) { return; } @@ -33,7 +37,7 @@ export class MessageUpRepository extends Repository { if (store.MESSAGE_UP) { data.forEach((update) => { this.writeStore({ - path: join(this.storePath, 'message-up', update.owner), + path: join(this.storePath, 'message-up', instanceName), fileName: update.id, data: update, }); diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 9fe5c99a..fc261866 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -1,6 +1,6 @@ import { opendirSync, readdirSync, rmSync } from 'fs'; import { WAStartupService } from './whatsapp.service'; -import { INSTANCE_DIR } from '../../config/path.config'; +import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config'; import EventEmitter2 from 'eventemitter2'; import { join } from 'path'; import { Logger } from '../../config/logger.config'; @@ -16,6 +16,7 @@ import { NotFoundException } from '../../exceptions'; import { Db } from 'mongodb'; import { initInstance } from '../whatsapp.module'; import { RedisCache } from '../../db/redis.client'; +import { execSync } from 'child_process'; export class WAMonitoringService { constructor( @@ -216,7 +217,23 @@ export class WAMonitoringService { rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); } - public async clearStoreFiles(instanceName: string) {} + public async cleaningStoreFiles(instanceName: string) { + this.logger.verbose('cleaning store files instance: ' + instanceName); + + if (!this.db.ENABLED) { + const instance = this.waInstances[instanceName]; + + rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + + execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`); + execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`); + execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`); + execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`); + + execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`); + execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`); + } + } public async loadInstance() { this.logger.verbose('load instances'); @@ -302,7 +319,7 @@ export class WAMonitoringService { try { this.logger.verbose('request cleaning up instance: ' + instanceName); this.cleaningUp(instanceName); - this.clearStoreFiles(instanceName); + this.cleaningStoreFiles(instanceName); } finally { this.logger.warn(`Instance "${instanceName}" - REMOVED`); } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 692f0e33..c90b0b54 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -30,6 +30,7 @@ import makeWASocket, { WAMessageUpdate, WASocket, getAggregateVotesInPollMessage, + Browsers, } from '@whiskeysockets/baileys'; import { Auth, @@ -554,14 +555,14 @@ export class WAStartupService { `rm -rf ${join( this.storePath, key.toLowerCase().replace('_', '-'), - this.instance.wuid, + this.instance.name, )}/*.json`, ); this.logger.verbose( `Cleaned ${join( this.storePath, key.toLowerCase().replace('_', '-'), - this.instance.wuid, + this.instance.name, )}/*.json`, ); } @@ -601,7 +602,8 @@ export class WAStartupService { const { version } = await fetchLatestBaileysVersion(); this.logger.verbose('Baileys version: ' + version); const session = this.configService.get('CONFIG_SESSION_PHONE'); - const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; + // const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; + const browser: WABrowserDescription = Browsers.appropriate(session.CLIENT); this.logger.verbose('Browser: ' + JSON.stringify(browser)); const socketConfig: UserFacingSocketConfig = { @@ -692,7 +694,11 @@ export class WAStartupService { await this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, database.SAVE_DATA.CHATS); + await this.repository.chat.insert( + chatsRaw, + this.instance.name, + database.SAVE_DATA.CHATS, + ); }, 'chats.update': async ( @@ -757,7 +763,11 @@ export class WAStartupService { await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Inserting contacts in database'); - await this.repository.contact.insert(contactsRaw, database.SAVE_DATA.CONTACTS); + await this.repository.contact.insert( + contactsRaw, + this.instance.name, + database.SAVE_DATA.CONTACTS, + ); }, 'contacts.update': async (contacts: Partial[]) => { @@ -808,7 +818,11 @@ export class WAStartupService { await this.sendDataWebhook(Events.CHATS_SET, chatsRaw); this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, database.SAVE_DATA.CHATS); + await this.repository.chat.insert( + chatsRaw, + this.instance.name, + database.SAVE_DATA.CHATS, + ); } const messagesRaw: MessageRaw[] = []; @@ -890,7 +904,11 @@ export class WAStartupService { await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], database.SAVE_DATA.NEW_MESSAGE); + await this.repository.message.insert( + [messageRaw], + this.instance.name, + database.SAVE_DATA.NEW_MESSAGE, + ); }, 'messages.update': async (args: WAMessageUpdate[], database: Database) => { @@ -934,6 +952,7 @@ export class WAStartupService { this.logger.verbose('Inserting message in database'); await this.repository.messageUpdate.insert( [message], + this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE, ); } From 437803da0775b7e01c494f98eb11105c345b95a3 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 06:52:56 -0300 Subject: [PATCH 08/15] feat: Added verbose logs --- CHANGELOG.md | 1 + src/whatsapp/controllers/chat.controller.ts | 22 ++++ src/whatsapp/controllers/group.controller.ts | 36 ++++++ .../controllers/instance.controller.ts | 78 ++++++++++- .../controllers/sendMessage.controller.ts | 36 ++++++ .../controllers/webhook.controller.ts | 7 + src/whatsapp/routers/chat.router.ts | 122 ++++++++++++++++++ src/whatsapp/routers/group.router.ts | 94 ++++++++++++++ src/whatsapp/routers/instance.router.ts | 57 ++++++++ src/whatsapp/routers/sendMessage.router.ts | 75 +++++++++++ src/whatsapp/routers/webhook.router.ts | 15 +++ 11 files changed, 542 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40aa77fc..f74648ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Adjusted set in webhook to go empty when enabled false * Adjust in store files +* Added verbose logs # 1.1.3 (2023-07-06 11:43) diff --git a/src/whatsapp/controllers/chat.controller.ts b/src/whatsapp/controllers/chat.controller.ts index 8d567a51..0a176059 100644 --- a/src/whatsapp/controllers/chat.controller.ts +++ b/src/whatsapp/controllers/chat.controller.ts @@ -16,31 +16,40 @@ import { ContactQuery } from '../repository/contact.repository'; import { MessageQuery } from '../repository/message.repository'; import { MessageUpQuery } from '../repository/messageUp.repository'; import { WAMonitoringService } from '../services/monitor.service'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('ChatController'); export class ChatController { constructor(private readonly waMonitor: WAMonitoringService) {} public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) { + logger.verbose('requested whatsappNumber from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].whatsappNumber(data); } public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) { + logger.verbose('requested readMessage from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data); } public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) { + logger.verbose('requested archiveChat from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].archiveChat(data); } public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) { + logger.verbose('requested deleteMessage from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].deleteMessage(data); } public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) { + logger.verbose('requested fetchProfilePicture from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].profilePicture(data.number); } public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) { + logger.verbose('requested fetchContacts from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchContacts(query); } @@ -48,22 +57,29 @@ export class ChatController { { instanceName }: InstanceDto, data: getBase64FromMediaMessageDto, ) { + logger.verbose( + 'requested getBase64FromMediaMessage from ' + instanceName + ' instance', + ); return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data); } public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) { + logger.verbose('requested fetchMessages from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchMessages(query); } public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) { + logger.verbose('requested fetchStatusMessage from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query); } public async fetchChats({ instanceName }: InstanceDto) { + logger.verbose('requested fetchChats from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchChats(); } public async fetchPrivacySettings({ instanceName }: InstanceDto) { + logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings(); } @@ -71,6 +87,7 @@ export class ChatController { { instanceName }: InstanceDto, data: PrivacySettingDto, ) { + logger.verbose('requested updatePrivacySettings from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data); } @@ -78,12 +95,14 @@ export class ChatController { { instanceName }: InstanceDto, data: ProfilePictureDto, ) { + logger.verbose('requested fetchBusinessProfile from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile( data.number, ); } public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) { + logger.verbose('requested updateProfileName from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name); } @@ -91,6 +110,7 @@ export class ChatController { { instanceName }: InstanceDto, data: ProfileStatusDto, ) { + logger.verbose('requested updateProfileStatus from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].updateProfileStatus( data.status, ); @@ -100,6 +120,7 @@ export class ChatController { { instanceName }: InstanceDto, data: ProfilePictureDto, ) { + logger.verbose('requested updateProfilePicture from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].updateProfilePicture( data.picture, ); @@ -109,6 +130,7 @@ export class ChatController { { instanceName }: InstanceDto, data: ProfilePictureDto, ) { + logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].removeProfilePicture(); } } diff --git a/src/whatsapp/controllers/group.controller.ts b/src/whatsapp/controllers/group.controller.ts index e8376bfa..e0254796 100644 --- a/src/whatsapp/controllers/group.controller.ts +++ b/src/whatsapp/controllers/group.controller.ts @@ -12,21 +12,31 @@ import { } from '../dto/group.dto'; import { InstanceDto } from '../dto/instance.dto'; import { WAMonitoringService } from '../services/monitor.service'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('ChatController'); export class GroupController { constructor(private readonly waMonitor: WAMonitoringService) {} public async createGroup(instance: InstanceDto, create: CreateGroupDto) { + logger.verbose('requested createGroup from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].createGroup(create); } public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) { + logger.verbose( + 'requested updateGroupPicture from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture( update, ); } public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) { + logger.verbose( + 'requested updateGroupSubject from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject( update, ); @@ -36,38 +46,54 @@ export class GroupController { instance: InstanceDto, update: GroupDescriptionDto, ) { + logger.verbose( + 'requested updateGroupDescription from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription( update, ); } public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) { + logger.verbose('requested findGroupInfo from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid); } public async fetchAllGroups(instance: InstanceDto) { + logger.verbose( + 'requested fetchAllGroups from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(); } public async inviteCode(instance: InstanceDto, groupJid: GroupJid) { + logger.verbose('requested inviteCode from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].inviteCode(groupJid); } public async inviteInfo(instance: InstanceDto, inviteCode: GroupInvite) { + logger.verbose('requested inviteInfo from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode); } public async sendInvite(instance: InstanceDto, data: GroupSendInvite) { + logger.verbose('requested sendInvite from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data); } public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) { + logger.verbose( + 'requested revokeInviteCode from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode( groupJid, ); } public async findParticipants(instance: InstanceDto, groupJid: GroupJid) { + logger.verbose( + 'requested findParticipants from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].findParticipants( groupJid, ); @@ -77,22 +103,32 @@ export class GroupController { instance: InstanceDto, update: GroupUpdateParticipantDto, ) { + logger.verbose( + 'requested updateGParticipate from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant( update, ); } public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) { + logger.verbose( + 'requested updateGSetting from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update); } public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) { + logger.verbose( + 'requested toggleEphemeral from ' + instance.instanceName + ' instance', + ); return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral( update, ); } public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) { + logger.verbose('requested leaveGroup from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid); } } diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index b309366a..c2abbffa 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -33,9 +33,13 @@ export class InstanceController { qrcode, token, }: InstanceDto) { + this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); + const mode = this.configService.get('AUTHENTICATION').INSTANCE.MODE; if (mode === 'container') { + this.logger.verbose('container mode'); + if (Object.keys(this.waMonitor.waInstances).length > 0) { throw new BadRequestException([ 'Instance already created', @@ -43,8 +47,10 @@ export class InstanceController { ]); } + this.logger.verbose('checking duplicate token'); await this.authService.checkDuplicateToken(token); + this.logger.verbose('creating instance'); const instance = new WAStartupService( this.configService, this.eventEmitter, @@ -52,9 +58,12 @@ export class InstanceController { this.cache, ); instance.instanceName = instanceName; + this.logger.verbose('instance: ' + instance.instanceName + ' created'); + this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.delInstanceTime(instance.instanceName); + this.logger.verbose('generating hash'); const hash = await this.authService.generateHash( { instanceName: instance.instanceName, @@ -62,9 +71,12 @@ export class InstanceController { token, ); + this.logger.verbose('hash: ' + hash + ' generated'); + let getEvents: string[]; if (webhook) { + this.logger.verbose('creating webhook'); try { this.webhookService.create(instance, { enabled: true, @@ -79,6 +91,17 @@ export class InstanceController { } } + this.logger.verbose('instance created'); + this.logger.verbose({ + instance: { + instanceName: instance.instanceName, + status: 'created', + }, + hash, + webhook, + events: getEvents, + }); + return { instance: { instanceName: instance.instanceName, @@ -89,8 +112,12 @@ export class InstanceController { events: getEvents, }; } else { + this.logger.verbose('server mode'); + + this.logger.verbose('checking duplicate token'); await this.authService.checkDuplicateToken(token); + this.logger.verbose('creating instance'); const instance = new WAStartupService( this.configService, this.eventEmitter, @@ -98,9 +125,13 @@ export class InstanceController { this.cache, ); instance.instanceName = instanceName; + + this.logger.verbose('instance: ' + instance.instanceName + ' created'); + this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.delInstanceTime(instance.instanceName); + this.logger.verbose('generating hash'); const hash = await this.authService.generateHash( { instanceName: instance.instanceName, @@ -108,9 +139,12 @@ export class InstanceController { token, ); + this.logger.verbose('hash: ' + hash + ' generated'); + let getEvents: string[]; if (webhook) { + this.logger.verbose('creating webhook'); try { this.webhookService.create(instance, { enabled: true, @@ -128,11 +162,25 @@ export class InstanceController { let getQrcode: wa.QrCode; if (qrcode) { + this.logger.verbose('creating qrcode'); await instance.connectToWhatsapp(); await delay(2000); getQrcode = instance.qrCode; } + this.logger.verbose('instance created'); + this.logger.verbose({ + instance: { + instanceName: instance.instanceName, + status: 'created', + }, + hash, + webhook, + webhook_by_events, + events: getEvents, + qrcode: getQrcode, + }); + return { instance: { instanceName: instance.instanceName, @@ -149,11 +197,18 @@ export class InstanceController { public async connectToWhatsapp({ instanceName }: InstanceDto) { try { + this.logger.verbose( + 'requested connectToWhatsapp from ' + instanceName + ' instance', + ); + const instance = this.waMonitor.waInstances[instanceName]; const state = instance?.connectionStatus?.state; + this.logger.verbose('state: ' + state); + switch (state) { case 'close': + this.logger.verbose('connecting'); await instance.connectToWhatsapp(); await delay(2000); return instance.qrCode; @@ -169,8 +224,12 @@ export class InstanceController { public async restartInstance({ instanceName }: InstanceDto) { try { + this.logger.verbose('requested restartInstance from ' + instanceName + ' instance'); + + this.logger.verbose('deleting instance: ' + instanceName); delete this.waMonitor.waInstances[instanceName]; - console.log(this.waMonitor.waInstances[instanceName]); + + this.logger.verbose('creating instance: ' + instanceName); const instance = new WAStartupService( this.configService, this.eventEmitter, @@ -179,6 +238,10 @@ export class InstanceController { ); instance.instanceName = instanceName; + + this.logger.verbose('instance: ' + instance.instanceName + ' created'); + + this.logger.verbose('connecting instance: ' + instanceName); await instance.connectToWhatsapp(); this.waMonitor.waInstances[instance.instanceName] = instance; @@ -189,11 +252,14 @@ export class InstanceController { } public async connectionState({ instanceName }: InstanceDto) { + this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); return this.waMonitor.waInstances[instanceName]?.connectionStatus; } public async fetchInstances({ instanceName }: InstanceDto) { + this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance'); if (instanceName) { + this.logger.verbose('instanceName: ' + instanceName); return this.waMonitor.instanceInfo(instanceName); } @@ -201,11 +267,14 @@ export class InstanceController { } public async logout({ instanceName }: InstanceDto) { + this.logger.verbose('requested logout from ' + instanceName + ' instance'); try { + this.logger.verbose('logging out instance: ' + instanceName); await this.waMonitor.waInstances[instanceName]?.client?.logout( 'Log out instance: ' + instanceName, ); + this.logger.verbose('close connection instance: ' + instanceName); this.waMonitor.waInstances[instanceName]?.client?.ws?.close(); return { error: false, message: 'Instance logged out' }; @@ -215,7 +284,9 @@ export class InstanceController { } public async deleteInstance({ instanceName }: InstanceDto) { + this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance'); const stateConn = await this.connectionState({ instanceName }); + if (stateConn.state === 'open') { throw new BadRequestException([ 'Deletion failed', @@ -224,10 +295,14 @@ export class InstanceController { } try { if (stateConn.state === 'connecting') { + this.logger.verbose('logging out instance: ' + instanceName); + await this.logout({ instanceName }); delete this.waMonitor.waInstances[instanceName]; return { error: false, message: 'Instance deleted' }; } else { + this.logger.verbose('deleting instance: ' + instanceName); + delete this.waMonitor.waInstances[instanceName]; this.eventEmitter.emit('remove.instance', instanceName, 'inner'); return { error: false, message: 'Instance deleted' }; @@ -238,6 +313,7 @@ export class InstanceController { } public async refreshToken(_: InstanceDto, oldToken: OldToken) { + this.logger.verbose('requested refreshToken'); return await this.authService.refreshToken(oldToken); } } diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts index 8e70c26c..c2d5298c 100644 --- a/src/whatsapp/controllers/sendMessage.controller.ts +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -17,17 +17,29 @@ import { } from '../dto/sendMessage.dto'; import { WAMonitoringService } from '../services/monitor.service'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('MessageRouter'); + export class SendMessageController { constructor(private readonly waMonitor: WAMonitoringService) {} public async sendText({ instanceName }: InstanceDto, data: SendTextDto) { + logger.verbose('requested sendText from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].textMessage(data); } public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) { + logger.verbose('requested sendMedia from ' + instanceName + ' instance'); if (isBase64(data?.mediaMessage?.media) && !data?.mediaMessage?.fileName) { throw new BadRequestException('For bse64 the file name must be informed.'); } + logger.verbose( + 'isURL: ' + + isURL(data?.mediaMessage?.media) + + ', isBase64: ' + + isBase64(data?.mediaMessage?.media), + ); if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) { return await this.waMonitor.waInstances[instanceName].mediaMessage(data); } @@ -35,6 +47,14 @@ export class SendMessageController { } public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) { + logger.verbose('requested sendSticker from ' + instanceName + ' instance'); + + logger.verbose( + 'isURL: ' + + isURL(data?.stickerMessage?.image) + + ', isBase64: ' + + isBase64(data?.stickerMessage?.image), + ); if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) { return await this.waMonitor.waInstances[instanceName].mediaSticker(data); } @@ -42,6 +62,14 @@ export class SendMessageController { } public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) { + logger.verbose('requested sendWhatsAppAudio from ' + instanceName + ' instance'); + + logger.verbose( + 'isURL: ' + + isURL(data?.audioMessage?.audio) + + ', isBase64: ' + + isBase64(data?.audioMessage?.audio), + ); if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) { return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data); } @@ -49,6 +77,7 @@ export class SendMessageController { } public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) { + logger.verbose('requested sendButtons from ' + instanceName + ' instance'); if ( isBase64(data.buttonMessage.mediaMessage?.media) && !data.buttonMessage.mediaMessage?.fileName @@ -59,18 +88,22 @@ export class SendMessageController { } public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) { + logger.verbose('requested sendLocation from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].locationMessage(data); } public async sendList({ instanceName }: InstanceDto, data: SendListDto) { + logger.verbose('requested sendList from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].listMessage(data); } public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) { + logger.verbose('requested sendContact from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].contactMessage(data); } public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) { + logger.verbose('requested sendReaction from ' + instanceName + ' instance'); if (!data.reactionMessage.reaction.match(/[^\(\)\w\sà-ú"-\+]+/)) { throw new BadRequestException('"reaction" must be an emoji'); } @@ -78,14 +111,17 @@ export class SendMessageController { } public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) { + logger.verbose('requested sendPoll from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].pollMessage(data); } public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto) { + logger.verbose('requested sendStatus from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].statusMessage(data); } public async sendLinkPreview({ instanceName }: InstanceDto, data: SendLinkPreviewDto) { + logger.verbose('requested sendLinkPreview from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].linkPreview(data); } } diff --git a/src/whatsapp/controllers/webhook.controller.ts b/src/whatsapp/controllers/webhook.controller.ts index e8141dc7..b5747b2e 100644 --- a/src/whatsapp/controllers/webhook.controller.ts +++ b/src/whatsapp/controllers/webhook.controller.ts @@ -3,16 +3,22 @@ import { BadRequestException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; import { WebhookDto } from '../dto/webhook.dto'; import { WebhookService } from '../services/webhook.service'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('WebhookController'); export class WebhookController { constructor(private readonly webhookService: WebhookService) {} public async createWebhook(instance: InstanceDto, data: WebhookDto) { + logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance'); + if (data.enabled && !isURL(data.url, { require_tld: false })) { throw new BadRequestException('Invalid "url" property'); } if (!data.enabled) { + logger.verbose('webhook disabled'); data.url = ''; data.events = []; } @@ -21,6 +27,7 @@ export class WebhookController { } public async findWebhook(instance: InstanceDto) { + logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance'); return this.webhookService.find(instance); } } diff --git a/src/whatsapp/routers/chat.router.ts b/src/whatsapp/routers/chat.router.ts index 46d05aa9..50ead521 100644 --- a/src/whatsapp/routers/chat.router.ts +++ b/src/whatsapp/routers/chat.router.ts @@ -32,12 +32,22 @@ import { HttpStatus } from './index.router'; import { MessageUpQuery } from '../repository/messageUp.repository'; import { proto } from '@whiskeysockets/baileys'; import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('ChatRouter'); export class ChatRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router .post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => { + logger.verbose('request received in whatsappNumbers'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: whatsappNumberSchema, @@ -48,6 +58,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => { + logger.verbose('request received in markMessageAsRead'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: readMessageSchema, @@ -58,6 +75,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('archiveChat'), ...guards, async (req, res) => { + logger.verbose('request received in archiveChat'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: archiveChatSchema, @@ -71,6 +95,13 @@ export class ChatRouter extends RouterBroker { this.routerPath('deleteMessageForEveryone'), ...guards, async (req, res) => { + logger.verbose('request received in deleteMessageForEveryone'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: deleteMessageSchema, @@ -82,6 +113,13 @@ export class ChatRouter extends RouterBroker { }, ) .post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => { + logger.verbose('request received in fetchProfilePictureUrl'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profilePictureSchema, @@ -92,6 +130,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('findContacts'), ...guards, async (req, res) => { + logger.verbose('request received in findContacts'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: contactValidateSchema, @@ -102,6 +147,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => { + logger.verbose('request received in getBase64FromMediaMessage'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: null, @@ -113,6 +165,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('findMessages'), ...guards, async (req, res) => { + logger.verbose('request received in findMessages'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: messageValidateSchema, @@ -123,6 +182,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => { + logger.verbose('request received in findStatusMessage'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: messageUpSchema, @@ -133,6 +199,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('findChats'), ...guards, async (req, res) => { + logger.verbose('request received in findChats'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: null, @@ -144,6 +217,13 @@ export class ChatRouter extends RouterBroker { }) // Profile routes .get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => { + logger.verbose('request received in fetchPrivacySettings'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: null, @@ -154,6 +234,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .put(this.routerPath('updatePrivacySettings'), ...guards, async (req, res) => { + logger.verbose('request received in updatePrivacySettings'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: privacySettingsSchema, @@ -165,6 +252,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('fetchBusinessProfile'), ...guards, async (req, res) => { + logger.verbose('request received in fetchBusinessProfile'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profilePictureSchema, @@ -176,6 +270,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('updateProfileName'), ...guards, async (req, res) => { + logger.verbose('request received in updateProfileName'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profileNameSchema, @@ -186,6 +287,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('updateProfileStatus'), ...guards, async (req, res) => { + logger.verbose('request received in updateProfileStatus'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profileStatusSchema, @@ -196,6 +304,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .put(this.routerPath('updateProfilePicture'), ...guards, async (req, res) => { + logger.verbose('request received in updateProfilePicture'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profilePictureSchema, @@ -207,6 +322,13 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .delete(this.routerPath('removeProfilePicture'), ...guards, async (req, res) => { + logger.verbose('request received in removeProfilePicture'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ request: req, schema: profilePictureSchema, diff --git a/src/whatsapp/routers/group.router.ts b/src/whatsapp/routers/group.router.ts index 17764f4e..8cb3032e 100644 --- a/src/whatsapp/routers/group.router.ts +++ b/src/whatsapp/routers/group.router.ts @@ -26,12 +26,21 @@ import { } from '../dto/group.dto'; import { groupController } from '../whatsapp.module'; import { HttpStatus } from './index.router'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('GroupRouter'); export class GroupRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router .post(this.routerPath('create'), ...guards, async (req, res) => { + logger.verbose('request received in createGroup'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: createGroupSchema, @@ -42,6 +51,13 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('updateGroupSubject'), ...guards, async (req, res) => { + logger.verbose('request received in updateGroupSubject'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.groupValidate({ request: req, schema: updateGroupSubjectSchema, @@ -52,6 +68,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => { + logger.verbose('request received in updateGroupPicture'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: updateGroupPictureSchema, @@ -62,6 +84,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('updateGroupDescription'), ...guards, async (req, res) => { + logger.verbose('request received in updateGroupDescription'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: updateGroupDescriptionSchema, @@ -73,6 +101,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => { + logger.verbose('request received in findGroupInfos'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: groupJidSchema, @@ -83,6 +117,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('fetchAllGroups'), ...guards, async (req, res) => { + logger.verbose('request received in fetchAllGroups'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupNoValidate({ request: req, schema: {}, @@ -93,6 +133,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('participants'), ...guards, async (req, res) => { + logger.verbose('request received in participants'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: groupJidSchema, @@ -103,6 +149,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('inviteCode'), ...guards, async (req, res) => { + logger.verbose('request received in inviteCode'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: groupJidSchema, @@ -113,6 +165,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('inviteInfo'), ...guards, async (req, res) => { + logger.verbose('request received in inviteInfo'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.inviteCodeValidate({ request: req, schema: groupInviteSchema, @@ -123,6 +181,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('sendInvite'), ...guards, async (req, res) => { + logger.verbose('request received in sendInvite'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupNoValidate({ request: req, schema: groupSendInviteSchema, @@ -133,6 +197,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => { + logger.verbose('request received in revokeInviteCode'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: groupJidSchema, @@ -143,6 +213,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('updateParticipant'), ...guards, async (req, res) => { + logger.verbose('request received in updateParticipant'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: updateParticipantsSchema, @@ -153,6 +229,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('updateSetting'), ...guards, async (req, res) => { + logger.verbose('request received in updateSetting'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: updateSettingsSchema, @@ -163,6 +245,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => { + logger.verbose('request received in toggleEphemeral'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: toggleEphemeralSchema, @@ -173,6 +261,12 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => { + logger.verbose('request received in leaveGroup'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.groupValidate({ request: req, schema: {}, diff --git a/src/whatsapp/routers/instance.router.ts b/src/whatsapp/routers/instance.router.ts index 29a59a87..799c8249 100644 --- a/src/whatsapp/routers/instance.router.ts +++ b/src/whatsapp/routers/instance.router.ts @@ -7,6 +7,9 @@ import { HttpStatus } from './index.router'; import { OldToken } from '../services/auth.service'; import { Auth, ConfigService, Database } from '../../config/env.config'; import { dbserver } from '../../db/db.connect'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('InstanceRouter'); export class InstanceRouter extends RouterBroker { constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) { @@ -14,6 +17,12 @@ export class InstanceRouter extends RouterBroker { const auth = configService.get('AUTHENTICATION'); this.router .post('/create', ...guards, async (req, res) => { + logger.verbose('request received in createInstance'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -24,6 +33,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .put(this.routerPath('restart'), ...guards, async (req, res) => { + logger.verbose('request received in restartInstance'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -34,6 +49,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('connect'), ...guards, async (req, res) => { + logger.verbose('request received in connectInstance'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -44,6 +65,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('connectionState'), ...guards, async (req, res) => { + logger.verbose('request received in connectionState'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -54,6 +81,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => { + logger.verbose('request received in fetchInstances'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: null, @@ -64,6 +97,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .delete(this.routerPath('logout'), ...guards, async (req, res) => { + logger.verbose('request received in logoutInstances'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -74,6 +113,12 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) .delete(this.routerPath('delete'), ...guards, async (req, res) => { + logger.verbose('request received in deleteInstances'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, @@ -86,6 +131,12 @@ export class InstanceRouter extends RouterBroker { if (auth.TYPE === 'jwt') { this.router.put('/refreshToken', async (req, res) => { + logger.verbose('request received in refreshToken'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: oldTokenSchema, @@ -98,6 +149,12 @@ export class InstanceRouter extends RouterBroker { } this.router.delete('/deleteDatabase', async (req, res) => { + logger.verbose('request received in deleteDatabase'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const db = this.configService.get('DATABASE'); if (db.ENABLED) { try { diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index 92b01557..d8d3acea 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -30,12 +30,21 @@ import { import { sendMessageController } from '../whatsapp.module'; import { RouterBroker } from '../abstract/abstract.router'; import { HttpStatus } from './index.router'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('MessageRouter'); export class MessageRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router .post(this.routerPath('sendText'), ...guards, async (req, res) => { + logger.verbose('request received in sendText'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: textMessageSchema, @@ -46,6 +55,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendMedia'), ...guards, async (req, res) => { + logger.verbose('request received in sendMedia'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: mediaMessageSchema, @@ -56,6 +71,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendWhatsAppAudio'), ...guards, async (req, res) => { + logger.verbose('request received in sendWhatsAppAudio'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: audioMessageSchema, @@ -67,6 +88,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendButtons'), ...guards, async (req, res) => { + logger.verbose('request received in sendButtons'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: buttonMessageSchema, @@ -77,6 +104,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendLocation'), ...guards, async (req, res) => { + logger.verbose('request received in sendLocation'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: locationMessageSchema, @@ -87,6 +120,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendList'), ...guards, async (req, res) => { + logger.verbose('request received in sendList'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: listMessageSchema, @@ -97,6 +136,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendContact'), ...guards, async (req, res) => { + logger.verbose('request received in sendContact'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: contactMessageSchema, @@ -107,6 +152,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendReaction'), ...guards, async (req, res) => { + logger.verbose('request received in sendReaction'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: reactionMessageSchema, @@ -117,6 +168,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendPoll'), ...guards, async (req, res) => { + logger.verbose('request received in sendPoll'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: pollMessageSchema, @@ -127,6 +184,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendStatus'), ...guards, async (req, res) => { + logger.verbose('request received in sendStatus'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: statusMessageSchema, @@ -137,6 +200,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendLinkPreview'), ...guards, async (req, res) => { + logger.verbose('request received in sendLinkPreview'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: linkPreviewSchema, @@ -148,6 +217,12 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) .post(this.routerPath('sendSticker'), ...guards, async (req, res) => { + logger.verbose('request received in sendSticker'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: stickerMessageSchema, diff --git a/src/whatsapp/routers/webhook.router.ts b/src/whatsapp/routers/webhook.router.ts index 70a65a0c..c520d9d5 100644 --- a/src/whatsapp/routers/webhook.router.ts +++ b/src/whatsapp/routers/webhook.router.ts @@ -5,12 +5,21 @@ import { InstanceDto } from '../dto/instance.dto'; import { WebhookDto } from '../dto/webhook.dto'; import { webhookController } from '../whatsapp.module'; import { HttpStatus } from './index.router'; +import { Logger } from '../../config/logger.config'; + +const logger = new Logger('WebhookRouter'); export class WebhookRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router .post(this.routerPath('set'), ...guards, async (req, res) => { + logger.verbose('request received in setWebhook'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: webhookSchema, @@ -21,6 +30,12 @@ export class WebhookRouter extends RouterBroker { res.status(HttpStatus.CREATED).json(response); }) .get(this.routerPath('find'), ...guards, async (req, res) => { + logger.verbose('request received in findWebhook'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); const response = await this.dataValidate({ request: req, schema: instanceNameSchema, From eca4285ea81abf6f25977659a84ddbb3c7a0b2a1 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 07:15:34 -0300 Subject: [PATCH 09/15] fix: Fixed the problem when do not save contacts when receive messages --- CHANGELOG.md | 4 +- src/validate/validate.schema.ts | 5 +- src/whatsapp/abstract/abstract.repository.ts | 6 ++ src/whatsapp/dto/sendMessage.dto.ts | 3 +- src/whatsapp/repository/contact.repository.ts | 34 ++++++++ src/whatsapp/services/whatsapp.service.ts | 84 +++++++++++++++++-- 6 files changed, 124 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f74648ee..ca2885d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,14 @@ ### Features * Route to send status broadcast +* Added verbose logs +* Insert allContacts in payload of endpoint sendStatus ### Fixed * Adjusted set in webhook to go empty when enabled false * Adjust in store files -* Added verbose logs +* Fixed the problem when do not save contacts when receive messages # 1.1.3 (2023-07-06 11:43) diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index e9a45679..9cf411e5 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -211,9 +211,10 @@ export const statusMessageSchema: JSONSchema7 = { description: '"statusJidList" must be an array of numeric strings', }, }, + allContacts: { type: 'boolean', enum: [true, false] }, }, - required: ['type', 'content', 'statusJidList'], - ...isNotEmpty('type', 'content', 'statusJidList'), + required: ['type', 'content'], + ...isNotEmpty('type', 'content'), }, }, required: ['statusMessage'], diff --git a/src/whatsapp/abstract/abstract.repository.ts b/src/whatsapp/abstract/abstract.repository.ts index 8e56148e..a7215383 100644 --- a/src/whatsapp/abstract/abstract.repository.ts +++ b/src/whatsapp/abstract/abstract.repository.ts @@ -7,6 +7,7 @@ export type IInsert = { insertCount: number }; export interface IRepository { insert(data: any, instanceName: string, saveDb?: boolean): Promise; + update(data: any, instanceName: string, saveDb?: boolean): Promise; find(query: any): Promise; delete(query: any, force?: boolean): Promise; @@ -48,6 +49,11 @@ export abstract class Repository implements IRepository { public insert(data: any, instanceName: string, saveDb = false): Promise { throw new Error('Method not implemented.'); } + + public update(data: any, instanceName: string, saveDb = false): Promise { + throw new Error('Method not implemented.'); + } + public find(query: any): Promise { throw new Error('Method not implemented.'); } diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index 61d4379c..4f1ff88b 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -35,7 +35,8 @@ class linkPreviewMessage { export class StatusMessage { type: string; content: string; - statusJidList: string[]; + statusJidList?: string[]; + allContacts?: boolean; caption?: string; backgroundColor?: string; font?: number; diff --git a/src/whatsapp/repository/contact.repository.ts b/src/whatsapp/repository/contact.repository.ts index 59d8947f..67c51dff 100644 --- a/src/whatsapp/repository/contact.repository.ts +++ b/src/whatsapp/repository/contact.repository.ts @@ -53,6 +53,40 @@ export class ContactRepository extends Repository { } } + public async update( + data: ContactRaw, + instanceName: string, + saveDb = false, + ): Promise { + try { + if (this.dbSettings.ENABLED && saveDb) { + const contact = await this.contactModel.findOneAndUpdate( + { id: data.id }, + { ...data }, + ); + return { insertCount: contact ? 1 : 0 }; + } + + const store = this.configService.get('STORE'); + + if (store.CONTACTS) { + this.writeStore({ + path: join(this.storePath, 'contacts', instanceName), + fileName: data.id, + data, + }); + + return { insertCount: 1 }; + } + + return { insertCount: 0 }; + } catch (error) { + return error; + } finally { + data = undefined; + } + } + public async find(query: ContactQuery): Promise { try { if (this.dbSettings.ENABLED) { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c90b0b54..94efbc91 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -770,7 +770,7 @@ export class WAStartupService { ); }, - 'contacts.update': async (contacts: Partial[]) => { + 'contacts.update': async (contacts: Partial[], database: Database) => { this.logger.verbose('Event received: contacts.update'); this.logger.verbose('Verifying if contacts exists in database to update'); @@ -782,6 +782,18 @@ export class WAStartupService { profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, owner: this.instance.wuid, }); + + this.logger.verbose('Updating contacts in database'); + await this.repository.contact.update( + { + id: contact.id, + pushName: contact?.name ?? contact?.verifiedName, + profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, + owner: this.instance.wuid, + }, + this.instance.name, + database.SAVE_DATA.CONTACTS, + ); } this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); @@ -909,6 +921,52 @@ export class WAStartupService { this.instance.name, database.SAVE_DATA.NEW_MESSAGE, ); + + this.logger.verbose('Verifying contact from message'); + const contact = await this.repository.contact.find({ + where: { owner: this.instance.wuid, id: received.key.remoteJid }, + }); + + if (contact?.length) { + this.logger.verbose('Contact found in database'); + const contactRaw: ContactRaw = { + id: received.key.remoteJid, + pushName: contact[0].pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)) + .profilePictureUrl, + owner: this.instance.wuid, + }; + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); + await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); + + this.logger.verbose('Updating contact in database'); + await this.repository.contact.update( + contactRaw, + this.instance.name, + database.SAVE_DATA.CONTACTS, + ); + return; + } + + this.logger.verbose('Contact not found in database'); + const contactRaw: ContactRaw = { + id: received.key.remoteJid, + pushName: received.pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)) + .profilePictureUrl, + owner: this.instance.wuid, + }; + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); + await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); + + this.logger.verbose('Inserting contact in database'); + await this.repository.contact.insert( + [contactRaw], + this.instance.name, + database.SAVE_DATA.CONTACTS, + ); }, 'messages.update': async (args: WAMessageUpdate[], database: Database) => { @@ -1072,7 +1130,7 @@ export class WAStartupService { if (events['contacts.update']) { this.logger.verbose('Listening event: contacts.update'); const payload = events['contacts.update']; - this.contactHandle['contacts.update'](payload); + this.contactHandle['contacts.update'](payload, database); } } }); @@ -1386,12 +1444,22 @@ export class WAStartupService { throw new BadRequestException('Content is required'); } - if ( - !status.statusJidList || - !Array.isArray(status.statusJidList) || - !status.statusJidList.length - ) { - throw new BadRequestException('Status jid list is required'); + if (status.allContacts) { + const contacts = await this.repository.contact.find({ + where: { owner: this.instance.wuid }, + }); + + if (!contacts.length) { + throw new BadRequestException('Contacts not found'); + } + + status.statusJidList = contacts + .filter((contact) => contact.pushName) + .map((contact) => contact.id); + } + + if (!status.statusJidList?.length && !status.allContacts) { + throw new BadRequestException('StatusJidList is required'); } if (status.type === 'text') { From 048bea376d28705c511202d7d707f3995bc76315 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 07:47:06 -0300 Subject: [PATCH 10/15] fix: Changed owner of the jid for instanceName --- CHANGELOG.md | 1 + src/whatsapp/repository/auth.repository.ts | 17 ++- src/whatsapp/repository/chat.repository.ts | 28 +++++ src/whatsapp/repository/contact.repository.ts | 86 +++++++++++++-- src/whatsapp/repository/message.repository.ts | 42 +++++-- .../repository/messageUp.repository.ts | 31 ++++++ src/whatsapp/repository/repository.manager.ts | 18 +++ src/whatsapp/repository/webhook.repository.ts | 20 ++++ src/whatsapp/services/whatsapp.service.ts | 103 +++++++++++------- 9 files changed, 284 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2885d0..41d3bb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Adjusted set in webhook to go empty when enabled false * Adjust in store files * Fixed the problem when do not save contacts when receive messages +* Changed owner of the jid for instanceName # 1.1.3 (2023-07-06 11:43) diff --git a/src/whatsapp/repository/auth.repository.ts b/src/whatsapp/repository/auth.repository.ts index 4b6d4d00..c795737c 100644 --- a/src/whatsapp/repository/auth.repository.ts +++ b/src/whatsapp/repository/auth.repository.ts @@ -4,7 +4,7 @@ import { IInsert, Repository } from '../abstract/abstract.repository'; import { IAuthModel, AuthRaw } from '../models'; import { readFileSync } from 'fs'; import { AUTH_DIR } from '../../config/path.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../config/logger.config'; export class AuthRepository extends Repository { constructor( @@ -16,24 +16,35 @@ export class AuthRepository extends Repository { } private readonly auth: Auth; + private readonly logger = new Logger('AuthRepository'); public async create(data: AuthRaw, instance: string): Promise { try { + this.logger.verbose('creating auth'); if (this.dbSettings.ENABLED) { + this.logger.verbose('saving auth to db'); const insert = await this.authModel.replaceOne( { _id: instance }, { ...data }, { upsert: true }, ); + + this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth'); return { insertCount: insert.modifiedCount }; } + this.logger.verbose('saving auth to store'); + this.writeStore({ path: join(AUTH_DIR, this.auth.TYPE), fileName: instance, data, }); + this.logger.verbose( + 'auth saved to store in path: ' + join(AUTH_DIR, this.auth.TYPE) + '/' + instance, + ); + this.logger.verbose('auth created'); return { insertCount: 1 }; } catch (error) { return { error } as any; @@ -42,10 +53,14 @@ export class AuthRepository extends Repository { public async find(instance: string): Promise { try { + this.logger.verbose('finding auth'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding auth in db'); return await this.authModel.findOne({ _id: instance }); } + this.logger.verbose('finding auth in store'); + return JSON.parse( readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), { encoding: 'utf-8', diff --git a/src/whatsapp/repository/chat.repository.ts b/src/whatsapp/repository/chat.repository.ts index f717ae2f..0f05760c 100644 --- a/src/whatsapp/repository/chat.repository.ts +++ b/src/whatsapp/repository/chat.repository.ts @@ -3,6 +3,7 @@ import { ConfigService, StoreConf } from '../../config/env.config'; import { IInsert, Repository } from '../abstract/abstract.repository'; import { opendirSync, readFileSync, rmSync } from 'fs'; import { ChatRaw, IChatModel } from '../models'; +import { Logger } from '../../config/logger.config'; export class ChatQuery { where: ChatRaw; @@ -16,35 +17,54 @@ export class ChatRepository extends Repository { super(configService); } + private readonly logger = new Logger('ChatRepository'); + public async insert( data: ChatRaw[], instanceName: string, saveDb = false, ): Promise { + this.logger.verbose('inserting chats'); if (data.length === 0) { + this.logger.verbose('no chats to insert'); return; } try { + this.logger.verbose('saving chats to store'); if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('saving chats to db'); const insert = await this.chatModel.insertMany([...data]); + + this.logger.verbose('chats saved to db: ' + insert.length + ' chats'); return { insertCount: insert.length }; } + this.logger.verbose('saving chats to store'); + const store = this.configService.get('STORE'); if (store.CHATS) { + this.logger.verbose('saving chats to store'); data.forEach((chat) => { this.writeStore({ path: join(this.storePath, 'chats', instanceName), fileName: chat.id, data: chat, }); + this.logger.verbose( + 'chats saved to store in path: ' + + join(this.storePath, 'chats', instanceName) + + '/' + + chat.id, + ); }); + this.logger.verbose('chats saved to store'); return { insertCount: data.length }; } + this.logger.verbose('chats not saved to store'); return { insertCount: 0 }; } catch (error) { return error; @@ -55,10 +75,14 @@ export class ChatRepository extends Repository { public async find(query: ChatQuery): Promise { try { + this.logger.verbose('finding chats'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding chats in db'); return await this.chatModel.find({ owner: query.where.owner }); } + this.logger.verbose('finding chats in store'); + const chats: ChatRaw[] = []; const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner)); for await (const dirent of openDir) { @@ -74,6 +98,7 @@ export class ChatRepository extends Repository { } } + this.logger.verbose('chats found in store: ' + chats.length + ' chats'); return chats; } catch (error) { return []; @@ -82,10 +107,13 @@ export class ChatRepository extends Repository { public async delete(query: ChatQuery) { try { + this.logger.verbose('deleting chats'); if (this.dbSettings.ENABLED) { + this.logger.verbose('deleting chats in db'); return await this.chatModel.deleteOne({ ...query.where }); } + this.logger.verbose('deleting chats in store'); rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), { force: true, recursive: true, diff --git a/src/whatsapp/repository/contact.repository.ts b/src/whatsapp/repository/contact.repository.ts index 67c51dff..648b5bf4 100644 --- a/src/whatsapp/repository/contact.repository.ts +++ b/src/whatsapp/repository/contact.repository.ts @@ -3,6 +3,7 @@ import { join } from 'path'; import { ConfigService, StoreConf } from '../../config/env.config'; import { ContactRaw, IContactModel } from '../models'; import { IInsert, Repository } from '../abstract/abstract.repository'; +import { Logger } from '../../config/logger.config'; export class ContactQuery { where: ContactRaw; @@ -16,35 +17,55 @@ export class ContactRepository extends Repository { super(configService); } + private readonly logger = new Logger('ContactRepository'); + public async insert( data: ContactRaw[], instanceName: string, saveDb = false, ): Promise { + this.logger.verbose('inserting contacts'); + if (data.length === 0) { + this.logger.verbose('no contacts to insert'); return; } try { if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('saving contacts to db'); + const insert = await this.contactModel.insertMany([...data]); + + this.logger.verbose('contacts saved to db: ' + insert.length + ' contacts'); return { insertCount: insert.length }; } + this.logger.verbose('saving contacts to store'); + const store = this.configService.get('STORE'); if (store.CONTACTS) { + this.logger.verbose('saving contacts to store'); data.forEach((contact) => { this.writeStore({ path: join(this.storePath, 'contacts', instanceName), fileName: contact.id, data: contact, }); + this.logger.verbose( + 'contacts saved to store in path: ' + + join(this.storePath, 'contacts', instanceName) + + '/' + + contact.id, + ); }); + this.logger.verbose('contacts saved to store: ' + data.length + ' contacts'); return { insertCount: data.length }; } + this.logger.verbose('contacts not saved'); return { insertCount: 0 }; } catch (error) { return error; @@ -54,31 +75,63 @@ export class ContactRepository extends Repository { } public async update( - data: ContactRaw, + data: ContactRaw[], instanceName: string, saveDb = false, ): Promise { try { - if (this.dbSettings.ENABLED && saveDb) { - const contact = await this.contactModel.findOneAndUpdate( - { id: data.id }, - { ...data }, - ); - return { insertCount: contact ? 1 : 0 }; + this.logger.verbose('updating contacts'); + + if (data.length === 0) { + this.logger.verbose('no contacts to update'); + return; } + if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('updating contacts in db'); + + const contacts = data.map((contact) => { + return { + updateOne: { + filter: { id: contact.id }, + update: { ...contact }, + upsert: true, + }, + }; + }); + + const { nModified } = await this.contactModel.bulkWrite(contacts); + + this.logger.verbose('contacts updated in db: ' + nModified + ' contacts'); + return { insertCount: nModified }; + } + + this.logger.verbose('updating contacts in store'); + const store = this.configService.get('STORE'); if (store.CONTACTS) { - this.writeStore({ - path: join(this.storePath, 'contacts', instanceName), - fileName: data.id, - data, + this.logger.verbose('updating contacts in store'); + data.forEach((contact) => { + this.writeStore({ + path: join(this.storePath, 'contacts', instanceName), + fileName: contact.id, + data: contact, + }); + this.logger.verbose( + 'contacts updated in store in path: ' + + join(this.storePath, 'contacts', instanceName) + + '/' + + contact.id, + ); }); - return { insertCount: 1 }; + this.logger.verbose('contacts updated in store: ' + data.length + ' contacts'); + + return { insertCount: data.length }; } + this.logger.verbose('contacts not updated'); return { insertCount: 0 }; } catch (error) { return error; @@ -89,11 +142,16 @@ export class ContactRepository extends Repository { public async find(query: ContactQuery): Promise { try { + this.logger.verbose('finding contacts'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding contacts in db'); return await this.contactModel.find({ ...query.where }); } + + this.logger.verbose('finding contacts in store'); const contacts: ContactRaw[] = []; if (query?.where?.id) { + this.logger.verbose('finding contacts in store by id'); contacts.push( JSON.parse( readFileSync( @@ -108,6 +166,8 @@ export class ContactRepository extends Repository { ), ); } else { + this.logger.verbose('finding contacts in store by owner'); + const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), { encoding: 'utf-8', }); @@ -124,6 +184,8 @@ export class ContactRepository extends Repository { } } } + + this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts'); return contacts; } catch (error) { return []; diff --git a/src/whatsapp/repository/message.repository.ts b/src/whatsapp/repository/message.repository.ts index 4da56e8a..dbfe01fc 100644 --- a/src/whatsapp/repository/message.repository.ts +++ b/src/whatsapp/repository/message.repository.ts @@ -3,6 +3,7 @@ import { join } from 'path'; import { IMessageModel, MessageRaw } from '../models'; import { IInsert, Repository } from '../abstract/abstract.repository'; import { opendirSync, readFileSync } from 'fs'; +import { Logger } from '../../config/logger.config'; export class MessageQuery { where: MessageRaw; @@ -17,17 +18,23 @@ export class MessageRepository extends Repository { super(configService); } + private readonly logger = new Logger('MessageRepository'); + public async insert( data: MessageRaw[], instanceName: string, saveDb = false, ): Promise { + this.logger.verbose('inserting messages'); + if (!Array.isArray(data) || data.length === 0) { + this.logger.verbose('no messages to insert'); return; } try { if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('saving messages to db'); const cleanedData = data.map((obj) => { const cleanedObj = { ...obj }; if ('extendedTextMessage' in obj.message) { @@ -48,23 +55,37 @@ export class MessageRepository extends Repository { }); const insert = await this.messageModel.insertMany([...cleanedData]); + + this.logger.verbose('messages saved to db: ' + insert.length + ' messages'); return { insertCount: insert.length }; } + this.logger.verbose('saving messages to store'); + const store = this.configService.get('STORE'); if (store.MESSAGES) { - data.forEach((msg) => - this.writeStore({ - path: join(this.storePath, 'messages', instanceName), - fileName: msg.key.id, - data: msg, - }), - ); + this.logger.verbose('saving messages to store'); + data.forEach((message) => { + this.writeStore({ + path: join(this.storePath, 'messages', instanceName), + fileName: message.key.id, + data: message, + }); + this.logger.verbose( + 'messages saved to store in path: ' + + join(this.storePath, 'messages', instanceName) + + '/' + + message.key.id, + ); + }); + + this.logger.verbose('messages saved to store: ' + data.length + ' messages'); return { insertCount: data.length }; } + this.logger.verbose('messages not saved to store'); return { insertCount: 0 }; } catch (error) { console.log('ERROR: ', error); @@ -76,21 +97,26 @@ export class MessageRepository extends Repository { public async find(query: MessageQuery) { try { + this.logger.verbose('finding messages'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding messages in db'); if (query?.where?.key) { for (const [k, v] of Object.entries(query.where.key)) { query.where['key.' + k] = v; } delete query?.where?.key; } + return await this.messageModel .find({ ...query.where }) .sort({ messageTimestamp: -1 }) .limit(query?.limit ?? 0); } + this.logger.verbose('finding messages in store'); const messages: MessageRaw[] = []; if (query?.where?.key?.id) { + this.logger.verbose('finding messages in store by id'); messages.push( JSON.parse( readFileSync( @@ -105,6 +131,7 @@ export class MessageRepository extends Repository { ), ); } else { + this.logger.verbose('finding messages in store by owner'); const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), { encoding: 'utf-8', }); @@ -123,6 +150,7 @@ export class MessageRepository extends Repository { } } + this.logger.verbose('messages found in store: ' + messages.length + ' messages'); return messages .sort((x, y) => { return (y.messageTimestamp as number) - (x.messageTimestamp as number); diff --git a/src/whatsapp/repository/messageUp.repository.ts b/src/whatsapp/repository/messageUp.repository.ts index f9a42ee0..6a9f9cc4 100644 --- a/src/whatsapp/repository/messageUp.repository.ts +++ b/src/whatsapp/repository/messageUp.repository.ts @@ -3,6 +3,7 @@ import { IMessageUpModel, MessageUpdateRaw } from '../models'; import { IInsert, Repository } from '../abstract/abstract.repository'; import { join } from 'path'; import { opendirSync, readFileSync } from 'fs'; +import { Logger } from '../../config/logger.config'; export class MessageUpQuery { where: MessageUpdateRaw; @@ -17,35 +18,54 @@ export class MessageUpRepository extends Repository { super(configService); } + private readonly logger = new Logger('MessageUpRepository'); + public async insert( data: MessageUpdateRaw[], instanceName: string, saveDb?: boolean, ): Promise { + this.logger.verbose('inserting message up'); + if (data.length === 0) { + this.logger.verbose('no message up to insert'); return; } try { if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('saving message up to db'); const insert = await this.messageUpModel.insertMany([...data]); + + this.logger.verbose('message up saved to db: ' + insert.length + ' message up'); return { insertCount: insert.length }; } + this.logger.verbose('saving message up to store'); + const store = this.configService.get('STORE'); if (store.MESSAGE_UP) { + this.logger.verbose('saving message up to store'); data.forEach((update) => { this.writeStore({ path: join(this.storePath, 'message-up', instanceName), fileName: update.id, data: update, }); + this.logger.verbose( + 'message up saved to store in path: ' + + join(this.storePath, 'message-up', instanceName) + + '/' + + update.id, + ); }); + this.logger.verbose('message up saved to store: ' + data.length + ' message up'); return { insertCount: data.length }; } + this.logger.verbose('message up not saved to store'); return { insertCount: 0 }; } catch (error) { return error; @@ -54,15 +74,21 @@ export class MessageUpRepository extends Repository { public async find(query: MessageUpQuery) { try { + this.logger.verbose('finding message up'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding message up in db'); return await this.messageUpModel .find({ ...query.where }) .sort({ datetime: -1 }) .limit(query?.limit ?? 0); } + this.logger.verbose('finding message up in store'); + const messageUpdate: MessageUpdateRaw[] = []; if (query?.where?.id) { + this.logger.verbose('finding message up in store by id'); + messageUpdate.push( JSON.parse( readFileSync( @@ -77,6 +103,8 @@ export class MessageUpRepository extends Repository { ), ); } else { + this.logger.verbose('finding message up in store by owner'); + const openDir = opendirSync( join(this.storePath, 'message-up', query.where.owner), { encoding: 'utf-8' }, @@ -96,6 +124,9 @@ export class MessageUpRepository extends Repository { } } + this.logger.verbose( + 'message up found in store: ' + messageUpdate.length + ' message up', + ); return messageUpdate .sort((x, y) => { return y.datetime - x.datetime; diff --git a/src/whatsapp/repository/repository.manager.ts b/src/whatsapp/repository/repository.manager.ts index eac7b18e..1e08213a 100644 --- a/src/whatsapp/repository/repository.manager.ts +++ b/src/whatsapp/repository/repository.manager.ts @@ -8,6 +8,7 @@ import { AuthRepository } from './auth.repository'; import { Auth, ConfigService, Database } from '../../config/env.config'; import { execSync } from 'child_process'; import { join } from 'path'; +import { Logger } from '../../config/logger.config'; export class RepositoryBroker { constructor( @@ -20,19 +21,26 @@ export class RepositoryBroker { private configService: ConfigService, dbServer?: MongoClient, ) { + this.logger.verbose('initializing repository broker'); this.dbClient = dbServer; this.__init_repo_without_db__(); } private dbClient?: MongoClient; + private readonly logger = new Logger('RepositoryBroker'); public get dbServer() { return this.dbClient; } private __init_repo_without_db__() { + this.logger.verbose('initializing repository without db'); if (!this.configService.get('DATABASE').ENABLED) { + this.logger.verbose('database is disabled'); + const storePath = join(process.cwd(), 'store'); + + this.logger.verbose('creating store path: ' + storePath); execSync( `mkdir -p ${join( storePath, @@ -40,10 +48,20 @@ export class RepositoryBroker { this.configService.get('AUTHENTICATION').TYPE, )}`, ); + + this.logger.verbose('creating chats path: ' + join(storePath, 'chats')); execSync(`mkdir -p ${join(storePath, 'chats')}`); + + this.logger.verbose('creating contacts path: ' + join(storePath, 'contacts')); execSync(`mkdir -p ${join(storePath, 'contacts')}`); + + this.logger.verbose('creating messages path: ' + join(storePath, 'messages')); execSync(`mkdir -p ${join(storePath, 'messages')}`); + + this.logger.verbose('creating message-up path: ' + join(storePath, 'message-up')); execSync(`mkdir -p ${join(storePath, 'message-up')}`); + + this.logger.verbose('creating webhook path: ' + join(storePath, 'webhook')); execSync(`mkdir -p ${join(storePath, 'webhook')}`); } } diff --git a/src/whatsapp/repository/webhook.repository.ts b/src/whatsapp/repository/webhook.repository.ts index 2a0c12a4..d9b34af1 100644 --- a/src/whatsapp/repository/webhook.repository.ts +++ b/src/whatsapp/repository/webhook.repository.ts @@ -3,6 +3,7 @@ import { ConfigService } from '../../config/env.config'; import { join } from 'path'; import { readFileSync } from 'fs'; import { IWebhookModel, WebhookRaw } from '../models'; +import { Logger } from '../../config/logger.config'; export class WebhookRepository extends Repository { constructor( @@ -12,23 +13,39 @@ export class WebhookRepository extends Repository { super(configService); } + private readonly logger = new Logger('WebhookRepository'); + public async create(data: WebhookRaw, instance: string): Promise { try { + this.logger.verbose('creating webhook'); if (this.dbSettings.ENABLED) { + this.logger.verbose('saving webhook to db'); const insert = await this.webhookModel.replaceOne( { _id: instance }, { ...data }, { upsert: true }, ); + + this.logger.verbose('webhook saved to db: ' + insert.modifiedCount + ' webhook'); return { insertCount: insert.modifiedCount }; } + this.logger.verbose('saving webhook to store'); + this.writeStore({ path: join(this.storePath, 'webhook'), fileName: instance, data, }); + this.logger.verbose( + 'webhook saved to store in path: ' + + join(this.storePath, 'webhook') + + '/' + + instance, + ); + + this.logger.verbose('webhook created'); return { insertCount: 1 }; } catch (error) { return error; @@ -37,10 +54,13 @@ export class WebhookRepository extends Repository { public async find(instance: string): Promise { try { + this.logger.verbose('finding webhook'); if (this.dbSettings.ENABLED) { + this.logger.verbose('finding webhook in db'); return await this.webhookModel.findOne({ _id: instance }); } + this.logger.verbose('finding webhook in store'); return JSON.parse( readFileSync(join(this.storePath, 'webhook', instance + '.json'), { encoding: 'utf-8', diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 94efbc91..f86c29cc 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -509,7 +509,7 @@ export class WAStartupService { this.logger.verbose('Getting message with key: ' + JSON.stringify(key)); try { const webMessageInfo = (await this.repository.message.find({ - where: { owner: this.instance.wuid, key: { id: key.id } }, + where: { owner: this.instance.name, key: { id: key.id } }, })) as unknown as proto.IWebMessageInfo[]; if (full) { this.logger.verbose('Returning full message'); @@ -677,7 +677,7 @@ export class WAStartupService { this.logger.verbose('Finding chats in database'); const chatsRepository = await this.repository.chat.find({ - where: { owner: this.instance.wuid }, + where: { owner: this.instance.name }, }); this.logger.verbose('Verifying if chats exists in database to insert'); @@ -726,7 +726,7 @@ export class WAStartupService { chats.forEach( async (chat) => await this.repository.chat.delete({ - where: { owner: this.instance.wuid, id: chat }, + where: { owner: this.instance.name, id: chat }, }), ); @@ -741,7 +741,7 @@ export class WAStartupService { this.logger.verbose('Finding contacts in database'); const contactsRepository = await this.repository.contact.find({ - where: { owner: this.instance.wuid }, + where: { owner: this.instance.name }, }); this.logger.verbose('Verifying if contacts exists in database to insert'); @@ -755,7 +755,7 @@ export class WAStartupService { id: contact.id, pushName: contact?.name || contact?.verifiedName, profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, - owner: this.instance.wuid, + owner: this.instance.name, }); } @@ -780,24 +780,19 @@ export class WAStartupService { id: contact.id, pushName: contact?.name ?? contact?.verifiedName, profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, - owner: this.instance.wuid, + owner: this.instance.name, }); - - this.logger.verbose('Updating contacts in database'); - await this.repository.contact.update( - { - id: contact.id, - pushName: contact?.name ?? contact?.verifiedName, - profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, - owner: this.instance.wuid, - }, - this.instance.name, - database.SAVE_DATA.CONTACTS, - ); } this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); + + this.logger.verbose('Updating contacts in database'); + await this.repository.contact.update( + contactsRaw, + this.instance.name, + database.SAVE_DATA.CONTACTS, + ); }, }; @@ -821,7 +816,7 @@ export class WAStartupService { const chatsRaw: ChatRaw[] = chats.map((chat) => { return { id: chat.id, - owner: this.instance.wuid, + owner: this.instance.name, lastMsgTimestamp: chat.lastMessageRecvTimestamp, }; }); @@ -839,7 +834,7 @@ export class WAStartupService { const messagesRaw: MessageRaw[] = []; const messagesRepository = await this.repository.message.find({ - where: { owner: this.instance.wuid }, + where: { owner: this.instance.name }, }); for await (const [, m] of Object.entries(messages)) { if (!m.message) { @@ -847,7 +842,7 @@ export class WAStartupService { } if ( messagesRepository.find( - (mr) => mr.owner === this.instance.wuid && mr.key.id === m.key.id, + (mr) => mr.owner === this.instance.name && mr.key.id === m.key.id, ) ) { continue; @@ -864,7 +859,7 @@ export class WAStartupService { message: { ...m.message }, messageType: getContentType(m.message), messageTimestamp: m.messageTimestamp as number, - owner: this.instance.wuid, + owner: this.instance.name, }); } @@ -906,7 +901,7 @@ export class WAStartupService { message: { ...received.message }, messageType: getContentType(received.message), messageTimestamp: received.messageTimestamp as number, - owner: this.instance.wuid, + owner: this.instance.name, source: getDevice(received.key.id), }; @@ -924,9 +919,22 @@ export class WAStartupService { this.logger.verbose('Verifying contact from message'); const contact = await this.repository.contact.find({ - where: { owner: this.instance.wuid, id: received.key.remoteJid }, + where: { owner: this.instance.name, id: received.key.remoteJid }, }); + const contactRaw: ContactRaw = { + id: received.key.remoteJid, + pushName: received.pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)) + .profilePictureUrl, + owner: this.instance.name, + }; + + if (contactRaw.id === 'status@broadcast') { + this.logger.verbose('Contact is status@broadcast'); + return; + } + if (contact?.length) { this.logger.verbose('Contact found in database'); const contactRaw: ContactRaw = { @@ -934,7 +942,7 @@ export class WAStartupService { pushName: contact[0].pushName, profilePictureUrl: (await this.profilePicture(received.key.remoteJid)) .profilePictureUrl, - owner: this.instance.wuid, + owner: this.instance.name, }; this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); @@ -942,7 +950,7 @@ export class WAStartupService { this.logger.verbose('Updating contact in database'); await this.repository.contact.update( - contactRaw, + [contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS, ); @@ -950,13 +958,6 @@ export class WAStartupService { } this.logger.verbose('Contact not found in database'); - const contactRaw: ContactRaw = { - id: received.key.remoteJid, - pushName: received.pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)) - .profilePictureUrl, - owner: this.instance.wuid, - }; this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); @@ -1000,7 +1001,7 @@ export class WAStartupService { ...key, status: status[update.status], datetime: Date.now(), - owner: this.instance.wuid, + owner: this.instance.name, pollUpdates, }; @@ -1436,6 +1437,8 @@ export class WAStartupService { } private async formatStatusMessage(status: StatusMessage) { + this.logger.verbose('Formatting status message'); + if (!status.type) { throw new BadRequestException('Type is required'); } @@ -1445,17 +1448,23 @@ export class WAStartupService { } if (status.allContacts) { + this.logger.verbose('All contacts defined as true'); + + this.logger.verbose('Getting contacts from database'); const contacts = await this.repository.contact.find({ - where: { owner: this.instance.wuid }, + where: { owner: this.instance.name }, }); if (!contacts.length) { throw new BadRequestException('Contacts not found'); } + this.logger.verbose('Getting contacts with push name'); status.statusJidList = contacts .filter((contact) => contact.pushName) .map((contact) => contact.id); + + this.logger.verbose(status.statusJidList); } if (!status.statusJidList?.length && !status.allContacts) { @@ -1463,6 +1472,8 @@ export class WAStartupService { } if (status.type === 'text') { + this.logger.verbose('Type defined as text'); + if (!status.backgroundColor) { throw new BadRequestException('Background color is required'); } @@ -1483,6 +1494,8 @@ export class WAStartupService { }; } if (status.type === 'image') { + this.logger.verbose('Type defined as image'); + return { content: { image: { @@ -1496,6 +1509,8 @@ export class WAStartupService { }; } if (status.type === 'video') { + this.logger.verbose('Type defined as video'); + return { content: { video: { @@ -1509,8 +1524,12 @@ export class WAStartupService { }; } if (status.type === 'audio') { + this.logger.verbose('Type defined as audio'); + + this.logger.verbose('Processing audio'); const convert = await this.processAudio(status.content, 'status@broadcast'); if (typeof convert === 'string') { + this.logger.verbose('Audio processed'); const audio = fs.readFileSync(convert).toString('base64'); const result = { @@ -2097,11 +2116,11 @@ export class WAStartupService { public async fetchContacts(query: ContactQuery) { this.logger.verbose('Fetching contacts'); if (query?.where) { - query.where.owner = this.instance.wuid; + query.where.owner = this.instance.name; } else { query = { where: { - owner: this.instance.wuid, + owner: this.instance.name, }, }; } @@ -2111,11 +2130,11 @@ export class WAStartupService { public async fetchMessages(query: MessageQuery) { this.logger.verbose('Fetching messages'); if (query?.where) { - query.where.owner = this.instance.wuid; + query.where.owner = this.instance.name; } else { query = { where: { - owner: this.instance.wuid, + owner: this.instance.name, }, limit: query?.limit, }; @@ -2126,11 +2145,11 @@ export class WAStartupService { public async fetchStatusMessage(query: MessageUpQuery) { this.logger.verbose('Fetching status messages'); if (query?.where) { - query.where.owner = this.instance.wuid; + query.where.owner = this.instance.name; } else { query = { where: { - owner: this.instance.wuid, + owner: this.instance.name, }, limit: query?.limit, }; @@ -2140,7 +2159,7 @@ export class WAStartupService { public async fetchChats() { this.logger.verbose('Fetching chats'); - return await this.repository.chat.find({ where: { owner: this.instance.wuid } }); + return await this.repository.chat.find({ where: { owner: this.instance.name } }); } public async fetchPrivacySettings() { From c973730accf31d91d42636c786d590f0db6f1a6f Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 10:50:20 -0300 Subject: [PATCH 11/15] fix: Create .env for installation in docker --- CHANGELOG.md | 1 + Docker/.env | 87 +++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yaml | 80 ++--------------------------------------- 3 files changed, 90 insertions(+), 78 deletions(-) create mode 100644 Docker/.env diff --git a/CHANGELOG.md b/CHANGELOG.md index 41d3bb85..1dfc9425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Adjust in store files * Fixed the problem when do not save contacts when receive messages * Changed owner of the jid for instanceName +* Create .env for installation in docker # 1.1.3 (2023-07-06 11:43) diff --git a/Docker/.env b/Docker/.env new file mode 100644 index 00000000..f786d341 --- /dev/null +++ b/Docker/.env @@ -0,0 +1,87 @@ +CORS_ORIGIN='*' # Or separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' +CORS_METHODS='POST,GET,PUT,DELETE' +CORS_CREDENTIALS=true + +# Determine the logs to be displayed +LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS' +LOG_COLOR=true +LOG_BAILEYS=error + +# Determine how long the instance should be deleted from memory in case of no connection. +# Default time: 5 minutes +# If you don't even want an expiration, enter the value false +DEL_INSTANCE=false + +# Temporary data storage +STORE_MESSAGES=true +STORE_MESSAGE_UP=true +STORE_CONTACTS=true +STORE_CHATS=true +CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h +CLEAN_STORE_MESSAGES=true +CLEAN_STORE_MESSAGE_UP=true +CLEAN_STORE_CONTACTS=true +CLEAN_STORE_CHATS=true + +# Permanent data storage +DATABASE_ENABLED=false +DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true +DATABASE_CONNECTION_DB_PREFIX_NAME=evolution + +DATABASE_SAVE_DATA_INSTANCE=true +DATABASE_SAVE_DATA_OLD_MESSAGE=false +DATABASE_SAVE_DATA_NEW_MESSAGE=true +DATABASE_SAVE_MESSAGE_UPDATE=true +DATABASE_SAVE_DATA_CONTACTS=true +DATABASE_SAVE_DATA_CHATS=true + +REDIS_ENABLED=false +REDIS_URI=redis://redis:6379/1 +REDIS_PREFIX_KEY=evolution + +# Webhook Settings +## Define a global webhook that will listen for enabled events from all instances +WEBHOOK_GLOBAL_URL= +WEBHOOK_GLOBAL_ENABLED=false +# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event +WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false +## Set the events you want to hear +WEBHOOK_EVENTS_APPLICATION_STARTUP=false +WEBHOOK_EVENTS_QRCODE_UPDATED=true +WEBHOOK_EVENTS_MESSAGES_SET=true +WEBHOOK_EVENTS_MESSAGES_UPSERT=true +WEBHOOK_EVENTS_MESSAGES_UPDATE=true +WEBHOOK_EVENTS_CONTACTS_SET=true +WEBHOOK_EVENTS_CONTACTS_UPSERT=true +WEBHOOK_EVENTS_CONTACTS_UPDATE=true +WEBHOOK_EVENTS_PRESENCE_UPDATE=true +WEBHOOK_EVENTS_CHATS_SET=true +WEBHOOK_EVENTS_CHATS_UPSERT=true +WEBHOOK_EVENTS_CHATS_UPDATE=true +WEBHOOK_EVENTS_CHATS_DELETE=true +WEBHOOK_EVENTS_GROUPS_UPSERT=true +WEBHOOK_EVENTS_GROUPS_UPDATE=true +WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true +WEBHOOK_EVENTS_CONNECTION_UPDATE=true +# This event fires every time a new token is requested via the refresh route +WEBHOOK_EVENTS_NEW_JWT_TOKEN=true + +CONFIG_SESSION_PHONE_CLIENT=Evolution API +CONFIG_SESSION_PHONE_NAME=chrome # chrome | firefox | edge | opera | safari + +# Set qrcode display limit +QRCODE_LIMIT=30 + +# Defines an authentication type for the api +AUTHENTICATION_TYPE='apikey' # jwt or 'apikey' +## Define a global apikey to access all instances. +### OBS: This key must be inserted in the request header to create an instance. +AUTHENTICATION_API_KEY='B6D711FCDE4D4FD5936544120E713976' +AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true +## Set the secret key to encrypt and decrypt your token and its expiration time +AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires +AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' + +AUTHENTICATION_INSTANCE_MODE=server +AUTHENTICATION_INSTANCE_NAME=evolution +AUTHENTICATION_INSTANCE_WEBHOOK_URL= \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index ad6921e5..839f830b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,84 +13,8 @@ services: volumes: - evolution_instances:/evolution/instances - evolution_store:/evolution/store - environment: - - LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS - - LOG_BAILEYS=error - # Determine how long the instance should be deleted from memory in case of no connection. - # Default time: 5 minutes - # If you don't even want an expiration, enter the value false - - DEL_INSTANCE=false # 5 or false - # Temporary data storage - - STORE_MESSAGES=true - - STORE_MESSAGE_UP=true - - STORE_CONTACTS=true - - STORE_CHATS=true - - CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h - - CLEAN_STORE_MESSAGES=true - - CLEAN_STORE_MESSAGE_UP=true - - CLEAN_STORE_CONTACTS=true - - CLEAN_STORE_CHATS=true - # Permanent data storage - - DATABASE_ENABLED=false - - DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true - - DATABASE_CONNECTION_DB_PREFIX_NAME=evolution - # Choose the data you want to save in the application's database or store - - DATABASE_SAVE_DATA_INSTANCE=true - - DATABASE_SAVE_DATA_OLD_MESSAGE=false - - DATABASE_SAVE_DATA_NEW_MESSAGE=true - - DATABASE_SAVE_MESSAGE_UPDATE=true - - DATABASE_SAVE_DATA_CONTACTS=true - - DATABASE_SAVE_DATA_CHATS=true - - REDIS_ENABLED=true - - REDIS_URI=redis://redis:6379/1 - - REDIS_PREFIX_KEY=evolution - # Webhook Settings - # Define a global webhook that will listen for enabled events from all instances - - WEBHOOK_GLOBAL_URL= - - WEBHOOK_GLOBAL_ENABLED=false - # With this option activated, you work with a url per webhook event, respecting the global url and the name of each event - - WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false - # Automatically maps webhook paths - # Set the events you want to hear - - WEBHOOK_EVENTS_APPLICATION_STARTUP=false - - WEBHOOK_EVENTS_QRCODE_UPDATED=true - - WEBHOOK_EVENTS_MESSAGES_SET=true - - WEBHOOK_EVENTS_MESSAGES_UPSERT=true - - WEBHOOK_EVENTS_MESSAGES_UPDATE=true - - WEBHOOK_EVENTS_CONTACTS_SET=true - - WEBHOOK_EVENTS_CONTACTS_UPSERT=true - - WEBHOOK_EVENTS_CONTACTS_UPDATE=true - - WEBHOOK_EVENTS_PRESENCE_UPDATE=true - - WEBHOOK_EVENTS_CHATS_SET=true - - WEBHOOK_EVENTS_CHATS_UPSERT=true - - WEBHOOK_EVENTS_CHATS_UPDATE=true - - WEBHOOK_EVENTS_CHATS_DELETE=true - - WEBHOOK_EVENTS_GROUPS_UPSERT=true - - WEBHOOK_EVENTS_GROUPS_UPDATE=true - - WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true - - WEBHOOK_EVENTS_CONNECTION_UPDATE=true - # This event fires every time a new token is requested via the refresh route - - WEBHOOK_EVENTS_NEW_JWT_TOKEN=true - # Name that will be displayed on smartphone connection - - CONFIG_SESSION_PHONE_CLIENT=Evolution API - - CONFIG_SESSION_PHONE_NAME=chrome # chrome | firefox | edge | opera | safari - # Set qrcode display limit - - QRCODE_LIMIT=30 - # Defines an authentication type for the api - - AUTHENTICATION_TYPE=apikey # jwt or apikey - # Define a global apikey to access all instances - # OBS: This key must be inserted in the request header to create an instance. - - AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976 - # Expose the api key on return from fetch instances - - AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true - # Set the secret key to encrypt and decrypt your token and its expiration time. - - AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s === 1h | zero (0) - never expires - # Set the instance name and webhook url to create an instance in init the application - # With this option activated, you work with a url per webhook event, respecting the local url and the name of each event - - AUTHENTICATION_INSTANCE_MODE=server # container or server - # if you are using container mode, set the container name and the webhook url to default instance - - AUTHENTICATION_INSTANCE_NAME=evolution - - AUTHENTICATION_INSTANCE_WEBHOOK_URL= + env_file: + - ./Docker/.env command: ['node', './dist/src/main.js'] networks: - evolution-net From 26a99d36967293dfa55b1e797c99852aeeb9a6fc Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 10:53:48 -0300 Subject: [PATCH 12/15] fix: Create .env for installation in docker --- docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index 839f830b..45345f32 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,6 +8,7 @@ services: api: container_name: evolution_api image: evolution/api:local + restart: always ports: - 8080:8080 volumes: From 14ad09e86702e43e95b0d06a4504cc4a235a4e3d Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 10:56:57 -0300 Subject: [PATCH 13/15] fix: Create .env for installation in docker --- .gitignore | 2 ++ Docker/{.env => .env.example} | 0 2 files changed, 2 insertions(+) rename Docker/{.env => .env.example} (100%) diff --git a/.gitignore b/.gitignore index 6e059b6c..07e3ff3c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /dist /node_modules +# /Docker/.env + # Logs logs/**.json *.log diff --git a/Docker/.env b/Docker/.env.example similarity index 100% rename from Docker/.env rename to Docker/.env.example From b1769edb655d0f3c58b0f9e38388cf81c3e15644 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 11:00:33 -0300 Subject: [PATCH 14/15] fix: Create .env for installation in docker --- .gitignore | 2 +- Docker/.env.example | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 07e3ff3c..e707b33e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ /dist /node_modules -# /Docker/.env +/Docker/.env # Logs logs/**.json diff --git a/Docker/.env.example b/Docker/.env.example index f786d341..b258a83e 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -5,7 +5,7 @@ CORS_CREDENTIALS=true # Determine the logs to be displayed LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS' LOG_COLOR=true -LOG_BAILEYS=error +LOG_BAILEYS=error # "fatal" | "error" | "warn" | "info" | "debug" | "trace" # Determine how long the instance should be deleted from memory in case of no connection. # Default time: 5 minutes @@ -28,6 +28,7 @@ DATABASE_ENABLED=false DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true DATABASE_CONNECTION_DB_PREFIX_NAME=evolution +# Choose the data you want to save in the application's database or store DATABASE_SAVE_DATA_INSTANCE=true DATABASE_SAVE_DATA_OLD_MESSAGE=false DATABASE_SAVE_DATA_NEW_MESSAGE=true @@ -41,7 +42,7 @@ REDIS_PREFIX_KEY=evolution # Webhook Settings ## Define a global webhook that will listen for enabled events from all instances -WEBHOOK_GLOBAL_URL= +WEBHOOK_GLOBAL_URL='' WEBHOOK_GLOBAL_ENABLED=false # With this option activated, you work with a url per webhook event, respecting the global url and the name of each event WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false @@ -66,6 +67,7 @@ WEBHOOK_EVENTS_CONNECTION_UPDATE=true # This event fires every time a new token is requested via the refresh route WEBHOOK_EVENTS_NEW_JWT_TOKEN=true +# Name that will be displayed on smartphone connection CONFIG_SESSION_PHONE_CLIENT=Evolution API CONFIG_SESSION_PHONE_NAME=chrome # chrome | firefox | edge | opera | safari @@ -81,7 +83,9 @@ AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true ## Set the secret key to encrypt and decrypt your token and its expiration time AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' - -AUTHENTICATION_INSTANCE_MODE=server +# Set the instance name and webhook url to create an instance in init the application +# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event +AUTHENTICATION_INSTANCE_MODE=server # container or server +# if you are using container mode, set the container name and the webhook url to default instance AUTHENTICATION_INSTANCE_NAME=evolution AUTHENTICATION_INSTANCE_WEBHOOK_URL= \ No newline at end of file From c7600ff059789717ec988f8e49f3229a461be8d8 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Sat, 8 Jul 2023 11:01:10 -0300 Subject: [PATCH 15/15] fix: Create .env for installation in docker --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dfc9425..a9966b51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 1.1.4 (homolog) +# 1.1.4 (2023-07-08 11:01) ### Features