diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 906fff1881..5a684fee0a 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -931,6 +931,7 @@ export class ChatwootService { messageBody?: any, sourceId?: string, quotedMsg?: MessageModel, + referral?: Record, ) { const client = await this.clientCw(instance); @@ -943,6 +944,12 @@ export class ChatwootService { const sourceReplyId = quotedMsg?.chatwootMessageId || null; + let contentAttributes: Record = { ...replyToIds }; + + if (referral) { + contentAttributes = { ...contentAttributes, referral }; + } + const message = await client.messages.create({ accountId: this.provider.accountId, conversationId: conversationId, @@ -952,9 +959,7 @@ export class ChatwootService { attachments: attachments, private: privateMessage || false, source_id: sourceId, - content_attributes: { - ...replyToIds, - }, + content_attributes: contentAttributes, source_reply_id: sourceReplyId ? sourceReplyId.toString() : null, }, }); @@ -1057,6 +1062,7 @@ export class ChatwootService { messageBody?: any, sourceId?: string, quotedMsg?: MessageModel, + referral?: Record, ) { if (sourceId && this.isImportHistoryAvailable()) { const messageAlreadySaved = await chatwootImport.getExistingSourceIds([sourceId], conversationId); @@ -1079,17 +1085,24 @@ export class ChatwootService { const sourceReplyId = quotedMsg?.chatwootMessageId || null; + let contentAttributes: Record = {}; + if (messageBody && instance) { const replyToIds = await this.getReplyToIds(messageBody, instance); if (replyToIds.in_reply_to || replyToIds.in_reply_to_external_id) { - const content = JSON.stringify({ - ...replyToIds, - }); - data.append('content_attributes', content); + contentAttributes = { ...contentAttributes, ...replyToIds }; } } + if (referral) { + contentAttributes = { ...contentAttributes, referral }; + } + + if (Object.keys(contentAttributes).length > 0) { + data.append('content_attributes', JSON.stringify(contentAttributes)); + } + if (sourceReplyId) { data.append('source_reply_id', sourceReplyId.toString()); } @@ -1727,21 +1740,55 @@ export class ChatwootService { body: string; thumbnailUrl: string; sourceUrl: string; + sourceId?: string; + sourceType?: string; + mediaType?: string; + mediaUrl?: string; } + const externalAdReply = + msg.extendedTextMessage?.contextInfo?.externalAdReply || + msg.contextInfo?.externalAdReply || + msg.message?.extendedTextMessage?.contextInfo?.externalAdReply || + msg.message?.contextInfo?.externalAdReply; + const adsMessage: AdsMessage | undefined = { - title: msg.extendedTextMessage?.contextInfo?.externalAdReply?.title || msg.contextInfo?.externalAdReply?.title, - body: msg.extendedTextMessage?.contextInfo?.externalAdReply?.body || msg.contextInfo?.externalAdReply?.body, - thumbnailUrl: - msg.extendedTextMessage?.contextInfo?.externalAdReply?.thumbnailUrl || - msg.contextInfo?.externalAdReply?.thumbnailUrl, - sourceUrl: - msg.extendedTextMessage?.contextInfo?.externalAdReply?.sourceUrl || msg.contextInfo?.externalAdReply?.sourceUrl, + title: externalAdReply?.title, + body: externalAdReply?.body, + thumbnailUrl: externalAdReply?.thumbnailUrl, + sourceUrl: externalAdReply?.sourceUrl, + sourceId: externalAdReply?.sourceId, + sourceType: externalAdReply?.sourceType, + mediaType: externalAdReply?.mediaType, + mediaUrl: externalAdReply?.mediaUrl, }; return adsMessage; } + private buildReferralAttributes(adsMessage: { + title?: string; + body?: string; + thumbnailUrl?: string; + sourceUrl?: string; + sourceId?: string; + sourceType?: string; + mediaType?: string; + mediaUrl?: string; + }) { + const referral: Record = {}; + + if (adsMessage.sourceId) referral.source_id = adsMessage.sourceId; + if (adsMessage.sourceType) referral.source_type = adsMessage.sourceType; + if (adsMessage.sourceUrl) referral.source_url = adsMessage.sourceUrl; + if (adsMessage.title) referral.headline = adsMessage.title; + if (adsMessage.body) referral.body = adsMessage.body; + if (adsMessage.mediaType) referral.media_type = adsMessage.mediaType; + if (adsMessage.thumbnailUrl) referral.image_url = adsMessage.thumbnailUrl; + + return Object.keys(referral).length > 0 ? referral : undefined; + } + private getReactionMessage(msg: any) { interface ReactionMessage { key: { @@ -2212,50 +2259,89 @@ export class ChatwootService { const isAdsMessage = (adsMessage && adsMessage.title) || adsMessage.body || adsMessage.thumbnailUrl; if (isAdsMessage) { - const imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' }); + const truncStr = (str: string, len: number) => { + if (!str) return ''; + + return str.length > len ? str.substring(0, len) + '...' : str; + }; + + const title = truncStr(adsMessage.title, 40); + const description = truncStr(adsMessage?.body, 75); + const referralAttributes = this.buildReferralAttributes(adsMessage); + const adCaption = `${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`; + + const sendWithoutThumbnail = async () => { + const send = await this.createMessage( + instance, + getConversation, + adCaption, + messageType, + false, + [], + body, + 'WAID:' + body.key.id, + quotedMsg, + referralAttributes, + ); + + if (!send) { + this.logger.warn('message not sent'); + return; + } + + return send; + }; + + let imgBuffer; + try { + imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' }); + } catch (error) { + this.logger.warn(`Failed to download ads thumbnail: ${error?.message || error}`); + return sendWithoutThumbnail(); + } const extension = mimeTypes.extension(imgBuffer.headers['content-type']); const mimeType = extension && mimeTypes.lookup(extension); if (!mimeType) { this.logger.warn('mimetype of Ads message not found'); - return; + return sendWithoutThumbnail(); } const random = Math.random().toString(36).substring(7); const nameFile = `${random}.${mimeTypes.extension(mimeType)}`; const fileData = Buffer.from(imgBuffer.data, 'binary'); - const img = await Jimp.read(fileData); - await img.cover({ - w: 320, - h: 180, - }); - const processedBuffer = await img.getBuffer(JimpMime.png); + let processedBuffer: Buffer = fileData; + try { + const img = await Jimp.read(fileData); + await img.cover({ + w: 320, + h: 180, + }); + processedBuffer = await img.getBuffer(JimpMime.png); + } catch (error) { + this.logger.warn( + `Failed to process ads thumbnail with Jimp, sending raw image: ${error?.message || error}`, + ); + } const fileStream = new Readable(); fileStream._read = () => {}; // _read is required but you can noop it fileStream.push(processedBuffer); fileStream.push(null); - const truncStr = (str: string, len: number) => { - if (!str) return ''; - - return str.length > len ? str.substring(0, len) + '...' : str; - }; - - const title = truncStr(adsMessage.title, 40); - const description = truncStr(adsMessage?.body, 75); - const send = await this.sendData( getConversation, fileStream, nameFile, messageType, - `${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`, + adCaption, instance, body, 'WAID:' + body.key.id, + quotedMsg, + referralAttributes, ); if (!send) {