conf(wai): eslint

dev/supplier-email-drawer
lyt 9 months ago
parent 086683f782
commit 3158595b30

@ -1,18 +1,29 @@
module.exports = { module.exports = {
env: { env: {
browser: true, node: true,
commonjs: true, es2021: true,
es6: true,
'jest/globals': true,
}, },
extends: ['standard', 'plugin:prettier/recommended'], extends: ['standard', 'prettier', 'plugin:prettier/recommended'],
plugins: ['prettier', 'jest'], plugins: ['n', 'prettier'],
parserOptions: { parserOptions: {
ecmaVersion: 2019, ecmaVersion: 'latest',
sourceType: 'module', sourceType: 'module',
}, },
rules: { rules: {
// Add here all the extra rules based on the developer preferences 'prettier/prettier': ['warn', { parser: 'flow' }],
'no-unused-vars': ['warn', { args: 'after-used', vars: 'all' }], 'n/exports-style': ['warn', 'module.exports'],
'n/file-extension-in-import': ['error', 'always'],
'n/prefer-promises/dns': 'error',
'n/prefer-promises/fs': 'error',
'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
}, },
overrides: [
{
files: ['test/**/*.js'],
env: {
mocha: true,
},
},
],
}; };

@ -0,0 +1 @@
* text=auto eol=lf

@ -5,5 +5,7 @@
"singleQuote": true, "singleQuote": true,
"trailingComma": "all", "trailingComma": "all",
"bracketSpacing": true, "bracketSpacing": true,
"proseWrap": "always" "proseWrap": "always",
"arrowParens": "avoid",
"endOfLine": "auto"
} }

