feat:增加 WhatsApp Baileys 本地调试

main
LiaoYijun 7 months ago
parent 21b86f656a
commit e3fc505d41

@ -27,6 +27,7 @@ import ChatAssign from '@/views/Conversations/ChatAssign'
import DingdingLogin from '@/views/dingding/Login'
import DingdingQRCode from '@/views/dingding/QRCode'
import DingdingAuthCode from '@/views/dingding/AuthCode'
import LocalWhatsAppQRCode from '@/views/accounts/LocalWhatsAppQRCode'
import useAuthStore from '@/stores/AuthStore'
import '@/assets/index.css'
@ -107,6 +108,7 @@ const router = createBrowserRouter([
{ path: 'dingding/callback', element: <DingdingCallback /> },
{ path: 'dingding/qr-code', element: <DingdingQRCode /> },
{ path: 'dingding/auth-code', element: <DingdingAuthCode /> },
{ path: 'whatsapp/qr-code', element: <LocalWhatsAppQRCode /> },
],
},
])

@ -1,6 +1,8 @@
import { BUILD_VERSION } from '@/config'
// Ref: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
const customHeaders = []
// 添加 HTTP Reuqest 自定义头部

@ -0,0 +1,24 @@
import { useEffect, useCallback, useState } from 'react'
import { Typography, App, Flex, Input, QRCode } from 'antd'
import { ReloadOutlined, CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons'
import useAuthStore from '@/stores/AuthStore'
import { fetchQRCode } from '@/actions/WaiAction'
import useConversationStore from '@/stores/ConversationStore'
import { isEmpty } from '@/utils/commons'
// QR Code WhatsApp Baileys
const LocalWhatsAppQRCode = () => {
const [qrCode, setQRCode] = useState('expired')
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Typography.Title level={4}>WhatsApp Baileys</Typography.Title>
<QRCode size={264} value={qrCode} errorLevel='L' />
<Input style={{ width: 400, marginTop: 16 }} placeholder='QR Code' onChange={(e) => {
setQRCode(e.target.value)
}}/>
</Flex>
)
}
export default LocalWhatsAppQRCode

