From 4d9f6ef4167a3383d7045ccfd31356913f1eaf15 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 8 Aug 2024 22:09:23 -0300 Subject: [PATCH] fix: import contacts with image in chatwoot --- .env.example | 7 +- CHANGELOG.md | 6 ++ .../channels/whatsapp.baileys.service.ts | 23 ++++-- .../channels/whatsapp.business.service.ts | 78 ++++++++++++++++--- src/utils/sendTelemetry.ts | 1 - 5 files changed, 95 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index 13f3ee1b..5d112db8 100644 --- a/.env.example +++ b/.env.example @@ -117,9 +117,10 @@ TYPEBOT_SEND_MEDIA_BASE64=true TYPEBOT_API_VERSION=latest CHATWOOT_ENABLED=false -CHATWOOT_MESSAGE_READ=false -CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://user:pass@host:5432/dbname -CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=false +CHATWOOT_MESSAGE_READ=true +CHATWOOT_MESSAGE_DELETE=true +CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://user:passwprd@host:5432/chatwoot?sslmode=disable +CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=true OPENAI_ENABLED=false OPENAI_API_KEY_GLOBAL= diff --git a/CHANGELOG.md b/CHANGELOG.md index e435c745..e9a571cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.0.9-rc (release candidate) + +### Fixed + +* Import contacts with image in chatwoot + # 2.0.8-rc (2024-08-08 20:23) ### Features diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 75ae26c5..8b67e145 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -818,14 +818,25 @@ export class BaileysStartupService extends ChannelStartupService { if (updatedContacts.length > 0) { await Promise.all( - updatedContacts.map((contact) => - this.prismaRepository.contact.updateMany({ + updatedContacts.map(async function (contact) { + const update = this.prismaRepository.contact.updateMany({ where: { remoteJid: contact.remoteJid, instanceId: this.instanceId }, data: { profilePicUrl: contact.profilePicUrl, }, - }), - ), + }); + + const instance = { instanceName: this.instance.name, instanceId: this.instance.id }; + + const findParticipant = await this.findContact(instance, contact.remoteJid.split('@')[0]); + + this.chatwootService.updateContact(instance, findParticipant.id, { + name: contact.pushName, + avatar_url: contact.profilePicUrl, + }); + + return update; + }), ); } } catch (error) { @@ -1636,10 +1647,12 @@ export class BaileysStartupService extends ChannelStartupService { public async profilePicture(number: string) { const jid = this.createJid(number); + const profilePictureUrl = await this.client.profilePictureUrl(jid, 'image'); + try { return { wuid: jid, - profilePictureUrl: await this.client.profilePictureUrl(jid, 'image'), + profilePictureUrl, }; } catch (error) { return { diff --git a/src/api/services/channels/whatsapp.business.service.ts b/src/api/services/channels/whatsapp.business.service.ts index 87c9e138..dbab3edb 100644 --- a/src/api/services/channels/whatsapp.business.service.ts +++ b/src/api/services/channels/whatsapp.business.service.ts @@ -4,8 +4,9 @@ import EventEmitter2 from 'eventemitter2'; import FormData from 'form-data'; import { createReadStream } from 'fs'; import { getMIMEType } from 'node-mime-types'; +import { join } from 'path'; -import { Chatwoot, ConfigService, Database, Dify, Openai, Typebot, WaBusiness } from '../../../config/env.config'; +import { Chatwoot, ConfigService, Database, Dify, Openai, S3, Typebot, WaBusiness } from '../../../config/env.config'; import { BadRequestException, InternalServerErrorException } from '../../../exceptions'; import { NumberBusiness } from '../../dto/chat.dto'; import { @@ -22,6 +23,7 @@ import { SendTemplateDto, SendTextDto, } from '../../dto/sendMessage.dto'; +import * as s3Service from '../../integrations/s3/libs/minio.server'; import { ProviderFiles } from '../../provider/sessions'; import { PrismaRepository } from '../../repository/repository.service'; import { Events, wa } from '../../types/wa.types'; @@ -316,20 +318,78 @@ export class BusinessStartupService extends ChannelStartupService { received?.messages[0].audio || received?.messages[0].video ) { - const buffer = await this.downloadMediaMessage(received?.messages[0]); messageRaw = { key, pushName, - message: { - ...this.messageMediaJson(received), - base64: buffer ? buffer.toString('base64') : undefined, - }, + message: this.messageMediaJson(received), contextInfo: this.messageMediaJson(received)?.contextInfo, messageType: this.renderMessageType(received.messages[0].type), messageTimestamp: parseInt(received.messages[0].timestamp) as number, source: 'unknown', instanceId: this.instanceId, }; + + if (this.configService.get('S3').ENABLE) { + try { + const message: any = received; + + const id = message[message.type].id; + let urlServer = this.configService.get('WA_BUSINESS').URL; + const version = this.configService.get('WA_BUSINESS').VERSION; + urlServer = `${urlServer}/${version}/${id}`; + const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${this.token}` }; + const result = await axios.get(urlServer, { headers }); + + const buffer = await axios.get(result.data.url, { headers, responseType: 'arraybuffer' }); + + const mediaType = message.messages[0].document + ? 'document' + : message.messages[0].image + ? 'image' + : message.messages[0].audio + ? 'audio' + : 'video'; + + const mimetype = result.headers['content-type']; + + const contentDisposition = result.headers['content-disposition']; + let fileName = `${message.messages[0].id}.${mimetype.split('/')[1]}`; + if (contentDisposition) { + const match = contentDisposition.match(/filename="(.+?)"/); + if (match) { + fileName = match[1]; + } + } + + const size = result.headers['content-length'] || buffer.data.byteLength; + + const fullName = join(`${this.instance.id}`, received.key.remoteJid, mediaType, fileName); + + await s3Service.uploadFile(fullName, buffer.data, size, { + 'Content-Type': mimetype, + }); + + await this.prismaRepository.media.create({ + data: { + messageId: received.messages[0].id, + instanceId: this.instanceId, + type: mediaType, + fileName: fullName, + mimetype, + }, + }); + + const mediaUrl = await s3Service.getObjectUrl(fullName); + + messageRaw.message.mediaUrl = mediaUrl; + } catch (error) { + this.logger.error(['Error on upload file to minio', error?.message, error?.stack]); + } + } else { + const buffer = await this.downloadMediaMessage(received?.messages[0]); + + messageRaw.message.base64 = buffer.toString('base64'); + } } else if (received?.messages[0].interactive) { messageRaw = { key, @@ -395,11 +455,7 @@ export class BusinessStartupService extends ChannelStartupService { }; } - if (this.localSettings.readMessages && received.key.id !== 'status@broadcast') { - // await this.client.readMessages([received.key]); - } - - if (this.localSettings.readStatus && received.key.id === 'status@broadcast') { + if (this.localSettings.readMessages) { // await this.client.readMessages([received.key]); } diff --git a/src/utils/sendTelemetry.ts b/src/utils/sendTelemetry.ts index 26d0972e..3611bbd3 100644 --- a/src/utils/sendTelemetry.ts +++ b/src/utils/sendTelemetry.ts @@ -12,7 +12,6 @@ export interface TelemetryData { export const sendTelemetry = async (route: string): Promise => { const enabled = process.env.TELEMETRY_ENABLED === undefined || process.env.TELEMETRY_ENABLED === 'true'; - console.log('Telemetry enabled:', enabled); if (!enabled) { return; }