You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
10 KiB
JavaScript
376 lines
10 KiB
JavaScript
/**
|
|
*
|
|
*/
|
|
'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);
|
|
msgReady.from = ctxContent.from.replace('+', '');
|
|
msgReady.to = ctxContent.to.replace('+', '');
|
|
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,
|
|
};
|