@ -240,6 +240,35 @@ const createWhatsApp = async phone => {
}
}
const handleTemplateMessage = async msgObj => {
const text = msgObj.message.templateMessage?.hydratedTemplate?.hydratedContentText;
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),
});
};
const handleMessagesUpdate = async messageUpdate => {
console.info('messages.update: ', JSON.stringify(messageUpdate, undefined, 2));
@ -354,6 +383,7 @@ const createWhatsApp = async phone => {
// 第一次一分钟,后面是 20 秒更新一次
if (qrCode === null) {
qrCode = qr;
console.info('QR Code: ' + qrCode);
waEmitter.emit('creds:update', {
id: generateId(),
qr, whatsAppNo,

@ -1,164 +0,0 @@
const {
makeWASocket,
Browsers,
DisconnectReason,
fetchLatestBaileysVersion,
makeCacheableSignalKeyStore,
makeInMemoryStore,
useMultiFileAuthState,
delay,
downloadMediaMessage,
isJidUser
} = require('@whiskeysockets/baileys');
const { formatPhoneNumber, parsePhoneNumber, formatStatus, formatTimestamp } = require('./helper');
const NodeCache = require('node-cache');
const P = require('pino');
const logger = P({ timestamp: () => `,"time":"${new Date().toJSON()}"` }, P.destination('./wa-logs.txt'))
logger.level = 'trace'
// external map to store retry counts of messages when decryption/encryption fails
// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
const msgRetryCounterCache = new NodeCache()
// the store maintains the data of the WA connection in memory
// can be written out to a file & read from it
const store = makeInMemoryStore({ logger })
store?.readFromFile('./baileys_store_multi.json')
// save every 10s
setInterval(() => {
store?.writeToFile('./baileys_store_multi.json')
}, 10_000)
// start a connection
const startSock = async() => {
const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
// fetch latest version of WA Web
const { version, isLatest } = await fetchLatestBaileysVersion()
console.log(`using WA v${version.join('.')}, isLatest: ${isLatest}`)
const sock = makeWASocket({
version,
logger,
auth: {
creds: state.creds,
/** caching makes the store faster to send/recv messages */
keys: makeCacheableSignalKeyStore(state.keys, logger),
},
msgRetryCounterCache,
generateHighQualityLinkPreview: true,
})
store?.bind(sock.ev)
const sendMessageWTyping = async(msg, jid) => {
await sock.presenceSubscribe(jid)
await delay(500)
await sock.sendPresenceUpdate('composing', jid)
await delay(2000)
await sock.sendPresenceUpdate('paused', jid)
await sock.sendMessage(jid, msg)
}
return new Promise((resolve) => {
// the process function lets you process all events that just occurred
// efficiently in a batch
sock.ev.process(
// events is a map for event name => event data
async(events) => {
// something about the connection changed
// maybe it closed, or we received all offline message or connection opened
if(events['connection.update']) {
const update = events['connection.update']
const { connection, qr, lastDisconnect } = update
if(connection === 'close') {
// reconnect if not logged out
if((lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut) {
startSock()
} else {
console.log('Connection closed. You are logged out.')
}
}
if (connection === 'open') {
console.log('Connection open.')
// setInterval(async() => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < 10; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
sendMessageWTyping({ text: result + "-local.Connection open 发送:" + new Date().toString() }, '8613317835586@s.whatsapp.net')
// }, 1000*60*5)
setInterval(async() => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < 10; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
sendMessageWTyping({ text: result + "-setInterval(5min) 发送:" + new Date().toString() }, '8613317835586@s.whatsapp.net')
}, 1000*60*5)
}
if (qr !== undefined) {
resolve(qr);
}
console.log('connection update', update)
}
if(events['creds.update']) {
await saveCreds()
}
// received a new message
if(events['messages.upsert']) {
const upsert = events['messages.upsert']
console.log('recv messages ', JSON.stringify(upsert, undefined, 2))
if(upsert.type === 'notify') {
for (const msg of upsert.messages) {
if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) {
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text
if (text == "requestPlaceholder" && !upsert.requestId) {
const messageId = await sock.requestPlaceholderResend(msg.key)
console.log('requested placeholder resync, id=', messageId)
} else if (upsert.requestId) {
console.log('Message received from phone, id=', upsert.requestId, msg)
}
}
}
}
}
}
)
})
//return sock
}
async function go() {
const qrCode = await startSock();
console.info('qr code: ', qrCode);
}
go();

@ -1,100 +0,0 @@
function generateRandomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const start = () => {
setInterval(() => {
const randomString = generateRandomString(10);
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "*/*");
myHeaders.append("Host", "wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn");
myHeaders.append("Connection", "keep-alive");
var raw = JSON.stringify({
"from": "8618777396951",
"to": "8613557032060",
"content": randomString + "-setInterval(2min) 发送:" + new Date().toString()
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn/api/v1/channels/send", requestOptions)
.then(rsp => rsp.json())
.then(json => console.info('8613557032060: ', json))
.catch(ex => console.error(ex));
}, 1000*60*2);
//
setInterval(() => {
const randomString = generateRandomString(10);
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "*/*");
myHeaders.append("Host", "wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn");
myHeaders.append("Connection", "keep-alive");
var raw = JSON.stringify({
"from": "8618777396951",
"to": "8613317835586",
"content": randomString + "-setInterval(3min) 发送:" + new Date().toString()
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn/api/v1/channels/send", requestOptions)
.then(rsp => rsp.json())
.then(json => console.info('8613317835586: ', json))
.catch(ex => console.error(ex));
}, 1000*60*3);
//
setInterval(() => {
const randomString = generateRandomString(10);
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "*/*");
myHeaders.append("Host", "wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn");
myHeaders.append("Connection", "keep-alive");
var raw = JSON.stringify({
"from": "8618777396951",
"to": "8617607735120",
"content": randomString + "-setInterval(5min) 发送:" + new Date().toString()
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://wai-server-01-qq4qmtq7wc9he4.chinahighlights.cn/api/v1/channels/send", requestOptions)
.then(rsp => rsp.json())
.then(json => console.info('8613317835586: ', json))
.catch(ex => console.error(ex));
}, 1000*60*5);
}
start();

@ -0,0 +1,8 @@
const { createWhatsApp } = require('./core/baileys');
const go = async () => {
const whatsApp = await createWhatsApp('8618777396951');
whatsApp.start();
}
go();
Loading…
Cancel
Save