feat(WAI): 按类型发消息

dev/supplier-email-drawer
Lei OT 9 months ago
parent 8c5db33155
commit 7363ebcb9a

@ -0,0 +1,232 @@
/**
*
*/
'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
};
},
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),
};
},
};
const apiSendMsgTypeMapped = {
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 } } : {}),
}),
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),
};
},
},
image: {
type: 'image',
contentToSend: msg => ({
...mediaMsg.contentToSend({ ...msg }),
}),
dataToDB: msg => mediaMsg.dataToDB(msg),
},
sticker: {
type: 'sticker',
contentToSend: msg => ({
...mediaMsg.contentToSend({ ...msg }),
}),
dataToDB: msg => mediaMsg.dataToDB(msg),
},
audio: {
type: 'audio',
contentToSend: msg => ({
...mediaMsg.contentToSend({ ...msg }),
}),
dataToDB: msg => mediaMsg.dataToDB(msg),
},
video: {
type: 'video', // todo: gif
contentToSend: msg => ({
...mediaMsg.contentToSend({ ...msg }),
}),
dataToDB: msg => mediaMsg.dataToDB(msg),
},
document: {
type: 'document',
contentToSend: msg => ({
...mediaMsg.contentToSend({ ...msg }),
}),
dataToDB: msg => mediaMsg.dataToDB(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,
},
}),
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),
};
},
},
// 不支持
location: {
type: 'location',
contentToSend: msg => ({
// ...msg,
to: msg.to,
externalId: msg.actionId,
// location: { degreesLatitude: msg.latitude, degreesLongitude: msg.longitude }, // todo:
}),
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),
};
},
},
// 不支持
contacts: {
type: 'contacts',
contentToSend: msg => ({
// ...msg,
to: msg.to,
externalId: msg.actionId,
// contacts: { displayName: '', contacts: msg.contacts }, // todo:
}),
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),
};
},
},
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
*/
exports.ctxToSendBuilder = ctxContent => {
const { type, contentToSend } = apiSendMsgTypeMapped[ctxContent.msgtype];
const msgReady = contentToSend(ctxContent);
return msgReady;
};
/**
* API Payload to DB
*/
exports.ctxToDB = ctxContent => {
const { type, dataToDB } = apiSendMsgTypeMapped[ctxContent.msgtype];
const msgReady = dataToDB(ctxContent);
return msgReady;
};

