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.
Global-sales/src/channel/whatsappUtils.js

733 lines
29 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { cloneDeep, isEmpty, olog, fixTo2Decimals } from "@/utils/commons";
import dayjs from "dayjs";
import { v4 as uuid } from "uuid";
export const replaceTemplateString = (str, replacements) => {
let result = str;
let keys = str.match(/{{(.*?)}}/g).map(key => key.replace(/{{|}}/g, ''));
for (let i = 0; i < keys.length; i++) {
let replaceValue = replacements[i];
let template = new RegExp(`{{${keys[i]}}}`, 'g');
result = result.replace(template, replaceValue);
}
return result;
}
/**
* @deprecated 在渲染时处理
*/
export const autoLinkText = (text) => {
return text;
// let regex = /(https?:\/\/[^\s]+)/g;
// let newText = text.replace(regex, '<a href="$1" target="_blank">$1</a>');
// return newText;
}
/**
*
// +8618777396951 lyj
{
"to": "+8613317835586", // qqs
"msgtype": "text",
"msgcontent": "{\"body\":\"txtmsgtest\"}"
}
*/
const mediaMsg = {
contentToSend: (msg) => ({
action: 'message',
actionId: msg.id,
renderId: msg.id,
to: msg.to,
msgcontent: {
[msg.type]: {
link: msg.data.dataUri,
...(msg.text ? { caption: msg.text } : {}),
...(msg.type === 'document' ? { filename: msg.name } : {})
},
...(msg.context ? { context: msg.context, message_origin: msg.message_origin.msgOrigin } : {}),
},
}),
contentToRender: (msg) => ({
...msg,
actionId: msg.id,
conversationid: msg.id.split('.')[0],
data: { ...msg.data, status: { download: msg.data?.loading ? false : true, click: true, loading: msg.data.loading } },
...(msg.context
? {
reply: {
id: msg.message_origin.id,
message: msg.message_origin.text,
title: msg.message_origin.senderName || 'Reference',
titleColor: msg.message_origin?.senderName !== 'me' ? '#a791ff' : '#128c7e',
},
}
: {}),
}),
};
export const sentMsgTypeMapped = {
text: {
type: 'text',
contentToSend: (msg) => ({
action: 'message',
actionId: msg.id,
renderId: msg.id,
to: msg.to,
msgtype: 'text',
msgcontent: { body: msg.text, preview_url: true, ...(msg.context ? { context: msg.context, message_origin: msg.message_origin?.msgOrigin || msg.message_origin } : {}) },
}),
contentToRender: (msg) => ({
...msg,
whatsapp_msg_type: 'text',
actionId: msg.id,
conversationid: msg.id.split('.')[0],
originText: msg.text,
text: autoLinkText(msg.text),
...(msg.context
? {
reply: {
id: msg.message_origin.id,
message: msg.message_origin.text,
title: msg.message_origin.senderName || 'Reference',
titleColor: msg.message_origin?.senderName !== 'me' ? '#a791ff' : '#128c7e',
photoURL: msg.message_origin?.data?.uri || '',
},
}
: {}),
}),
},
photo: {
type: 'image',
contentToSend: (msg) => ({
...mediaMsg.contentToSend({ ...msg, type: 'image' }),
msgtype: 'image',
}),
contentToRender: (msg) => ({
...msg,
...mediaMsg.contentToRender(msg),
whatsapp_msg_type: 'image',
}),
},
sticker: {
type: 'sticker',
contentToSend: (msg) => ({
...mediaMsg.contentToSend({ ...msg, type: 'sticker' }),
msgtype: 'sticker',
}),
contentToRender: (msg) => ({
...msg,
...mediaMsg.contentToRender(msg),
whatsapp_msg_type: 'sticker',
type: 'photo',
}),
},
video: {
type: 'video',
contentToSend: (msg) => ({
...mediaMsg.contentToSend(msg),
msgtype: 'video',
}),
contentToRender: (msg) => ({
...msg,
...mediaMsg.contentToRender(msg),
whatsapp_msg_type: 'video',
}),
},
document: {
type: 'document',
contentToSend: (msg) => ({
...mediaMsg.contentToSend(msg),
msgtype: 'document',
}),
contentToRender: (msg) => ({
...msg,
...mediaMsg.contentToRender(msg),
text: (msg?.name || '') + `\n${msg?.text || ''}`,
title: msg?.name || '',
originText: msg?.name || '',
whatsapp_msg_type: 'document',
type: 'file',
}),
},
whatsappTemplate: {
contentToSend: (msg) => ({
action: 'message',
actionId: msg.id,
renderId: msg.id,
to: msg.to,
msgtype: 'template',
msgcontent: {
...msg.template,
components: [
...msg.template.components.filter((com) => !['footer', 'buttons', 'header'].includes(com.type.toLowerCase())),
...(msg.template.components.filter((com) => 'header' === com.type.toLowerCase()).length > 0
? msg.template.components
.filter((com) => 'header' === com.type.toLowerCase())
.map((ele) => ({ type: 'header', parameters: [{ text: ele.text, type: ele.format.toLowerCase(), [ele.format.toLowerCase()]: { link: ele.example.header_url[0] } }] }))
: []),
...(msg.template.components.filter((com) => 'buttons' === com.type.toLowerCase()).length > 0
? msg.template.components
.filter((com) => 'buttons' === com.type.toLowerCase())[0]
// .buttons.filter((btns) => ! ['phone_number', 'url'].includes( btns.type.toLowerCase()))
.buttons.filter((btns) => ! isEmpty(btns.example)) // 静态按钮不发
.map((btn, btnI) => ({ type: 'button', sub_type: btn.type.toLowerCase(), index: btnI,
// parameters: [{ text: 'lq1FTtA8', type: 'text' }]
}))
: []),
],
},
}),
contentToRender: (msg) => {
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : null;
// const templateParam = (templateDataMapped?.body?.parameters || []).map(e => e.text);
// const fillTemplate = templateParam.length ? replaceTemplateString(msg.template_origin.components.body?.[0]?.text || '', templateParam) : (msg.template_origin.components.body?.[0]?.text || '');
// const footer = msg.template_origin.components?.footer?.[0]?.text || '';
return {
...msg,
template: {
...msg.template,
components: [
...msg.template.components.filter((com) => !['footer', 'buttons', 'header'].includes(com.type.toLowerCase())),
...(msg.template.components.filter((com) => 'header' === com.type.toLowerCase()).length > 0
? msg.template.components
.filter((com) => 'header' === com.type.toLowerCase())
.map((ele) => ({ type: 'header', parameters: [{ text: ele.text, type: ele.format.toLowerCase(), [ele.format.toLowerCase()]: { link: ele.example.header_url[0] } }] }))
: []),
...(msg.template.components.filter((com) => 'buttons' === com.type.toLowerCase()).length > 0
? msg.template.components
.filter((com) => 'buttons' === com.type.toLowerCase())[0]
// .buttons.filter((btns) => ! ['phone_number', 'url'].includes( btns.type.toLowerCase()))
.buttons.filter((btns) => ! isEmpty(btns.example)) // 静态按钮不发
.map((btn, btnI) => ({ type: 'button', sub_type: btn.type.toLowerCase(), index: btnI,
// parameters: [{ text: 'lq1FTtA8', type: 'text' }]
}))
: []),
],
},
actionId: msg.id,
conversationid: msg.id.split('.')[0],
type: 'text',
title: msg.template.name, // || msg.template_origin.components.header?.[0]?.text || '',
text: autoLinkText(templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '',
};
},
},
};
const whatsappMsgMapped = {
'whatsapp.inbound_message.received': {
getMsg: (result) => {
console.log('whatsapp.inbound_message.received', result);
return isEmpty(result?.whatsappInboundMessage) ? null : { ...result.whatsappInboundMessage, conversationid: result.conversationid, messageorigin: result.messageorigin };
},
contentToRender: (contentObj) => {
console.log('whatsapp.inbound_message.received to render', contentObj);
// const contentObj = result?.whatsappInboundMessage || result; // debug:
return parseRenderMessageItem(contentObj);
},
contentToUpdate: () => null,
},
'whatsapp.message.updated': {
getMsg: (result) => {
// console.log('getMsg', result);
return isEmpty(result?.whatsappMessage) ? null : { ...result.whatsappMessage, conversationid: result.conversationid, messageorigin: result.messageorigin };
},
contentToRender: (contentObj) => {
if (contentObj?.status === 'failed' && ['130472', 'BAD_REQUEST'].includes(contentObj.errorCode)) {
contentObj = {
...contentObj,
type: 'error',
text: { body: `${whatsappError?.[contentObj.errorCode] || contentObj.errorMessage}` }, // contentObj.errorMessage // Message failed to send.
id: contentObj.id,
wamid: contentObj.id,
};
return parseRenderMessageItem(contentObj);
}
// * 仅更新消息状态, 没有输出
return null;
},
contentToUpdate: (msgcontent) => ({
...msgcontent,
...parseRenderMessageItem(msgcontent),
id: msgcontent.wamid,
status: msgStatusRenderMapped[(msgcontent?.status || 'failed')],
sender: 'me',
dateString: msgcontent.status==='failed' ? `发送失败 ${whatsappError?.[msgcontent.errorCode] || msgcontent.errorMessage || ''}` : '',
}),
},
};
export const msgStatusRenderMapped = {
'accepted': 'waiting', // 'sent', // 接口的发送请求
'sent': 'sent',
'delivered': 'received',
'read': 'read',
'failed': 'failed',
};
export const msgStatusRenderMappedCN = {
'accepted': '[发送ing]',
'sent': '[已发送]',
'delivered': '[已送达]',
'read': '[已读]',
'failed': '❗',
};
export const receivedMsgTypeMapped = {
...cloneDeep(whatsappMsgMapped),
'message': {
// 发送消息的同步记录 status: 'accepted'
getMsg: (result) => ({ ...result, conversationid: result.actionId.split('.')[0] }),
contentToRender: () => null,
contentToUpdate: (msgcontent) => ({
...msgcontent,
actionId: msgcontent.actionId,
id: msgcontent.wamid,
status: msgStatusRenderMapped[(msgcontent?.status || 'failed')],
conversationid: msgcontent.actionId.split('.')[0], // msgcontent.conversation.sn,
date: msgcontent.createTime,
sender: 'me',
}),
},
'error': {
// 发送消息的同步返回: 发送失败时
getMsg: (result) => result,
contentToRender: () => null,
contentToUpdate: (msgcontent) => {
let apiErrorCode,
apiErrorMsg = '';
const waCode = msgcontent.error.message.match(/\(#(\d+)\)/);
let waError = whatsappError?.[waCode?.[1]] || msgcontent.error.message;
apiErrorMsg = whatsappError[msgcontent.error.code];
if (msgcontent.error.message.includes('Invalid E.146 phone number')) {
waError = whatsappError.INVALID_PHONE_NUMBER;
apiErrorMsg = '';
}
return {
...msgcontent,
id: msgcontent.actionId,
status: msgcontent?.status || 'failed',
dateString: `发送失败 ${waError} \n[${msgcontent.error.code}] ${apiErrorMsg}`,
conversationid: msgcontent.actionId.split('.')[0],
};
},
},
};
export const whatsappMsgTypeMapped = {
error: {
type: (_m) => ({ type: 'system' }),
data: (msg) => ({ id: msg.wamid, text: msg.errorCode ? msg.errorMessage : msg.text.body }),
},
system: {
type: 'system',
data: (msg) => ({ id: msg.wamid, text: msg.system.body }),
},
text: {
type: 'text',
data: (msg) => ({ id: msg.wamid, text: autoLinkText(msg.text.body), originText: msg.text.body, title: msg?.customerProfile?.name || '' }),
renderForReply: (msg) => ({ id: msg.wamid, message: msg.text.body }),
},
image: {
type: 'photo',
data: (msg) => ({
id: msg.wamid,
text: msg.image.caption,
data: {
id: msg.wamid,
uri: msg.image.link,
width: 'auto',
height: 200,
alt: msg.image.caption,
status: {
click: true,
loading: 0,
download: true,
},
},
originText: msg.image?.caption || '',
}),
renderForReply: (msg) => ({
id: msg.wamid,
photoURL: msg.image.link,
width: 'auto',
height: 200,
alt: msg.image?.caption || '',
message: msg.image?.caption || '',
}),
},
sticker: {
type: 'photo',
data: (msg) => ({
id: msg.wamid,
data: {
id: msg.wamid,
uri: msg.sticker.link,
width: '100%',
height: 120,
alt: '',
status: {
click: true,
loading: 0,
download: true,
},
},
}),
renderForReply: (msg) => ({
id: msg.wamid,
photoURL: msg.sticker.link,
width: '100%',
height: 200,
alt: '',
message: '[表情]',
}),
},
video: {
type: 'video',
data: (msg) => ({
id: msg.wamid,
text: msg.video.caption,
data: {
id: msg.wamid,
videoURL: msg.video.link,
status: {
click: true,
loading: 0,
download: true,
},
height: 200,
width: '100%',
},
}),
renderForReply: (msg) => ({
id: msg.wamid,
videoURL: msg.video.link,
photoURL: msg.video.link,
message: msg.video?.caption || '[视频]',
width: 200,
height: 200,
alt: '',
}),
},
audio: {
type: 'audio',
data: (msg) => ({
id: msg.wamid,
data: {
audioURL: msg.audio.link,
},
}),
renderForReply: (msg) => ({
id: msg.wamid,
message: '[语音]',
}),
},
// unsupported: { type: 'system', data: (msg) => ({ text: 'Message type is currently not supported.' }) },
unsupported: {
type: 'text',
data: (msg) => ({ id: msg.wamid, text: `[暂不支持此消息类型](${msg.wamid})`, dateString: `${dayjs(msg.sendTime).format('MM-DD HH:mm')} [ WhatsApp未提供消息内容 ] 可能是客人删除消息/会话, \n可询问客人截图/详细内容 或 忽略 📌` }),
renderForReply: (msg) => ({ id: msg?.wamid || msg.id, text: `[Message type unsupported](${msg.wamid})` }),
},
reaction: {
type: 'text',
data: (msg) => ({ id: msg.wamid, text: msg.reaction?.emoji || '' }),
},
document: {
type: 'file',
data: (msg) => ({
id: msg.wamid,
title: msg.document?.filename || '',
text: msg.document?.caption || msg.document?.filename || '',
data: { uri: msg.document.link, status: { click: false, download: true, loading: 0 } },
originText: msg.document?.caption || msg.document?.filename || '',
}),
renderForReply: (msg) => ({
id: msg.wamid,
message: msg.document?.caption || msg.document?.filename || '[文件]',
}),
},
contacts: {
type: 'meetingLink',
data: (msg) => ({
id: msg.wamid,
meetingID: msg.wamid,
title: msg.contacts.length === 1 ? `联系人` : `${msg.contacts.length} 位联系人`,
text: msg.contacts.map((ele) => `${(ele?.org?.company || '') +' '+ele.name.formatted_name}: ${ele.phones[0].wa_id || ele.phones[0].phone}`).join('\n'),
data: msg.contacts.map((ele) => ({ id: ele.phones[0]?.wa_id || ele.phones[0].phone, wa_id: ele.phones[0]?.wa_id || '', phone: ele.phones[0].phone, name: (ele?.org?.company || '') +' '+ele.name.formatted_name })),
waBtn: msg.contacts.some(ele => ele.phones.some(p => p.wa_id)),
}),
renderForReply: (msg) => ({
id: msg.wamid,
message: '[联系人] ' + msg.contacts[0].name.formatted_name + '...',
}),
},
location: {
type: 'location',
data: (msg) => ({
id: msg.wamid,
title: `位置信息 ${msg.location.name || ''} ↓打开高德地图`,
text: msg.location.address, // 地址
src: `https://uri.amap.com/marker?position=${msg.location.longitude},${msg.location.latitude}&callnative=1`,
data: {
longitude: msg.location?.longitude,
latitude: msg.location?.latitude,
},
originText: msg.location?.address || '',
}),
renderForReply: (msg) => ({
id: msg.wamid,
message: '[位置]',
}),
},
template: {
type: 'text',
data: (msg) => {
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : {};
return { id: msg.wamid, text: autoLinkText(templateDataMapped?.body?.text || `......${(templateDataMapped?.body?.parameters || []).map(pv => pv?.text || '').join('......')}......`), title: msg.template.name };
},
renderForReply: (msg) => {
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : null;
return { id: msg.wamid, message: templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || '', title: `${msg.template.name}` };
},
},
};
/**
* render received msg
*/
export const parseRenderMessageItem = (msg) => {
console.log('parseRenderMessageItem', msg);
const thisMsgType = Object.keys(whatsappMsgTypeMapped).includes(msg.type) ? msg.type : 'unsupported';
return {
msgOrigin: msg,
date: msg?.sendTime || msg?.createTime || '',
...(whatsappMsgTypeMapped?.[thisMsgType]?.data(msg) || {}),
conversationid: msg.conversationid,
...(typeof whatsappMsgTypeMapped[thisMsgType].type === 'function' ? whatsappMsgTypeMapped[thisMsgType].type(msg) : { type: whatsappMsgTypeMapped[thisMsgType].type || 'text' }),
// type: whatsappMsgTypeMapped?.[thisMsgType]?.type || 'text',
from: msg.from,
sender: msg.from,
senderName: msg?.customerProfile?.name || 'me', // msg.from,
// title: msg.customerProfile.name,
customer_name: msg?.customerProfile?.name || '',
whatsapp_name: msg?.customerProfile?.name || '',
whatsapp_phone_number: isEmpty(msg?.customerProfile) ? msg.to : msg.from,
whatsapp_msg_type: msg.type,
statusCN: msgStatusRenderMappedCN[msg?.status || 'failed'],
statusTitle: msgStatusRenderMappedCN[msg?.status || 'failed'],
replyButton: ['text', 'document', 'image'].includes(msg.type) && (msg?.status || '') !== 'failed',
...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true // || isEmpty(msg.messageorigin)
? {}
: {
reply: {
/**
* reply: { message: msg.messageorigin, title: 'React from', titleColor: '#1ba784' }
*/
title: isEmpty(msg.messageorigin) ? '...' : msg.messageorigin?.customerProfile?.name || 'me',
...(typeof whatsappMsgTypeMapped[(msg?.messageorigin?.type || 'unsupported')]?.renderForReply === 'function'
? whatsappMsgTypeMapped[(msg?.messageorigin?.type || 'unsupported')].renderForReply(msg.messageorigin)
: {}),
titleColor: msg.messageorigin?.customerProfile?.name && msg.messageorigin?.sender !== 'me' ? '#a791ff' : "#128c7e",
id: msg.context?.id || msg.reaction?.message_id,
},
origin: msg.context,
}),
};
};
/**
* 从数据库读取的记录
*/
export const parseRenderMessageList = (messages) => {
return messages.map((msg, i) => {
let msgContentString = '';
if (typeof msg.msgtext_AsJOSN === 'string') {
// debug: json 缺少一部分
msgContentString = msg.msgtext_AsJOSN.charAt(msg.msgtext_AsJOSN.length - 1) !== '}' ? msg.msgtext_AsJOSN + '}}' : msg.msgtext_AsJOSN;
}
const msgContent = typeof msg.msgtext_AsJOSN === 'string' ? JSON.parse(msgContentString) : msg.msgtext_AsJOSN;
msgContent.template = msg.msgtype === 'template' ? { ...msgContent.template, ...msg.template_AsJOSN } : {};
const msgType = Object.keys(whatsappMsgTypeMapped).includes(msgContent.type) ? msgContent.type : 'unsupported';
// const parseMethod = msgContent.bizType === 'whatsapp' ? cloneDeep(whatsappMsgTypeMapped) : {};
let waCode, waError = '';
if ((msgContent?.status || 'failed') === 'failed' && msgContent.errorMessage && msg.msg_direction === 'outbound') {
(waCode = msgContent.errorMessage.match(/\(#(\d+)\)/));
(waError = (whatsappError?.[waCode?.[1]] || whatsappError?.[msgContent.errorCode] || msgContent.errorMessage));
if (!isEmpty(msgContent.whatsappApiError)) {
waError = whatsappError?.[msgContent.whatsappApiError.code] || msgContent.whatsappApiError.message;
// waError += `\n[${msgContent.errorCode}] ${whatsappError?.[msgContent.errorCode] || msgContent.errorMessage}`;
}
if (msgContent.errorMessage.includes('Invalid E.146 phone number')) {
waError = whatsappError.INVALID_PHONE_NUMBER;
}
}
return {
...msg,
msgOrigin: msgContent,
...(whatsappMsgTypeMapped?.[msgType]?.data(msgContent) || {}),
type: msgContent.type,
...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }),
date: msgContent?.sendTime || msg.msgtime || '',
dateText: dayjs(msgContent?.sendTime || msg.msgtime).format('MM-DD HH:mm'),
localDate: (msg.msgtime || '').replace('T', ' '),
from: msgContent.from,
sender: msgContent.from,
senderName: msgContent?.customerProfile?.name || 'me', // msgContent.from,
replyButton: ['text', 'document', 'image'].includes(msgContent.type) && (msgContent?.status || '') !== 'failed',
...(msg.msg_direction === 'outbound'
? {
sender: 'me',
senderName: 'me',
status: msgStatusRenderMapped[msgContent?.status || 'failed'],
dateString: msgStatusRenderMapped[msgContent?.status || 'failed'] === 'failed' ? `${(msg.msgtime || '').replace('T', ' ')} 发送失败 ${waError}` : '',
statusCN: msgStatusRenderMappedCN[msgContent?.status || 'failed'],
statusTitle: msgStatusRenderMappedCN[msgContent?.status || 'failed'],
}
: {}),
...((isEmpty(msg.messageorigin_AsJOSN) && (isEmpty(msgContent.context) || msgContent.context?.forwarded === true))
// ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true || isEmpty(msg.messageorigin)
// ...((isEmpty(msg.messageorigin_AsJOSN) || isEmpty(msgContent.context))
? {}
: {
reply: {
message: msg.messageorigin_AsJOSN?.text?.body || msg.messageorigin_AsJOSN?.text,
title: msg.messageorigin_AsJOSN?.customerProfile?.name || msg.messageorigin_AsJOSN?.senderName || 'me',
...(typeof whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')]?.renderForReply === 'function'
? whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')].renderForReply(msg.messageorigin_AsJOSN)
: {}),
titleColor: msg.messageorigin_AsJOSN?.customerProfile?.name ? '#a791ff' : "#128c7e",
// titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e",
id: msgContent.context?.id || msgContent.context?.message_id || msgContent.reaction?.message_id,
},
origin: msg.messageorigin_AsJOSN,
}),
// conversationid: conversationid,
// title: msg.customerProfile.name,
whatsapp_msg_type: msgContent.type,
};
});
};
export const whatsappError = {
'BAD_REQUEST': ' ',
'PARAM_INVALID': '参数错误, 请联系技术组',
'INVALID_PHONE_NUMBER': '无效号码, 请修正号码后尝试发送',
'100': '参数错误, 请联系技术组',
'131026': '[131026] 消息无法投递(未同意WhatsApp 的隐私政策).\n请使用邮件联系',
'131047': '[131047] 会话未激活. \n请使用模板消息发送',
'131053': '[131053] 文件上传失败.',
'131048': '[131048] 账户被风控.', // 消息发送太多, 达到垃圾数量限制
'131031': '[131031] 账户已锁定.',
'130472': '[130472] 此号码不接收商业号消息\n请使用邮件联系 或 引导客户主动发起会话.',
};
export const whatsappSupportFileTypes = {
sticker: { types: ['image/webp'], size: 1024 * 100 },
photo: { types: ['image/jpeg', 'image/png'], size: 1024 * 1024 * 5 },
video: { types: ['video/mp4', 'video/3gp'], size: 1024 * 1024 * 16 },
document: {
types: [
'text/plain',
'application/pdf',
'application/vnd.ms-powerpoint',
'application/msword',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
],
size: 1024 * 1024 * 100,
},
audio: { types: ['audio/aac', 'audio/mp4', 'audio/mpeg', 'audio/amr', 'audio/ogg'], size: 1024 * 1024 * 16 },
};
/**
* 系统弹窗通知
*/
export const handleNotification = (title, _options) => {
if (!("Notification" in window)) {
// alert("This browser does not support desktop notification");
return false;
}
var notification;
const options = {
// requireInteraction: true, // 设置手动关闭
// tag: 'global-sales-notification', // 通知ID同类通知建议设置相同ID避免通知过多遮挡桌面
// icon: '/favicon.ico', // 通知图标
..._options,
};
// 检查用户是否同意接受通知
if (Notification.permission === 'granted') {
notification = new Notification(title, options);
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
notification = new Notification(title, options);
}
});
} else {
// 用户拒绝通知权限
}
// 通知弹窗被点击后的回调
if (typeof notification !== 'undefined') {
notification.onclick = () => {
// window.parent.parent.focus();
window.focus(); // 显示当前标签页
notification.close(); // 关闭通知,适用于设置了手动关闭的通知
};
}
};
/**
* WhatsApp 国际号码
*
* - Make sure to remove any leading 0s or special calling codes.
* - All phone numbers in Argentina (country code "54") should have a "9" between the country code and area code. The prefix "15" must be removed so the final number will have 13 digits total: +54 9 XXX XXX XXXX
* - Phone numbers in Mexico (country code "52") need to have "1" after "+52", even if they're Nextel numbers.
* - 巴西号码: 13位的话删除国家地区码后面的`9`
*/
export const phoneNumberToWAID = (input) => {
// Remove any non-digit characters
const cleanedInput = (input.replace(/[^\d]/g, ''))
.replace(/^0+/, '') // Remove leading zeros
;
// Check if the number starts with a valid country code
const countryCode = cleanedInput.slice(0, 2);
const isArgentina = countryCode === '54';
const isMexico = countryCode === '52';
const isBrazil = countryCode === '55';
// if (!isArgentina && !isMexico) {
if (!isArgentina) {
return cleanedInput; // Return the cleaned input as is
}
// Remove leading 0s and special calling codes
let formattedNumber = cleanedInput.replace(/^0+/, '');
if (isArgentina) {
// Remove the prefix "15" if present
// formattedNumber = formattedNumber.replace(/^15/, '');
formattedNumber = formattedNumber.replace(/^54 ?15/, '54');
// Remove leading '0' after country code
formattedNumber = formattedNumber.replace(/^54 ?0/, '54');
// Insert "9" between the country code and area code
formattedNumber = `54 9 ${formattedNumber.slice(2)}`;
} else if (isMexico) {
// Remove leading '0' after country code
// formattedNumber = formattedNumber.replace(/^52 ?0/, '52');
// Insert "1" after the country code
// formattedNumber = `52 1 ${formattedNumber.slice(2)}`;
} else if (isBrazil) {
if (cleanedInput.length > 12) {
formattedNumber = cleanedInput.slice(0, 4) + cleanedInput.slice(-8);
}
}
// Remove any non-digit characters
formattedNumber = formattedNumber.replace(/[^\d]/g, '');
return formattedNumber;
}
export const uploadProgressSimulate = () => fixTo2Decimals(Math.random() * (0.8 - 0.2) + 0.2);