|
|
|
|
@ -88,187 +88,152 @@ const createWhatsApp = async phone => {
|
|
|
|
|
|
|
|
|
|
store?.bind(waSocket.ev);
|
|
|
|
|
|
|
|
|
|
const handleMessagesUpsert = async upsert => {
|
|
|
|
|
console.info('messages.upsert: ', JSON.stringify(upsert, undefined, 2));
|
|
|
|
|
const parseTextMessage = original => {
|
|
|
|
|
const text = original.message?.conversation || original.message?.extendedTextMessage?.text;
|
|
|
|
|
return {
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (upsert.type === 'notify') {
|
|
|
|
|
for (const msg of upsert.messages) {
|
|
|
|
|
|
|
|
|
|
// 没有类型的消息,先忽略
|
|
|
|
|
if (!msg.message) {
|
|
|
|
|
console.info('!msg.message, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isJidStatusBroadcast(msg.key.remoteJid)) {
|
|
|
|
|
console.info('isJidStatusBroadcast, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isJidNewsletter(msg.key.remoteJid)) {
|
|
|
|
|
console.info('isJidNewsletter, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const messageType = Object.keys(msg.message)[0];
|
|
|
|
|
// 如果是群发(xxx@broadcast),participant 是发送人,不然则是 remoteJid
|
|
|
|
|
const remoteNo = isJidBroadcast(msg.key.remoteJid) ? decodeJid(msg.key.participant) : decodeJid(msg.key.remoteJid);
|
|
|
|
|
const externalId = externalIdCache.get(msg.key.id);
|
|
|
|
|
const isPersonal = isJidPersonal(msg.key.remoteJid);
|
|
|
|
|
const conversationType = isPersonal ? 'individual' : 'group';
|
|
|
|
|
const isGroup = isJidGroup(msg.key.remoteJid);
|
|
|
|
|
let groupSubject = groupSubjectCache.get(msg.key.remoteJid);
|
|
|
|
|
if (isGroup && groupSubject === undefined) {
|
|
|
|
|
const groupMetadata = await waSocket.groupMetadata(msg.key.remoteJid);
|
|
|
|
|
groupSubject = groupMetadata.subject;
|
|
|
|
|
groupSubjectCache.set(msg.key.remoteJid, groupMetadata.subject)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const emitEventName = msg.key.fromMe ? 'message:updated' : 'message:received';
|
|
|
|
|
const msgDirection = msg.key.fromMe ? 'outbound' : 'inbound';
|
|
|
|
|
const msgFrom = msg.key.fromMe ? whatsAppNo : remoteNo;
|
|
|
|
|
const msgTo = msg.key.fromMe ? remoteNo : whatsAppNo;
|
|
|
|
|
const msgStatus = msg.status === undefined ? '' : formatStatus(msg.status);
|
|
|
|
|
|
|
|
|
|
if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) {
|
|
|
|
|
|
|
|
|
|
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text;
|
|
|
|
|
|
|
|
|
|
waEmitter.emit(emitEventName, {
|
|
|
|
|
id: msg.key.id,
|
|
|
|
|
externalId,
|
|
|
|
|
status: msgStatus,
|
|
|
|
|
direction: msgDirection,
|
|
|
|
|
from: msgFrom,
|
|
|
|
|
to: msgTo,
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
conversation: {
|
|
|
|
|
type: conversationType,
|
|
|
|
|
name: groupSubject,
|
|
|
|
|
},
|
|
|
|
|
customerProfile: {
|
|
|
|
|
id: decodeJid(msg.key.participant),
|
|
|
|
|
name: msg.pushName,
|
|
|
|
|
},
|
|
|
|
|
whatsAppNo,
|
|
|
|
|
fromMe: msg.key.fromMe,
|
|
|
|
|
eventSource: serverConfig.name + '.messages.upsert.notify',
|
|
|
|
|
updateTime: formatTimestamp(msg.messageTimestamp),
|
|
|
|
|
});
|
|
|
|
|
} else if (messageType === 'imageMessage') {
|
|
|
|
|
const imageMessage = msg.message.imageMessage;
|
|
|
|
|
const fileExtension = getFileExtension(imageMessage.mimetype);
|
|
|
|
|
const imageBuffer = await downloadMediaMessage(
|
|
|
|
|
msg, 'buffer', {}, { logger, reuploadRequest: waSocket.updateMediaMessage, },
|
|
|
|
|
);
|
|
|
|
|
const imageFilename = './temp/image_' + whatsAppNo + '_' + msg.key.id + fileExtension;
|
|
|
|
|
await writeFile(imageFilename, imageBuffer);
|
|
|
|
|
|
|
|
|
|
waEmitter.emit(emitEventName, {
|
|
|
|
|
id: msg.key.id,
|
|
|
|
|
externalId,
|
|
|
|
|
status: msgStatus,
|
|
|
|
|
direction: msgDirection,
|
|
|
|
|
from: msgFrom,
|
|
|
|
|
to: msgTo,
|
|
|
|
|
type: 'image',
|
|
|
|
|
image: {
|
|
|
|
|
mimetype: imageMessage.mimetype,
|
|
|
|
|
sha256: uint8ArrayToBase64(imageMessage.fileSha256),
|
|
|
|
|
caption: imageMessage.caption,
|
|
|
|
|
filePath: imageFilename,
|
|
|
|
|
link_original: imageMessage.url,
|
|
|
|
|
},
|
|
|
|
|
conversation: {
|
|
|
|
|
type: conversationType,
|
|
|
|
|
name: groupSubject,
|
|
|
|
|
},
|
|
|
|
|
customerProfile: {
|
|
|
|
|
id: decodeJid(msg.key.participant),
|
|
|
|
|
name: msg.pushName,
|
|
|
|
|
},
|
|
|
|
|
whatsAppNo,
|
|
|
|
|
fromMe: msg.key.fromMe,
|
|
|
|
|
eventSource: serverConfig.name + '.messages.upsert.notify',
|
|
|
|
|
updateTime: formatTimestamp(msg.messageTimestamp),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parseImageMessage = async original => {
|
|
|
|
|
const imageMessage = original.message.imageMessage;
|
|
|
|
|
const fileExtension = getFileExtension(imageMessage.mimetype);
|
|
|
|
|
const imageBuffer = await downloadMediaMessage(
|
|
|
|
|
original, 'buffer', {}, { logger, reuploadRequest: waSocket.updateMediaMessage, },
|
|
|
|
|
);
|
|
|
|
|
const imageFilename = './temp/image_' + whatsAppNo + '_' + original.key.id + fileExtension;
|
|
|
|
|
await writeFile(imageFilename, imageBuffer);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
type: 'image',
|
|
|
|
|
image: {
|
|
|
|
|
mimetype: imageMessage.mimetype,
|
|
|
|
|
sha256: uint8ArrayToBase64(imageMessage.fileSha256),
|
|
|
|
|
caption: imageMessage.caption,
|
|
|
|
|
filePath: imageFilename,
|
|
|
|
|
link_original: imageMessage.url,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const parseTemplateMessage = original => {
|
|
|
|
|
if (original.message.templateMessage.hydratedTemplate) {
|
|
|
|
|
const text = original.message.templateMessage?.hydratedTemplate?.hydratedContentText;
|
|
|
|
|
return {
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
} else if (upsert.type === 'append') {
|
|
|
|
|
for (const msg of upsert.messages) {
|
|
|
|
|
if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) {
|
|
|
|
|
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text;
|
|
|
|
|
// 如果是群发(xxx@broadcast),participant 是发送人,不然则是 remoteJid
|
|
|
|
|
const remoteNo = isJidBroadcast(msg.key.remoteJid) ? decodeJid(msg.key.participant) : decodeJid(msg.key.remoteJid);
|
|
|
|
|
const externalId = externalIdCache.get(msg.key.id);
|
|
|
|
|
const isPersonal = isJidPersonal(msg.key.remoteJid);
|
|
|
|
|
const conversationType = isPersonal ? 'individual' : 'group';
|
|
|
|
|
|
|
|
|
|
const emitEventName = msg.key.fromMe ? 'message:updated' : 'message:received';
|
|
|
|
|
const msgDirection = msg.key.fromMe ? 'outbound' : 'inbound';
|
|
|
|
|
const msgFrom = msg.key.fromMe ? whatsAppNo : remoteNo;
|
|
|
|
|
const msgTo = msg.key.fromMe ? remoteNo : whatsAppNo;
|
|
|
|
|
const msgStatus = msg.status === undefined ? '' : formatStatus(msg.status);
|
|
|
|
|
|
|
|
|
|
waEmitter.emit(emitEventName, {
|
|
|
|
|
id: msg.key.id,
|
|
|
|
|
externalId,
|
|
|
|
|
status: msgStatus,
|
|
|
|
|
direction: msgDirection,
|
|
|
|
|
from: msgFrom,
|
|
|
|
|
to: msgTo,
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
conversation: {
|
|
|
|
|
type: conversationType,
|
|
|
|
|
},
|
|
|
|
|
customerProfile: {
|
|
|
|
|
id: decodeJid(msg.participant),
|
|
|
|
|
name: msg.pushName,
|
|
|
|
|
},
|
|
|
|
|
whatsAppNo,
|
|
|
|
|
fromMe: msg.key.fromMe,
|
|
|
|
|
eventSource: serverConfig.name + '.messages.upsert.append',
|
|
|
|
|
updateTime: formatTimestamp(msg.messageTimestamp),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if (original.message.templateMessage.interactiveMessageTemplate) {
|
|
|
|
|
const text = original.message.templateMessage.interactiveMessageTemplate?.body?.text;
|
|
|
|
|
return {
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTemplateMessage = async msgObj => {
|
|
|
|
|
const text = msgObj.message.templateMessage?.hydratedTemplate?.hydratedContentText;
|
|
|
|
|
const getMessageParser = (original) => {
|
|
|
|
|
|
|
|
|
|
waEmitter.emit(emitEventName, {
|
|
|
|
|
id: msgObj.key.id,
|
|
|
|
|
externalId,
|
|
|
|
|
status: msgStatus,
|
|
|
|
|
direction: msgDirection,
|
|
|
|
|
from: msgFrom,
|
|
|
|
|
to: msgTo,
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: {
|
|
|
|
|
body: text,
|
|
|
|
|
},
|
|
|
|
|
conversation: {
|
|
|
|
|
type: conversationType,
|
|
|
|
|
name: groupSubject,
|
|
|
|
|
},
|
|
|
|
|
customerProfile: {
|
|
|
|
|
id: decodeJid(msgObj.key.remoteJid),
|
|
|
|
|
name: msgObj.verifiedBizName || msgObj.pushName,
|
|
|
|
|
},
|
|
|
|
|
whatsAppNo,
|
|
|
|
|
fromMe: msgObj.key.fromMe,
|
|
|
|
|
eventSource: serverConfig.name + '.messages.upsert.notify',
|
|
|
|
|
updateTime: formatTimestamp(msgObj.messageTimestamp),
|
|
|
|
|
});
|
|
|
|
|
if (original.message?.conversation || original.message?.extendedTextMessage?.text) return parseTextMessage;
|
|
|
|
|
|
|
|
|
|
if (original.message?.imageMessage) return parseImageMessage;
|
|
|
|
|
|
|
|
|
|
if (original.message?.templateMessage) return parseTemplateMessage;
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getEventSource = (upsert) => {
|
|
|
|
|
if (upsert.type === 'notify') {
|
|
|
|
|
return serverConfig.name + '.messages.upsert.notify';
|
|
|
|
|
} else if (upsert.type === 'append') {
|
|
|
|
|
return serverConfig.name + '.messages.upsert.append';
|
|
|
|
|
} else {
|
|
|
|
|
return serverConfig.name + '.messages.upsert.unknown';
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleMessagesUpsert = async upsert => {
|
|
|
|
|
console.info('messages.upsert: ', JSON.stringify(upsert, undefined, 2));
|
|
|
|
|
|
|
|
|
|
for (const msg of upsert.messages) {
|
|
|
|
|
|
|
|
|
|
// 没有类型的消息,先忽略
|
|
|
|
|
if (!msg.message) {
|
|
|
|
|
console.info('!msg.message, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isJidStatusBroadcast(msg.key.remoteJid)) {
|
|
|
|
|
console.info('isJidStatusBroadcast, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isJidNewsletter(msg.key.remoteJid)) {
|
|
|
|
|
console.info('isJidNewsletter, ignored.');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是群发(xxx@broadcast),participant 是发送人,不然则是 remoteJid
|
|
|
|
|
const remoteNo = isJidBroadcast(msg.key.remoteJid) ? decodeJid(msg.key.participant) : decodeJid(msg.key.remoteJid);
|
|
|
|
|
const externalId = externalIdCache.get(msg.key.id);
|
|
|
|
|
const isPersonal = isJidPersonal(msg.key.remoteJid);
|
|
|
|
|
const conversationType = isPersonal ? 'individual' : 'group';
|
|
|
|
|
const isGroup = isJidGroup(msg.key.remoteJid);
|
|
|
|
|
let groupSubject = groupSubjectCache.get(msg.key.remoteJid);
|
|
|
|
|
if (isGroup && groupSubject === undefined) {
|
|
|
|
|
const groupMetadata = await waSocket.groupMetadata(msg.key.remoteJid);
|
|
|
|
|
groupSubject = groupMetadata.subject;
|
|
|
|
|
groupSubjectCache.set(msg.key.remoteJid, groupMetadata.subject)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const emitEventName = msg.key.fromMe ? 'message:updated' : 'message:received';
|
|
|
|
|
const msgEventSource = getEventSource(upsert);
|
|
|
|
|
const msgDirection = msg.key.fromMe ? 'outbound' : 'inbound';
|
|
|
|
|
const msgFrom = msg.key.fromMe ? whatsAppNo : remoteNo;
|
|
|
|
|
const msgTo = msg.key.fromMe ? remoteNo : whatsAppNo;
|
|
|
|
|
const msgStatus = msg.status === undefined ? '' : formatStatus(msg.status);
|
|
|
|
|
|
|
|
|
|
const standardMessage = {
|
|
|
|
|
id: msg.key.id,
|
|
|
|
|
externalId,
|
|
|
|
|
status: msgStatus,
|
|
|
|
|
direction: msgDirection,
|
|
|
|
|
from: msgFrom,
|
|
|
|
|
to: msgTo,
|
|
|
|
|
conversation: {
|
|
|
|
|
type: conversationType,
|
|
|
|
|
name: groupSubject,
|
|
|
|
|
},
|
|
|
|
|
customerProfile: {
|
|
|
|
|
id: isGroup ? decodeJid(msg.key.participant) : decodeJid(msg.key.remoteJid),
|
|
|
|
|
// 商业号使用 verifiedBizName,个人使用 pushName
|
|
|
|
|
name: msg.verifiedBizName || msg.pushName,
|
|
|
|
|
},
|
|
|
|
|
whatsAppNo,
|
|
|
|
|
fromMe: msg.key.fromMe,
|
|
|
|
|
eventSource: msgEventSource,
|
|
|
|
|
updateTime: formatTimestamp(msg.messageTimestamp),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const messageParser = getMessageParser(msg);
|
|
|
|
|
if (messageParser) {
|
|
|
|
|
const parsedMessage = await messageParser(msg);
|
|
|
|
|
const mergedMessage = Object.assign({}, standardMessage, parsedMessage);
|
|
|
|
|
waEmitter.emit(emitEventName, mergedMessage);
|
|
|
|
|
} else {
|
|
|
|
|
console.info('不支持该消息类型:', msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleMessagesUpdate = async messageUpdate => {
|
|
|
|
|
console.info('messages.update: ', JSON.stringify(messageUpdate, undefined, 2));
|
|
|
|
|
|
|
|
|
|
@ -383,7 +348,6 @@ const createWhatsApp = async phone => {
|
|
|
|
|
// 第一次一分钟,后面是 20 秒更新一次
|
|
|
|
|
if (qrCode === null) {
|
|
|
|
|
qrCode = qr;
|
|
|
|
|
console.info('QR Code: ' + qrCode);
|
|
|
|
|
waEmitter.emit('creds:update', {
|
|
|
|
|
id: generateId(),
|
|
|
|
|
qr, whatsAppNo,
|
|
|
|
|
|