/** * */ 'use strict'; const { objectMapper, pick } = require('../utils/commons.util'); const mediaMsg = { contentToSend: msg => { const { msgtype, msgcontent, ...body } = msg; return { // ...body, to: body.to, externalId: body.actionId, // type WAMediaUpload = Buffer | WAMediaPayloadStream | WAMediaPayloadURL; [msgtype]: { url: msgcontent[msgtype].link, }, ...(msgcontent[msgtype].caption ? { caption: msgcontent[msgtype].caption } : {}), // image, video, document // mimetype: 'audio/mp4', ...(msgtype === 'document' ? { filename: msgcontent[msgtype].filename } : {}), // document ...(msgcontent.context ? { quoted: { key: msgcontent.context.message_id } } : {}), // seconds?: number; // audio // isAnimated?: boolean; // sticker // jpegThumbnail?: string; // image, video }; }, waiContentToDB: msg => { const { type } = msg; return { IVADS_link_original: msg[type].link_original, IVADS_caption: msg[type].caption || '', IVADS_filename: msg[type].filename || '', IVADS_mime_type: msg[type].mimetype || '', IVADS_sha256: msg[type].sha256 || '', }; }, dataToDB: msg => { const { msgtype, msgcontent, ...body } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, IVADS_link: msgcontent[msgtype].link, IVADS_caption: msgcontent[msgtype].caption || '', IVADS_filename: msgcontent[msgtype].filename || '', ...(msgcontent.context ? { context_id: msgcontent.context.message_id, context_from: msgcontent.message_origin.from, } : {}), message_origin: JSON.stringify(msg), }; }, DbData: row => ({ [row.msgtype]: { link: row.IVADS_link, caption: row.IVADS_caption, filename: row.IVADS_filename, }, type: row.msgtype, ...(row.context_id ? { context: { id: row.context_id, from: row.context_from } } : {}), }), }; const waiMsgTypeMapped = { unresolvable: { type: 'unresolvable', contentToSend: msg => ({}), waiContentToDB: msg => ({}), dataToDB: msg => ({}), DbData: row => ({ type: 'unresolvable', }), }, text: { type: 'text', contentToSend: msg => ({ // ...msg, to: msg.to, externalId: msg.actionId, text: msg.msgcontent.body, // linkPreview: true, // {} ...(msg.msgcontent.context ? { quoted: { key: msg.msgcontent.context.message_id } } : {}), }), waiContentToDB: msg => { return { text_body: msg.text.body }; }, dataToDB: msg => { const { msgcontent } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, text_body: msgcontent.body, text_preview_url: msgcontent.preview_url, ...(msgcontent.context ? { context_id: msgcontent.context.message_id, context_from: msgcontent.message_origin.from, } : {}), message_origin: JSON.stringify(msg), }; }, DbData: row => ({ type: 'text', text: { body: row.text_body, preview_url: row.text_preview_url }, ...(row.context_id ? { context: { id: row.context_id, from: row.context_from } } : {}), }), }, image: { type: 'image', contentToSend: msg => ({ ...mediaMsg.contentToSend({ ...msg }), }), waiContentToDB: msg => mediaMsg.waiContentToDB(msg), dataToDB: msg => mediaMsg.dataToDB(msg), DbData: msg => mediaMsg.DbData(msg), }, sticker: { type: 'sticker', contentToSend: msg => ({ ...mediaMsg.contentToSend({ ...msg }), }), waiContentToDB: msg => mediaMsg.waiContentToDB(msg), dataToDB: msg => mediaMsg.dataToDB(msg), DbData: msg => mediaMsg.DbData(msg), }, audio: { type: 'audio', contentToSend: msg => ({ ...mediaMsg.contentToSend({ ...msg }), }), waiContentToDB: msg => mediaMsg.waiContentToDB(msg), dataToDB: msg => mediaMsg.dataToDB(msg), DbData: msg => mediaMsg.DbData(msg), }, video: { type: 'video', // todo: gif contentToSend: msg => ({ ...mediaMsg.contentToSend({ ...msg }), }), waiContentToDB: msg => mediaMsg.waiContentToDB(msg), dataToDB: msg => mediaMsg.dataToDB(msg), DbData: msg => mediaMsg.DbData(msg), }, document: { type: 'document', contentToSend: msg => ({ ...mediaMsg.contentToSend({ ...msg }), }), waiContentToDB: msg => mediaMsg.waiContentToDB(msg), dataToDB: msg => mediaMsg.dataToDB(msg), DbData: msg => mediaMsg.DbData(msg), }, react: { type: 'react', contentToSend: msg => ({ // ...msg, to: msg.to, externalId: msg.actionId, react: { text: msg.msgcontent.body, // emoji | use an empty string to remove the reaction key: msg.msgcontent.context.message_id, }, }), waiContentToDB: msg => { return { // todo: reaction_message_id: '', reaction_emoji: '', }; }, dataToDB: msg => { const { msgtype, msgcontent, ...body } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, reaction_message_id: '', reaction_emoji: '', message_origin: JSON.stringify(msg), }; }, DbData: row => ({ type: 'reaction', reaction: { emoji: row.reaction_emoji, message_id: row.reaction_message_id }, }), }, location: { type: 'location', contentToSend: msg => ({ // ...msg, to: msg.to, externalId: msg.actionId, // location: { degreesLatitude: msg.latitude, degreesLongitude: msg.longitude }, // todo: }), waiContentToDB: msg => { // todo: return {}; }, dataToDB: msg => { const { msgtype, msgcontent, ...body } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, message_origin: JSON.stringify(msg), }; }, DbData: row => ({ type: 'location', location: { latitude: row.location_latitude, longitude: row.location_longitude, name: row.location_name, address: row.location_address, url: row.location_url, }, }), }, contacts: { type: 'contacts', contentToSend: msg => ({ // ...msg, to: msg.to, externalId: msg.actionId, // contacts: { displayName: '', contacts: msg.contacts }, // todo: }), waiContentToDB: msg => { return { contacts: JSON.stringify(msg.contacts), }; }, dataToDB: msg => { const { msgtype, msgcontent, ...body } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, contacts: JSON.stringify(msg.contacts), message_origin: JSON.stringify(msg), }; }, DbData: row => ({ type: 'contacts', contacts: JSON.parse(row.contacts), }), }, template: { type: 'template', contentToSend: msg => ({ // ...msg, to: msg.to, externalId: msg.actionId, // msgcontent: { // ...msg.template, // components: [ // ...msg.template.components.filter(com => !['footer', 'buttons'].includes(com.type.toLowerCase())), // ], // }, }), dataToDB: msg => { const { msgtype, msgcontent, ...body } = msg; const record = pick(msg, ['actionId', 'msgtype', 'externalId', 'from', 'to']); return { direction: 'outbound', msg_status: 'ready', createTime: Date.now(), id: msg.actionId, ...record, message_origin: JSON.stringify(msg), }; }, }, }; /** * API Payload to Send */ const ctxToSendBuilder = ctxContent => { const { contentToSend } = waiMsgTypeMapped[ctxContent.msgtype]; const msgReady = contentToSend(ctxContent); return msgReady; }; /** * API Payload to DB */ const ctxToDB = ctxContent => { const { dataToDB } = waiMsgTypeMapped[ctxContent.msgtype]; const msgReady = dataToDB(ctxContent); return msgReady; }; /** * wai Event Msg data to DB */ const waiContentToDB = waiMsg => { const { waiContentToDB } = waiMsgTypeMapped[waiMsg.type]; const msgReady = waiContentToDB(waiMsg); return msgReady; }; /** * Parse DB Data to UI/API/Webhook */ const DbData = row => { const { DbData } = waiMsgTypeMapped[row.msgtype]; const msgReady = DbData(row); return msgReady; }; const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const path = require('path'); const PROJECT_ROOT = process.cwd(); /** * Upload Media File * * https://p9axztuwd7x8a7.mycht.cn/whatsapp_server/WAFileUpload * @param {*} filePath */ async function uploadMediaFile(filePath) { const url = 'https://p9axztuwd7x8a7.mycht.cn/whatsapp_server/WAFileUpload'; try { const pathObject = path.parse(filePath); const filename = 'wai_' + pathObject.name.replace(/\s/g, '-') + `_${Date.now()}` + pathObject.ext; const _filePath = path.join(PROJECT_ROOT, filePath); const formData = new FormData(); formData.append('file', fs.createReadStream(_filePath), filename); const response = await axios.post(url, formData, { headers: formData.getHeaders(), maxBodyLength: Infinity, maxContentLength: Infinity, }); const { result, errcode, errmsg } = response.data; if (errcode !== 0) { throw new Error(`Upload media file failed: ${errmsg}. ${filePath}`); } return errcode === 0 ? result.file_url : ''; } catch (error) { console.error('Upload failed:', error); // throw error; return ''; } } module.exports = { ctxToSendBuilder, ctxToDB, waiContentToDB, DbData, uploadMediaFile, };