diff --git a/CHANGELOG.md b/CHANGELOG.md index 80613150..9a6f1781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Feature * Added AWS SQS Integration * Added compatibility with typebot v2 +* Added endpoint sendPresence ### Fixed @@ -16,6 +17,8 @@ * Removed await from webhook when sending a message * Update typebot.service.ts - element.underline change ~ for * * Adjusts in proxy +* Removed api restart on receiving an error +* Fixes in mongodb and chatwoot # 1.5.4 (2023-10-09 20:43) diff --git a/src/config/error.config.ts b/src/config/error.config.ts index 7a6717da..dbba0d6a 100644 --- a/src/config/error.config.ts +++ b/src/config/error.config.ts @@ -8,7 +8,7 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - process.exit(1); + // process.exit(1); }); process.on('unhandledRejection', (error, origin) => { @@ -18,6 +18,6 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - process.exit(1); + // process.exit(1); }); } diff --git a/src/utils/use-multi-file-auth-state-db.ts b/src/utils/use-multi-file-auth-state-db.ts index 237b15d0..995ac92a 100644 --- a/src/utils/use-multi-file-auth-state-db.ts +++ b/src/utils/use-multi-file-auth-state-db.ts @@ -101,7 +101,7 @@ export async function useMultiFileAuthStateDb( }, }, saveCreds: async () => { - return writeData(creds, 'creds'); + return await writeData(creds, 'creds'); }, }; } diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 463f81d7..5523ccf2 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -987,6 +987,10 @@ export class ChatwootService { const state = waInstance?.connectionStatus?.state; if (state !== 'open') { + if (state === 'close') { + this.logger.verbose('request cleaning up instance: ' + instance.instanceName); + await this.waMonitor.cleaningUp(instance.instanceName); + } this.logger.verbose('connect to whatsapp'); const number = command.split(':')[1]; await waInstance.connectToWhatsapp(number); @@ -1272,13 +1276,6 @@ export class ChatwootService { public async eventWhatsapp(event: string, instance: InstanceDto, body: any) { this.logger.verbose('event whatsapp to instance: ' + instance.instanceName); try { - const client = await this.clientCw(instance); - - if (!client) { - this.logger.warn('client not found'); - return null; - } - const waInstance = this.waMonitor.waInstances[instance.instanceName]; if (!waInstance) { @@ -1286,6 +1283,13 @@ export class ChatwootService { return null; } + const client = await this.clientCw(instance); + + if (!client) { + this.logger.warn('client not found'); + return null; + } + if (event === 'messages.upsert' || event === 'send.message') { this.logger.verbose('event messages.upsert'); @@ -1535,16 +1539,18 @@ export class ChatwootService { await this.createBotMessage(instance, msgStatus, 'incoming'); } - // if (event === 'connection.update') { - // this.logger.verbose('event connection.update'); + if (event === 'connection.update') { + this.logger.verbose('event connection.update'); - // if (body.status === 'open') { - // const msgConnection = `🚀 Connection successfully established!`; - - // this.logger.verbose('send message to chatwoot'); - // await this.createBotMessage(instance, msgConnection, 'incoming'); - // } - // } + if (body.status === 'open') { + // if we have qrcode count then we understand that a new connection was established + if (this.waMonitor.waInstances[instance.instanceName].qrCode.count > 0) { + const msgConnection = `🚀 Connection successfully established!`; + this.logger.verbose('send message to chatwoot'); + await this.createBotMessage(instance, msgConnection, 'incoming'); + } + } + } if (event === 'qrcode.updated') { this.logger.verbose('event qrcode.updated'); diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 45cd4a1f..766569de 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -13,11 +13,11 @@ import { RedisCache } from '../../libs/redis.client'; import { AuthModel, ChamaaiModel, - ChatModel, + // ChatModel, ChatwootModel, - ContactModel, - MessageModel, - MessageUpModel, + // ContactModel, + // MessageModel, + // MessageUpModel, ProxyModel, RabbitmqModel, SettingsModel, @@ -39,7 +39,7 @@ export class WAMonitoringService { this.removeInstance(); this.noConnection(); - this.delInstanceFiles(); + // this.delInstanceFiles(); Object.assign(this.db, configService.get('DATABASE')); Object.assign(this.redis, configService.get('REDIS')); @@ -193,6 +193,13 @@ export class WAMonitoringService { public async cleaningUp(instanceName: string) { this.logger.verbose('cleaning up instance: ' + instanceName); + if (this.redis.ENABLED) { + this.logger.verbose('cleaning up instance in redis: ' + instanceName); + this.cache.reference = instanceName; + await this.cache.delAll(); + return; + } + if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { this.logger.verbose('cleaning up instance in database: ' + instanceName); await this.repository.dbServer.connect(); @@ -203,13 +210,6 @@ export class WAMonitoringService { return; } - if (this.redis.ENABLED) { - this.logger.verbose('cleaning up instance in redis: ' + instanceName); - this.cache.reference = instanceName; - await this.cache.delAll(); - return; - } - this.logger.verbose('cleaning up instance in files: ' + instanceName); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); } @@ -239,10 +239,10 @@ export class WAMonitoringService { this.logger.verbose('cleaning store database instance: ' + instanceName); - await ChatModel.deleteMany({ owner: instanceName }); - await ContactModel.deleteMany({ owner: instanceName }); - await MessageUpModel.deleteMany({ owner: instanceName }); - await MessageModel.deleteMany({ owner: instanceName }); + // await ChatModel.deleteMany({ owner: instanceName }); + // await ContactModel.deleteMany({ owner: instanceName }); + // await MessageUpModel.deleteMany({ owner: instanceName }); + // await MessageModel.deleteMany({ owner: instanceName }); await AuthModel.deleteMany({ _id: instanceName }); await WebhookModel.deleteMany({ _id: instanceName }); @@ -357,8 +357,8 @@ export class WAMonitoringService { this.eventEmitter.on('logout.instance', async (instanceName: string) => { this.logger.verbose('logout instance: ' + instanceName); try { - this.logger.verbose('request cleaning up instance: ' + instanceName); - this.cleaningUp(instanceName); + // this.logger.verbose('request cleaning up instance: ' + instanceName); + // this.cleaningUp(instanceName); } finally { this.logger.warn(`Instance "${instanceName}" - LOGOUT`); } diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 4ed9b04b..63fcebed 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -246,7 +246,7 @@ export class TypebotService { prefilledVariables: { ...data.prefilledVariables, remoteJid: data.remoteJid, - pushName: data.pushName || '', + pushName: data.pushName || data.prefilledVariables?.pushName || '', instanceName: instance.instanceName, }, }, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 5c3edc44..630c6a27 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1336,7 +1336,7 @@ export class WAStartupService { msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, - syncFullHistory: true, + syncFullHistory: false, userDevicesCache: this.userDevicesCache, transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { @@ -1409,6 +1409,7 @@ export class WAStartupService { browser, version, markOnlineOnConnect: this.localSettings.always_online, + retryRequestDelayMs: 10, connectTimeoutMs: 60_000, qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, @@ -1416,9 +1417,9 @@ export class WAStartupService { msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, - syncFullHistory: true, + syncFullHistory: false, userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, + transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); if (requiresPatch) { @@ -1637,157 +1638,165 @@ export class WAStartupService { database: Database, settings: SettingsRaw, ) => { - this.logger.verbose('Event received: messages.upsert'); - const received = messages[0]; + try { + this.logger.verbose('Event received: messages.upsert'); + for (const received of messages) { + if ( + (type !== 'notify' && type !== 'append') || + received.message?.protocolMessage || + received.message?.pollUpdateMessage + ) { + this.logger.verbose('message rejected'); + return; + } - if (type !== 'notify' || received.message?.protocolMessage || received.message?.pollUpdateMessage) { - this.logger.verbose('message rejected'); - return; - } + if (Long.isLong(received.messageTimestamp)) { + received.messageTimestamp = received.messageTimestamp?.toNumber(); + } - if (Long.isLong(received.messageTimestamp)) { - received.messageTimestamp = received.messageTimestamp?.toNumber(); - } + if (settings?.groups_ignore && received.key.remoteJid.includes('@g.us')) { + this.logger.verbose('group ignored'); + return; + } - if (settings?.groups_ignore && received.key.remoteJid.includes('@g.us')) { - this.logger.verbose('group ignored'); - return; - } + let messageRaw: MessageRaw; - let messageRaw: MessageRaw; + if ( + (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || + received?.message?.imageMessage + ) { + const buffer = await downloadMediaMessage( + { key: received.key, message: received?.message }, + 'buffer', + {}, + { + logger: P({ level: 'error' }) as any, + reuploadRequest: this.client.updateMediaMessage, + }, + ); + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { + ...received.message, + base64: buffer ? buffer.toString('base64') : undefined, + }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } else { + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { ...received.message }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } - if ( - (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || - received?.message?.imageMessage - ) { - const buffer = await downloadMediaMessage( - { key: received.key, message: received?.message }, - 'buffer', - {}, - { - logger: P({ level: 'error' }) as any, - reuploadRequest: this.client.updateMediaMessage, - }, - ); - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { - ...received.message, - base64: buffer ? buffer.toString('base64') : undefined, - }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } else { - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } + if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { + await this.client.readMessages([received.key]); + } - if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { - await this.client.readMessages([received.key]); - } + if (this.localSettings.read_status && received.key.id === 'status@broadcast') { + await this.client.readMessages([received.key]); + } - if (this.localSettings.read_status && received.key.id === 'status@broadcast') { - await this.client.readMessages([received.key]); - } + this.logger.log(messageRaw); - this.logger.log(messageRaw); + this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); + this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); - this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + if (this.localChatwoot.enabled) { + await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name }, + messageRaw, + ); + } - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name }, - messageRaw, - ); - } - - const typebotSessionRemoteJid = this.localTypebot.sessions?.find( - (session) => session.remoteJid === received.key.remoteJid, - ); - - if (this.localTypebot.enabled || typebotSessionRemoteJid) { - if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { - await this.typebotService.sendTypebot( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, + const typebotSessionRemoteJid = this.localTypebot.sessions?.find( + (session) => session.remoteJid === received.key.remoteJid, ); + + if ((this.localTypebot.enabled && type === 'notify') || typebotSessionRemoteJid) { + if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { + await this.typebotService.sendTypebot( + { instanceName: this.instance.name }, + messageRaw.key.remoteJid, + messageRaw, + ); + } + } + + if (this.localChamaai.enabled && messageRaw.key.fromMe === false && type === 'notify') { + await this.chamaaiService.sendChamaai( + { instanceName: this.instance.name }, + messageRaw.key.remoteJid, + messageRaw, + ); + } + + this.logger.verbose('Inserting message in database'); + await this.repository.message.insert([messageRaw], 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.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 = { + id: received.key.remoteJid, + pushName: contact[0].pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, + owner: this.instance.name, + }; + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); + this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); + + if (this.localChatwoot.enabled) { + await this.chatwootService.eventWhatsapp( + Events.CONTACTS_UPDATE, + { instanceName: this.instance.name }, + 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'); + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); + this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); + + this.logger.verbose('Inserting contact in database'); + this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); } + } catch (error) { + this.logger.error(error); } - - if (this.localChamaai.enabled && messageRaw.key.fromMe === false) { - await this.chamaaiService.sendChamaai( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], 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.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 = { - id: received.key.remoteJid, - pushName: contact[0].pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, - owner: this.instance.name, - }; - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); - - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.CONTACTS_UPDATE, - { instanceName: this.instance.name }, - 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'); - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); - - this.logger.verbose('Inserting contact in database'); - this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); }, 'messages.update': async (args: WAMessageUpdate[], database: Database, settings: SettingsRaw) => {