@ -3,10 +3,10 @@
const { sessionStore } = require('../../core'); // Import from core/index.js const { sessionStore } = require('../../core'); // Import from core/index.js
const { createWhatsApp } = require('../../core/baileys'); // Import from core/index.js const { createWhatsApp } = require('../../core/baileys'); // Import from core/index.js
const { getConnection } = require('../../services/connections.service'); const { getConnection } = require('../../services/connections.service');
const { objectMapper, isEmpty } = require('../../utils/commons.util'); const { isEmpty } = require('../../utils/commons.util');
const { domain, name: domainName } = require('../../config').server; const { domain } = require('../../config').server;
exports.newConnect = async ctx => { const newConnect = async ctx => {
const { phone } = ctx.query; const { phone } = ctx.query;
const existsSession = sessionStore.getSession(phone); const existsSession = sessionStore.getSession(phone);
if (!isEmpty(existsSession)) { if (!isEmpty(existsSession)) {
@ -25,11 +25,17 @@ exports.newConnect = async ctx => {
} }
}; };
exports.getAll = async () => { const getAll = async () => {
const findConnection = await getConnection({}); const findConnection = await getConnection({});
return findConnection; return findConnection;
}; };
exports.getSessions = async ctx => { const getSessions = async () => {
return Array.from(sessionStore.sessions); return Array.from(sessionStore.sessions);
}; };
module.exports = {
newConnect,
getAll,
getSessions,
};

@ -15,7 +15,7 @@ function sleep(ms) {
/** /**
* @deprecated 即将废弃 * @deprecated 即将废弃
*/ */
exports.sendText = async ctx => { const sendText = async ctx => {
const { from, to, msgcontent, content: _content, actionId } = ctx.request.body; const { from, to, msgcontent, content: _content, actionId } = ctx.request.body;
const content = _content || msgcontent.body || ''; const content = _content || msgcontent.body || '';
if (!from || !content) { if (!from || !content) {
@ -54,7 +54,7 @@ exports.sendText = async ctx => {
} }
}; };
exports.send = async ctx => { const send = async ctx => {
const { msgtype, from, to, msgcontent, content: _content, actionId } = ctx.request.body; const { msgtype, from, to, msgcontent, content: _content, actionId } = ctx.request.body;
const content = _content || msgcontent.body || ''; const content = _content || msgcontent.body || '';
logger.info('send', msgtype, from, to, content); logger.info('send', msgtype, from, to, content);
@ -81,3 +81,8 @@ exports.send = async ctx => {
ctx.assert(null, 500, 'Failed to send message'); ctx.assert(null, 500, 'Failed to send message');
} }
}; };
module.exports = {
send,
sendText,
};

@ -1,196 +1,196 @@
'use strict'; 'use strict';
const generateId = require('../../utils/generateId.util'); const generateId = require('../../utils/generateId.util');
const { domain, name: domainName } = require('../../config').server; const { domain, name: domainName } = require('../../config').server;
const whatsappEvents = require('../emitter'); const whatsappEvents = require('../emitter');
const { callWebhook } = require('../webhook'); const { callWebhook } = require('../webhook');
const { addConnection, updateConnection, addCurrentConnection, resetConnection } = require('../../services/connections.service'); const { updateConnection, addCurrentConnection, resetConnection } = require('../../services/connections.service');
const { objectMapper, pick } = require('../../utils/commons.util'); const { objectMapper, pick } = require('../../utils/commons.util');
const { sessionStore } = require('..'); const { sessionStore } = require('..');
const { getOutboundMessage, upsertOutboundMessage } = require('../../services/outbound_messages.service'); const { getOutboundMessage, upsertOutboundMessage } = require('../../services/outbound_messages.service');
const logger = require('../../utils/logger.util'); const logger = require('../../utils/logger.util');
const { DbData, } = require('../../helper/wai.msg.helper'); const { DbData } = require('../../helper/wai.msg.helper');
const connectionEventNames = ['connection:connect', 'connection:open', 'connection:close']; const connectionEventNames = ['connection:connect', 'connection:open', 'connection:close'];
const messageEventNames = ['message:received', 'message:updated']; const messageEventNames = ['message:received', 'message:updated'];
const eventTypeMapped = { const eventTypeMapped = {
'message:received': 'wai.message.received', 'message:received': 'wai.message.received',
'message:updated': 'wai.message.updated', 'message:updated': 'wai.message.updated',
'creds:update': 'wai.creds.update', 'creds:update': 'wai.creds.update',
}; };
const timeField = { saved: 'createTime', pending: 'createTime', sent: 'sendTime', delivered: 'deliverTime', read: 'readTime', failed: 'updateTime' }; const timeField = { saved: 'createTime', pending: 'createTime', sent: 'sendTime', delivered: 'deliverTime', read: 'readTime', failed: 'updateTime' };
const statusMapped = { saved: 'accepted', pending: 'accepted', sent: 'sent', delivered: 'delivered', read: 'read', failed: 'failed' }; const statusMapped = { saved: 'accepted', pending: 'accepted', sent: 'sent', delivered: 'delivered', read: 'read', failed: 'failed' };
const directionField = { 'message:received': 'inbound', 'message:updated': 'outbound' }; const directionField = { 'message:received': 'inbound', 'message:updated': 'outbound' };
const directionPrefix = { inbound: 'in_', outbound: 'out_' }; const directionPrefix = { inbound: 'in_', outbound: 'out_' };
const uniqueMsgId = (msg) => `${directionPrefix[msg.direction]}${msg.to.replace('+', '')}_${msg.id}`; const uniqueMsgId = msg => (msg.id && msg.direction ? `${directionPrefix[msg.direction]}${msg.to.replace('+', '')}_${msg.id}` : undefined);
/** /**
* @returns {Object} webhookBody * @returns {Object} webhookBody
*/ */
const webhookBodyBuilder = (messageData, messageType) => { const webhookBodyBuilder = (messageData, messageType) => {
const defaultContent = { id: '', from: '', to: '', externalId: '', type: '', direction: '', status: '', }; const defaultContent = { id: '', from: '', to: '', externalId: '', type: '', direction: '', status: '' };
const message = { const message = {
id: `evt_${generateId().replace(/-/g, '')}`, id: `evt_${generateId().replace(/-/g, '')}`,
type: eventTypeMapped[messageType], type: eventTypeMapped[messageType],
apiVersion: 'v2', apiVersion: 'v2',
webhooksource: 'wai', webhooksource: 'wai',
createTime: new Date(new Date().getTime() + 8 * 60 * 60 * 1000).toISOString(), // GMT +8 createTime: new Date(new Date().getTime() + 8 * 60 * 60 * 1000).toISOString(), // GMT +8
domainName, domainName,
conversationid: messageData?.externalId || '', conversationid: messageData?.externalId || '',
whatsAppNo: messageData?.whatsAppNo || '', whatsAppNo: messageData?.whatsAppNo || '',
waiMessage: { waiMessage: {
...defaultContent, ...defaultContent,
...messageData, ...messageData,
...(messageData.updateTime ? { [timeField[messageData.status]]: messageData.updateTime } : {}), ...(messageData.updateTime ? { [timeField[messageData.status]]: messageData.updateTime } : {}),
id: messageData.id && messageData.direction ? uniqueMsgId(messageData) : (messageData.id || generateId()), id: uniqueMsgId(messageData) || messageData.id || generateId(),
wamid: messageData.id || '', wamid: messageData.id || '',
// direction: directionField[messageType], // direction: directionField[messageType],
status: statusMapped?.[messageData.status] || messageData.status || '', status: statusMapped?.[messageData.status] || messageData.status || '',
externalId: messageData?.externalId || '', externalId: messageData?.externalId || '',
}, },
}; };
return message; return message;
}; };
const webhookBodyFill = (webhookBody, messageData) => { const webhookBodyFill = (webhookBody, messageData) => {
const DBDataObj = DbData(messageData); const DBDataObj = DbData(messageData);
Object.assign(webhookBody.waiMessage, DBDataObj); Object.assign(webhookBody.waiMessage, DBDataObj);
return webhookBody; return webhookBody;
}; };
/** /**
* WhatsApp 连接事件 * WhatsApp 连接事件
*/ */
const setupConnectionHandler = () => { const setupConnectionHandler = () => {
// connectionEventNames.forEach(eventName => { // connectionEventNames.forEach(eventName => {
whatsappEvents.on('connection:connect', async connectionData => { whatsappEvents.on('connection:connect', async connectionData => {
try { try {
// find Or create // find Or create
await addCurrentConnection({ await addCurrentConnection({
...objectMapper(connectionData, { phone: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id', createTimestamp: 'createtime', version: 'version' }, false), ...objectMapper(connectionData, { phone: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id', createTimestamp: 'createtime', version: 'version' }, false),
service_type: 'baileys', service_type: 'baileys',
status: 'connecting', status: 'connecting',
}); });
} catch (error) { } catch (error) {
logger.error({ connectionData, error }, 'error add connection'); logger.error({ connectionData, error }, 'error add connection');
} }
}); });
whatsappEvents.on('connection:open', async connectionData => { whatsappEvents.on('connection:open', async connectionData => {
logger.info(`event ${'connection:open'}`, connectionData); logger.info(`event ${'connection:open'}`, connectionData);
// todo: 更新实例 // todo: 更新实例
try { try {
await updateConnection( await updateConnection(
{ {
...objectMapper(connectionData, { whatsAppNo: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id' }), ...objectMapper(connectionData, { whatsAppNo: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id' }),
service_type: 'baileys', service_type: 'baileys',
closetime: null, closetime: null,
}, },
{ connect_domain: domain, connect_name: domainName }, { connect_domain: domain, connect_name: domainName },
); );
const webhookBody = webhookBodyBuilder({ ...connectionData, to: connectionData.whatsAppNo, connection: 'open' }, 'creds:update'); const webhookBody = webhookBodyBuilder({ ...connectionData, to: connectionData.whatsAppNo, connection: 'open' }, 'creds:update');
await callWebhook(webhookBody); await callWebhook(webhookBody);
} catch (error) { } catch (error) {
logger.error({ connectionData, error }, 'error add connection'); logger.error({ connectionData, error }, 'error add connection');
} }
}); });
whatsappEvents.on('connection:close', async connectionData => { whatsappEvents.on('connection:close', async connectionData => {
logger.info(`event ${'connection:close'}`, connectionData); logger.info(`event ${'connection:close'}`, connectionData);
try { try {
sessionStore.removeSession(connectionData.channelId); sessionStore.removeSession(connectionData.channelId);
await updateConnection( await updateConnection(
{ {
...objectMapper(connectionData, { whatsAppNo: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id' }), ...objectMapper(connectionData, { whatsAppNo: [{ key: 'wa_id' }, { key: 'sesson_id' }], channelId: 'channel_id' }),
service_type: 'baileys', service_type: 'baileys',
}, },
{ connect_domain: domain, connect_name: domainName }, { connect_domain: domain, connect_name: domainName },
); );
const webhookBody = webhookBodyBuilder({ ...connectionData, to: connectionData.whatsAppNo, connection: 'offline' }, 'creds:update'); const webhookBody = webhookBodyBuilder({ ...connectionData, to: connectionData.whatsAppNo, connection: 'offline' }, 'creds:update');
await callWebhook(webhookBody); await callWebhook(webhookBody);
} catch (error) { } catch (error) {
logger.error({ connectionData, error }, 'error close connection'); logger.error({ connectionData, error }, 'error close connection');
} }
}); });
// }); // });
}; };
/** /**
* 监听 Creds 更新事件 * 监听 Creds 更新事件
*/ */
const setupCredsHandler = () => { const setupCredsHandler = () => {
whatsappEvents.on('creds:update', async creds => { whatsappEvents.on('creds:update', async creds => {
logger.info('creds:update', creds); logger.info('creds:update', creds);
try { try {
const webhookBody = webhookBodyBuilder({ ...creds, to: creds.whatsAppNo, connection: '' }, 'creds:update'); const webhookBody = webhookBodyBuilder({ ...creds, to: creds.whatsAppNo, connection: '' }, 'creds:update');
await callWebhook(webhookBody); await callWebhook(webhookBody);
} catch (error) { } catch (error) {
logger.error({ creds, error }, 'error update creds'); logger.error({ creds, error }, 'error update creds');
} }
}); });
}; };
/** /**
* WhatsApp 消息事件 * WhatsApp 消息事件
* pending -> saved -> sent(*) -> delivered -> read * pending -> saved -> sent(*) -> delivered -> read
* saved -> pending -> sent(*) -> delivered -> read * saved -> pending -> sent(*) -> delivered -> read
*/ */
const setupMessageHandler = () => { const setupMessageHandler = () => {
messageEventNames.forEach(eventName => { messageEventNames.forEach(eventName => {
whatsappEvents.on(eventName, async messageData => { whatsappEvents.on(eventName, async messageData => {
// if (messageData.status === 'pending') { // if (messageData.status === 'pending') {
// logger.info('message pending', messageData); // logger.info('message pending', messageData);
// return false; // return false;
// } // }
try { try {
const now = new Date(new Date().getTime() + 60 * 60 * 1000).toISOString(); const now = new Date(new Date().getTime() + 60 * 60 * 1000).toISOString();
const savedId = uniqueMsgId(messageData); const savedId = uniqueMsgId(messageData);
const targetUpsert = messageData.externalId ? { actionId: messageData.externalId } : { id: savedId }; const targetUpsert = messageData.externalId ? { actionId: messageData.externalId } : { id: savedId };
const savedMsg = await getOutboundMessage(targetUpsert); const savedMsg = await getOutboundMessage(targetUpsert);
const bixFields = pick(savedMsg, ['actionId', 'externalId']); const bixFields = pick(savedMsg, ['actionId', 'externalId']);
logger.info('message evt\n', eventName, messageData, savedMsg); logger.info('message evt\n', eventName, messageData, savedMsg);
const _type = messageData?.type || savedMsg?.msgtype || 'text'; const _type = messageData?.type || savedMsg?.msgtype || 'text';
const typeField = { msgtype: _type }; // fix: type 空 const typeField = { msgtype: _type }; // fix: type 空
const webhookBody = webhookBodyBuilder({ ...messageData, ...bixFields, ...typeField }, eventName); const webhookBody = webhookBodyBuilder({ ...messageData, ...bixFields, ...typeField }, eventName);
const { waiMessage } = webhookBody; const { waiMessage } = webhookBody;
const timeFields = pick(waiMessage, [...Object.values(timeField), 'createTime', 'updateTime']); const timeFields = pick(waiMessage, [...Object.values(timeField), 'createTime', 'updateTime']);
const upsertFields = pick(waiMessage, ['direction', 'wamid', 'id', 'status']); const upsertFields = pick(waiMessage, ['direction', 'wamid', 'id', 'status']);
upsertFields.evt_id = webhookBody.id; upsertFields.evt_id = webhookBody.id;
const pusher = { customerProfile_id: waiMessage.customerProfile?.id || '', customerProfile_name: waiMessage.customerProfile?.name || '' }; const pusher = { customerProfile_id: waiMessage.customerProfile?.id || '', customerProfile_name: waiMessage.customerProfile?.name || '' };
const record = objectMapper(waiMessage, { from: 'from', to: 'to', status: 'msg_status', type: 'msgtype' }, false); const record = objectMapper(waiMessage, { from: 'from', to: 'to', status: 'msg_status', type: 'msgtype' }, false);
const contentFields = waiMessage.type === 'text' ? { text_body: waiMessage.text.body } : {}; const contentFields = waiMessage.type === 'text' ? { text_body: waiMessage.text.body } : {};
// const contentFieldsToDB = // const contentFieldsToDB =
// todo: 现在只能收text 消息, 后续再加其他类型 // todo: 现在只能收text 消息, 后续再加其他类型
const msgRow = await upsertOutboundMessage( const msgRow = await upsertOutboundMessage(
{ ...timeFields, ...upsertFields, ...pusher, ...contentFields, ...record, ...typeField, message_origin: savedMsg?.message_origin || JSON.stringify(messageData) }, { ...timeFields, ...upsertFields, ...pusher, ...contentFields, ...record, ...typeField, message_origin: savedMsg?.message_origin || JSON.stringify(messageData) },
targetUpsert, targetUpsert,
); );
// console.log('upsert=========================', upsert); // console.log('upsert=========================', upsert);
// todo: 把内容加上, 否则前端没显示 // todo: 把内容加上, 否则前端没显示
await callWebhook(webhookBodyFill(webhookBody, msgRow)); await callWebhook(webhookBodyFill(webhookBody, msgRow));
} catch (error) { } catch (error) {
logger.error({ messageData, error }, 'error call webhook'); logger.error({ messageData, error }, 'error call webhook');
} }
}); });
}); });
}; };
function setupWhatsappHandler() { function setupWhatsappHandler() {
setupConnectionHandler(); setupConnectionHandler();
setupCredsHandler(); setupCredsHandler();
setupMessageHandler(); setupMessageHandler();
} }
/** /**
* 登出: 当前服务的所有连接 * 登出: 当前服务的所有连接
*/ */
async function resetCurrentConnection() { async function resetCurrentConnection() {
await resetConnection(); await resetConnection();
} }
module.exports = { setupWhatsappHandler, resetCurrentConnection }; module.exports = { setupWhatsappHandler, resetCurrentConnection };

@ -253,7 +253,7 @@ const waiMsgTypeMapped = {
/** /**
* API Payload to Send * API Payload to Send
*/ */
exports.ctxToSendBuilder = ctxContent => { const ctxToSendBuilder = ctxContent => {
const { contentToSend } = waiMsgTypeMapped[ctxContent.msgtype]; const { contentToSend } = waiMsgTypeMapped[ctxContent.msgtype];
const msgReady = contentToSend(ctxContent); const msgReady = contentToSend(ctxContent);
return msgReady; return msgReady;
@ -262,7 +262,7 @@ exports.ctxToSendBuilder = ctxContent => {
/** /**
* API Payload to DB * API Payload to DB
*/ */
exports.ctxToDB = ctxContent => { const ctxToDB = ctxContent => {
const { dataToDB } = waiMsgTypeMapped[ctxContent.msgtype]; const { dataToDB } = waiMsgTypeMapped[ctxContent.msgtype];
const msgReady = dataToDB(ctxContent); const msgReady = dataToDB(ctxContent);
return msgReady; return msgReady;
@ -271,8 +271,14 @@ exports.ctxToDB = ctxContent => {
/** /**
* Parse DB Data to UI/API/Webhook * Parse DB Data to UI/API/Webhook
*/ */
exports.DbData = row => { const DbData = row => {
const { DbData } = waiMsgTypeMapped[row.msgtype]; const { DbData } = waiMsgTypeMapped[row.msgtype];
const msgReady = DbData(row); const msgReady = DbData(row);
return msgReady; return msgReady;
}; };
module.exports = {
ctxToSendBuilder,
ctxToDB,
DbData,
};

File diff suppressed because it is too large Load Diff

@ -12,18 +12,16 @@
"test": "NODE_ENV=test jest'" "test": "NODE_ENV=test jest'"
}, },
"devDependencies": { "devDependencies": {
"eslint": "5.16.0", "eslint": "^8.0.1",
"eslint-config-prettier": "4.3.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "12.0.0", "eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "2.17.2", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "22.5.1", "eslint-plugin-n": "^16.6.2",
"eslint-plugin-node": "9.0.1", "eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-prettier": "3.1.0", "eslint-plugin-promise": "^6.6.0",
"eslint-plugin-promise": "4.1.1",
"eslint-plugin-standard": "4.0.0",
"husky": "2.3.0", "husky": "2.3.0",
"nodemon": "1.19.0", "nodemon": "1.19.0",
"prettier": "1.17.1" "prettier": "^3.4.2"
}, },
"dependencies": { "dependencies": {
"@koa/cors": "2.2.3", "@koa/cors": "2.2.3",

@ -17,10 +17,10 @@ const getOutboundMessage = async msg => {
return r?.toJSON() || {}; return r?.toJSON() || {};
}; };
const createOutboundMessage = async (data) => { const createOutboundMessage = async data => {
const r = await OutboundModelModel.create(data); const r = await OutboundModelModel.create(data);
return r; return r;
} };
/** /**
* *
@ -37,7 +37,7 @@ const upsertOutboundMessage = async (data, where = {}) => {
const _where = isEmpty(where) ? data : where; const _where = isEmpty(where) ? data : where;
const [instance, created] = await OutboundModelModel.findOrCreate({ where: _where, defaults: { ...data } }); const [instance, created] = await OutboundModelModel.findOrCreate({ where: _where, defaults: { ...data } });
if (!created) { if (!created) {
await instance.update({ ...data, }, { where }); await instance.update({ ...data }, { where });
const savedI = await instance.save(); // reload const savedI = await instance.save(); // reload
console.info('update OutboundMessage --- 2\n', savedI.toJSON()); console.info('update OutboundMessage --- 2\n', savedI.toJSON());
return savedI.toJSON(); return savedI.toJSON();

Loading…
Cancel
Save