@ -3,9 +3,11 @@
const generateId = require('../../utils/generateId.util');
const { sessionStore } = require('../../core');
const { objectMapper, pick } = require('../../utils/commons.util');
const { ctxToSendBuilder, ctxToDB } = require('./apiPayloadHelper');
const { createOutboundMessage } = require('../../services/outbound_messages.service');
const waEmitter = require('../../core/emitter');
const logger = require('../../utils/logger.util');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
@ -55,36 +57,24 @@ exports.sendText = async ctx => {
exports.send = async ctx => {
const { msgtype, from, to, msgcontent, content: _content, actionId } = ctx.request.body;
const content = _content || msgcontent.body || '';
if (!from || !content) {
ctx.assert(from, 400, 'From and message are required');
logger.info('send', msgtype, from, to, content);
if (!from) {
ctx.assert(from, 400, '请先到个人资料页扫码登录WhatsApp');
return;
}
const wsToSend = sessionStore.getSession(from);
// console.log('find wsToSend', wsToSend)
if (!wsToSend) {
ctx.assert(wsToSend, 400, '未登录WhatsApp, 请到个人资料页扫码登录'); // 404
ctx.assert(wsToSend, 401, '未登录WhatsApp, 请到个人资料页扫码登录'); // 404
return;
}
try {
// wsToSend.sendTextMessage(to, content, actionId).then(({ messageId }) => {
// const messageId = generateId();
const _data = ctx.request.body;
const defaultR = { direction: 'outbound' };
const r1 = pick(_data, ['actionId', 'msgtype', 'externalId']);
r1.id = actionId; // id not null
// r1.wamid = messageId;
r1.msg_status = 'ready';
r1.createTime = Date.now();
const record = objectMapper(_data, { from: 'from', to: 'to' }, false);
const byType = _data.msgtype === 'text' ? { text_body: _data.msgcontent.body, text_preview_url: _data.msgcontent.preview_url } : {};
const toUpsert = { ...defaultR, ...r1, ...record, ...byType, message_origin: JSON.stringify(_data) };
const toUpsert = ctxToDB(_data);
await createOutboundMessage({ ...toUpsert });
// return 'Message sent successfully'; // { wsToSend, ret: 'Message sent successfully' };
// });
// wsToSend.sendTextMessage(to, content, actionId);
// todo: build msgtype content to wa
waEmitter.emit('request.' + from + '.send.' + msgtype, { to, content, externalId: actionId });
const messagePayload = ctxToSendBuilder(_data);
waEmitter.emit('request.' + from + '.send.' + msgtype, messagePayload);
return 'Message sent successfully'; // { wsToSend, ret: 'Message sent successfully' };
} catch (error) {
console.error('Error sending message:', error);

@ -64,7 +64,6 @@ const webhookBodyFill = (webhookBody, messageData) => {
*/
const setupConnectionHandler = () => {
// connectionEventNames.forEach(eventName => {
logger.info(`Setting up event ${'connection:connect'}`);
whatsappEvents.on('connection:connect', async connectionData => {
try {
// find Or create
@ -77,7 +76,6 @@ const setupConnectionHandler = () => {
logger.error({ connectionData, error }, 'error add connection');
}
});
logger.info(`Setting up event ${'connection:open'}`);
whatsappEvents.on('connection:open', async connectionData => {
logger.info(`event ${'connection:open'}`, connectionData);
// todo: 更新实例
@ -96,7 +94,6 @@ const setupConnectionHandler = () => {
logger.error({ connectionData, error }, 'error add connection');
}
});
logger.info(`Setting up event ${'connection:close'}`);
whatsappEvents.on('connection:close', async connectionData => {
logger.info(`event ${'connection:close'}`, connectionData);
try {
@ -140,7 +137,6 @@ const setupCredsHandler = () => {
*/
const setupMessageHandler = () => {
messageEventNames.forEach(eventName => {
logger.info(`Setting up event ${eventName}`);
whatsappEvents.on(eventName, async messageData => {
// if (messageData.status === 'pending') {
// logger.info('message pending', messageData);

@ -37,7 +37,7 @@ module.exports = function(sequelize, DataTypes) {
id: {
type: DataTypes.STRING(200),
allowNull: false,
unique: 'outbound_messages_id_IDX',
unique: 'outbound_messages_unique',
},
wamid: {
type: DataTypes.STRING(200),
@ -51,6 +51,10 @@ module.exports = function(sequelize, DataTypes) {
type: DataTypes.STRING(100),
allowNull: true,
},
customerProfile_id: {
type: DataTypes.STRING(100),
allowNull: true,
},
customerProfile_name: {
type: DataTypes.TEXT,
allowNull: true,
@ -116,6 +120,67 @@ module.exports = function(sequelize, DataTypes) {
type: DataTypes.STRING(200),
allowNull: true,
},
IVADS_link_original: {
type: DataTypes.TEXT,
allowNull: true,
comment: '供应商给的url',
},
IVADS_link: {
type: DataTypes.TEXT,
allowNull: true,
},
IVADS_caption: {
type: DataTypes.STRING(1024),
allowNull: true,
},
IVADS_id: {
type: DataTypes.STRING(100),
allowNull: true,
},
IVADS_sha256: {
type: DataTypes.STRING(200),
allowNull: true,
},
IVADS_mime_type: {
type: DataTypes.STRING(255),
allowNull: true,
},
IVADS_filename: {
type: DataTypes.TEXT,
allowNull: true,
},
location_latitude: {
type: DataTypes.STRING(50),
allowNull: true,
},
location_longitude: {
type: DataTypes.STRING(50),
allowNull: true,
},
location_name: {
type: DataTypes.TEXT,
allowNull: true,
},
location_address: {
type: DataTypes.TEXT,
allowNull: true,
},
location_url: {
type: DataTypes.TEXT,
allowNull: true,
},
contacts: {
type: DataTypes.TEXT,
allowNull: true,
},
reaction_message_id: {
type: DataTypes.STRING(200),
allowNull: true,
},
reaction_emoji: {
type: DataTypes.TEXT,
allowNull: true,
},
message_origin: {
type: DataTypes.TEXT,
allowNull: true,
@ -133,7 +198,7 @@ module.exports = function(sequelize, DataTypes) {
fields: [{ name: 'sn' }],
},
{
name: 'outbound_messages_id_IDX',
name: 'outbound_messages_unique',
unique: true,
using: 'BTREE',
fields: [{ name: 'id' }],

Loading…
Cancel
Save