@ -1,12 +1,12 @@
const {
const {
makeWASocket ,
makeWASocket ,
WAProto ,
Browsers ,
DisconnectReason ,
DisconnectReason ,
fetchLatestBaileysVersion ,
fetchLatestBaileysVersion ,
makeCacheableSignalKeyStore ,
makeCacheableSignalKeyStore ,
makeInMemoryStore ,
makeInMemoryStore ,
useMultiFileAuthState ,
useMultiFileAuthState ,
downloadMediaMessage
downloadMediaMessage ,
} = require ( '@whiskeysockets/baileys' ) ;
} = require ( '@whiskeysockets/baileys' ) ;
const { writeFile } = require ( 'fs/promises' ) ;
const { writeFile } = require ( 'fs/promises' ) ;
const waEmitter = require ( '../emitter' ) ;
const waEmitter = require ( '../emitter' ) ;
@ -15,254 +15,282 @@ const generateId = require('../../utils/generateId.util');
const NodeCache = require ( 'node-cache' ) ;
const NodeCache = require ( 'node-cache' ) ;
const P = require ( 'pino' ) ;
const P = require ( 'pino' ) ;
const logger = P ( { timestamp : ( ) => ` ,"time":" ${ new Date ( ) . toJSON ( ) } " ` } , P . destination ( './wa-logs.txt' ) )
const logger = P ( { timestamp : ( ) => ` ,"time":" ${ new Date ( ) . toJSON ( ) } " ` } , P . destination ( './wa-logs.txt' ) ) ;
logger . level = 'trace'
logger . level = 'trace' ;
// external map to store retry counts of messages when decryption/encryption fails
// 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
// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
const msgRetryCounterCache = new NodeCache ( )
const msgRetryCounterCache = new NodeCache ( ) ;
// the store maintains the data of the WA connection in memory
// the store maintains the data of the WA connection in memory
// can be written out to a file & read from it
// can be written out to a file & read from it
const store = makeInMemoryStore ( { logger } )
const store = makeInMemoryStore ( { logger } ) ;
store ? . readFromFile ( './baileys_store_multi.json' )
store ? . readFromFile ( './baileys_store_multi.json' ) ;
// save every 10s
// save every 10s
setInterval ( ( ) => {
setInterval ( ( ) => {
store ? . writeToFile ( './baileys_store_multi.json' )
store ? . writeToFile ( './baileys_store_multi.json' ) ;
} , 10_000 )
} , 10_000 ) ;
waEmitter . on ( 'message:updated' , event => { console . info ( 'msg:event' , event ) } )
waEmitter . on ( 'message:updated' , event => {
waEmitter . on ( 'message:received' , event => { console . info ( 'msg:event' , event ) } )
console . info ( 'msg:event' , event ) ;
} ) ;
waEmitter . on ( 'message:received' , event => {
console . info ( 'msg:event' , event ) ;
} ) ;
// start a connection
// start a connection
const createWhatsApp = async ( phone ) => {
const createWhatsApp = async phone => {
const channelId = generateId ( )
const channelId = generateId ( ) ;
const whatsAppNo = phone
const whatsAppNo = phone ;
const { state , saveCreds } = await useMultiFileAuthState ( 'baileys_auth_info/' + phone )
const { state , saveCreds } = await useMultiFileAuthState ( 'baileys_auth_info/' + phone ) ;
// fetch latest version of WA Web
// fetch latest version of WA Web
const { version , isLatest } = await fetchLatestBaileysVersion ( )
const { version , isLatest } = await fetchLatestBaileysVersion ( ) ;
const waVersion = version . join ( '.' ) + ', ' + ( isLatest ? 'latest' : 'out' )
const waVersion = version . join ( '.' ) + ', ' + ( isLatest ? 'latest' : 'out' ) ;
const formatPhoneNumber = ( number ) => {
const formatPhoneNumber = number => {
if ( number === null || number === undefined ) return '' ;
if ( number === null || number === undefined ) return ''
if ( number . indexOf ( '@g.us' ) > - 1 ) {
if ( number . indexOf ( '@g.us' ) > - 1 ) {
return number
return number ;
} else if ( number . indexOf ( '@broadcast' ) > - 1 ) {
} else if ( number . indexOf ( '@broadcast' ) > - 1 ) {
return number
return number ;
} else {
} else {
return number + '@s.whatsapp.net'
return number + '@s.whatsapp.net' ;
}
}
}
} ;
const parsePhoneNumber = ( number ) => {
if ( number === null || number === undefined ) return ''
const parsePhoneNumber = number => {
if ( number === null || number === undefined ) return '' ;
if ( number . indexOf ( '@s.whatsapp.net' ) > - 1 ) {
if ( number . indexOf ( '@s.whatsapp.net' ) > - 1 ) {
return number . split ( '@' ) [ 0 ]
return number . split ( '@' ) [ 0 ] ;
} else {
} else {
return number
return number ;
}
}
// status: sent read delivered failed
// 2 sent, 3 delivered, 4 read, 0 error
// Time: 2008-07-07 15:37:07
const formatStatus = ( number ) => {
if ( number === 2 ) return 'sent'
else if ( number === 3 ) return 'delivered'
else if ( number === 4 ) return 'read'
else if ( number === 0 ) return 'error'
else return 'played'
}
}
const formatTimestamp = ( timestamp ) => {
} ;
if ( timestamp === null ) return ''
const datetime = new Date ( timestamp * 1000 )
// status: sent read delivered failed
// 2 sent, 3 delivered, 4 read, 0 error
// Time: 2008-07-07 15:37:07
return datetime . getFullYear ( ) + '-' + ( datetime . getMonth ( ) + 1 ) + '-' + datetime . getDay ( ) + ' ' + datetime . getHours ( ) + ':' + datetime . getMinutes ( ) + ':' + datetime . getSeconds ( )
const formatStatus = number => {
}
if ( number === 2 ) return 'sent' ;
else if ( number === 3 ) return 'delivered' ;
else if ( number === 4 ) return 'read' ;
else if ( number === 0 ) return 'error' ;
else return 'played' ;
} ;
const formatTimestamp = timestamp => {
if ( timestamp === null ) return '' ;
const datetime = new Date ( timestamp * 1000 ) ;
return datetime . getFullYear ( ) + '-' + ( datetime . getMonth ( ) + 1 ) + '-' + datetime . getDay ( ) + ' ' + datetime . getHours ( ) + ':' + datetime . getMinutes ( ) + ':' + datetime . getSeconds ( ) ;
} ;
const sendTextMessage = ( whatsAppNo , content ) => {
const sendTextMessage = ( whatsAppNo , content ) => {
const number = formatPhoneNumber ( whatsAppNo )
const number = formatPhoneNumber ( whatsAppNo ) ;
console . info ( 'formatPhoneNumber: ' , number )
console . info ( 'formatPhoneNumber: ' , number ) ;
waSocket . sendMessage ( number , { text : content } )
waSocket . sendMessage ( number , { text : content } ) ;
}
} ;
const sendImageMessage = ( whatsAppNo , imageUrl ) => {
const sendImageMessage = ( whatsAppNo , imageUrl ) => {
const number = formatPhoneNumber ( whatsAppNo )
const number = formatPhoneNumber ( whatsAppNo ) ;
waSocket . sendMessage ( number , {
waSocket . sendMessage ( number , {
image : { url : imageUrl } ,
image : { url : imageUrl } ,
} )
} ) ;
}
} ;
let waSocket = null
let waSocket = null ;
const start = ( ) => {
const start = ( ) => {
return new Promise ( ( resolve , reject ) => {
return new Promise ( ( resolve , reject ) => {
waSocket = makeWASocket ( {
waSocket = makeWASocket ( {
version ,
version ,
logger ,
logger ,
auth : {
auth : {
creds : state . creds ,
creds : state . creds ,
/** caching makes the store faster to send/recv messages */
/** caching makes the store faster to send/recv messages */
keys : makeCacheableSignalKeyStore ( state . keys , logger ) ,
keys : makeCacheableSignalKeyStore ( state . keys , logger ) ,
} ,
} ,
msgRetryCounterCache ,
// https://github.com/WhiskeySockets/Baileys/blob/31bc8ab/src/Utils/generics.ts#L21
generateHighQualityLinkPreview : true ,
browser : Browsers . macOS ( 'Desktop' ) ,
} )
msgRetryCounterCache ,
generateHighQualityLinkPreview : false ,
// something about the connection changed
syncFullHistory : false ,
} ) ;
// something about the connection changed
// maybe it closed, or we received all offline message or connection opened
// maybe it closed, or we received all offline message or connection opened
waSocket . ev . on ( 'connection.update' , async ( update ) => {
waSocket . ev . on ( 'connection.update' , async update => {
console . log ( 'connection update: ' , update )
console . log ( 'connection update: ' , update ) ;
const { connection , lastDisconnect , qr } = update
const { connection , lastDisconnect , qr } = update ;
if ( connection === 'close' ) {
if ( connection === 'close' ) {
console . log ( '链接断开:' , lastDisconnect )
console . log ( '链接断开:' , lastDisconnect ) ;
if ( lastDisconnect ? . error ? . output ? . statusCode !== DisconnectReason . loggedOut ) {
if ( lastDisconnect ? . error ? . output ? . statusCode !== DisconnectReason . loggedOut ) {
start ( )
start ( ) ;
} else {
} else {
waSocket . end ( ( error ) => console . error ( 'end.error: ' , error ) )
waSocket . end ( error => console . error ( 'end.error: ' , error ) ) ;
waSocket . logout ( ( msg ) => console . error ( 'logout.msg: ' , msg ) )
waSocket . logout ( msg => console . error ( 'logout.msg: ' , msg ) ) ;
console . log ( 'Connection closed. You are logged out.' )
console . log ( 'Connection closed. You are logged out.' ) ;
}
}
} else if ( connection === 'open' ) {
} else if ( connection === 'open' ) {
console . info ( '链接成功' )
console . info ( '链接成功' ) ;
waEmitter . emit ( 'connection.open' , waSocket ) ;
waEmitter . emit ( 'connection.open' , waSocket ) ;
} else if ( qr !== undefined ) {
} else if ( qr !== undefined ) {
// WebSocket 创建成功等待扫码,如果没有扫码会更新 qr
// WebSocket 创建成功等待扫码,如果没有扫码会更新 qr
resolve ( qr )
resolve ( qr ) ;
}
}
} )
} ) ;
waSocket . ev . on ( 'messages.upsert' , async ( upsert ) => {
waSocket . ev . on ( 'messages.upsert' , async upsert => {
console . log ( '收到消息:' , JSON . stringify ( upsert , undefined , 2 ) )
console . log ( '收到消息:' , JSON . stringify ( upsert , undefined , 2 ) ) ;
if ( upsert . type === 'notify' ) {
if ( upsert . type === 'notify' ) {
for ( const msg of upsert . messages ) {
for ( const msg of upsert . messages ) {
const messageType = Object . keys ( msg . message ) [ 0 ] ;
const messageType = Object . keys ( msg . message ) [ 0 ]
console . log ( 'messageType' , messageType ) ;
console . log ( 'messageType' , messageType )
if ( messageType === 'imageMessage' ) {
if ( messageType === 'imageMessage' ) {
// download the message
// download the message
const buffer = await downloadMediaMessage (
const buffer = await downloadMediaMessage (
msg ,
msg ,
'buffer' ,
'buffer' ,
{ } ,
{ } ,
{
{
logger ,
logger ,
// pass this so that baileys can request a reupload of media
// pass this so that baileys can request a reupload of media
// that has been deleted
// that has been deleted
reuploadRequest : waSocket . updateMediaMessage
reuploadRequest : waSocket . updateMediaMessage ,
}
} ,
)
) ;
// save to file
// save to file
await writeFile ( 'd:/my-download.jpeg' , buffer )
await writeFile ( 'd:/my-download.jpeg' , buffer ) ;
console . log ( 'writeFile' , messageType )
console . log ( 'writeFile' , messageType ) ;
}
}
const fromWhatsAppNo = parsePhoneNumber ( msg . key . remoteJid )
const fromWhatsAppNo = parsePhoneNumber ( msg . key . remoteJid ) ;
if ( msg . key . remoteJid . indexOf ( '@g.us' ) > - 1 ) {
const metadata = await waSocket . groupMetadata ( msg . key . remoteJid ) ;
console . log ( '群: ' + metadata . subject + ', description: ' + metadata . desc ) ;
try {
const ppUrl = await waSocket . profilePictureUrl ( msg . key . remoteJid ) ;
console . log ( '群头像: ' + ppUrl ) ;
} catch ( ex ) {
console . error ( '群头像出错了: ' , ex ) ;
}
} else {
console . log ( 'remoteJid: ' , msg . key . remoteJid ) ;
try {
const ppUrl = await waSocket . profilePictureUrl ( msg . key . remoteJid ) ;
console . log ( '个人头像: ' + ppUrl ) ;
} catch ( ex ) {
console . error ( '个人头像出错了: ' , ex ) ;
}
}
if ( msg . message ? . conversation || msg . message ? . extendedTextMessage ? . text ) {
if ( msg . message ? . conversation || msg . message ? . extendedTextMessage ? . text ) {
const text = msg . message ? . conversation || msg . message ? . extendedTextMessage ? . text
const text = msg . message ? . conversation || msg . message ? . extendedTextMessage ? . text ;
if ( text . indexOf ( '图片' ) > - 1 ) {
if ( text . indexOf ( '图片' ) > - 1 ) {
sendImageMessage ( fromWhatsAppNo , 'https://images.asiahighlights.com/allpicture/2022/07/8a7d9ced5936463bb904c82a_cut_750x850_349.webp' )
sendImageMessage ( fromWhatsAppNo , 'https://images.asiahighlights.com/allpicture/2022/07/8a7d9ced5936463bb904c82a_cut_750x850_349.webp' ) ;
} else if ( text . indexOf ( '文本' ) > - 1 ) {
} else if ( text . indexOf ( '文本' ) > - 1 ) {
sendTextMessage ( fromWhatsAppNo , '文本消息' + new Date ( ) . toString ( ) )
sendTextMessage ( fromWhatsAppNo , ' 这是 文本消息: ' + new Date ( ) . toString ( ) ) ;
} else if ( text . indexOf ( '发群' ) > - 1 ) {
} else if ( text . indexOf ( '发群' ) > - 1 ) {
sendTextMessage ( '120363335516526642@g.us' , '这是群消息:' + new Date ( ) . toString ( ) )
sendTextMessage ( '120363335516526642@g.us' , '这是群消息:' + new Date ( ) . toString ( ) ) ;
}
}
if ( msg . key . fromMe ) {
if ( msg . key . fromMe ) {
waEmitter . emit ( 'message:updated' , {
waEmitter . emit ( 'message:updated' , {
"id" : msg . key . id ,
id : msg . key . id ,
"status" : formatStatus ( msg . status ) ,
status : formatStatus ( msg . status ) ,
"from" : whatsAppNo ,
from : whatsAppNo ,
"to" : fromWhatsAppNo ,
to : fromWhatsAppNo ,
"type" : "text" ,
type : 'text' ,
"text" : {
text : {
"body" : text
body : text ,
} ,
} ,
participant : {
participant : {
id : parsePhoneNumber ( msg . key . participant ) ,
id : parsePhoneNumber ( msg . key . participant ) ,
name : msg . pushName
name : msg . pushName ,
} ,
} ,
"updateTime" : formatTimestamp ( msg . messageTimestamp ) ,
updateTime : formatTimestamp ( msg . messageTimestamp ) ,
} ) ;
} ) ;
} else {
} else {
waEmitter . emit ( 'message:received' , {
waEmitter . emit ( 'message:received' , {
"id" : msg . key . id ,
id : msg . key . id ,
"status" : "" ,
status : '' ,
"from" : fromWhatsAppNo ,
from : fromWhatsAppNo ,
"to" : whatsAppNo ,
to : whatsAppNo ,
"type" : "text" ,
type : 'text' ,
"text" : {
text : {
"body" : text
body : text ,
} ,
} ,
participant : {
participant : {
id : parsePhoneNumber ( msg . key . participant ) ,
id : parsePhoneNumber ( msg . key . participant ) ,
name : msg . pushName
name : msg . pushName ,
} ,
} ,
"createTime" : formatTimestamp ( msg . messageTimestamp ) ,
createTime : formatTimestamp ( msg . messageTimestamp ) ,
} ) ;
} ) ;
}
}
}
}
}
}
}
}
} )
} ) ;
waSocket . ev . on ( 'messages.update' , async ( messageUpdate ) => {
waSocket . ev . on ( 'messages.update' , async messageUpdate => {
console . info ( 'messages.update: ' , messageUpdate )
console . info ( 'messages.update: ' , messageUpdate ) ;
for ( const msg of messageUpdate ) {
for ( const msg of messageUpdate ) {
waEmitter . emit ( 'message:updated' , {
waEmitter . emit ( 'message:updated' , {
"id" : msg . key . id ,
id : msg . key . id ,
"status" : formatStatus ( msg . update . status ) ,
status : formatStatus ( msg . update . status ) ,
"from" : msg . key . fromMe ? whatsAppNo : parsePhoneNumber ( msg . key . remoteJid ) ,
from : msg . key . fromMe ? whatsAppNo : parsePhoneNumber ( msg . key . remoteJid ) ,
"to" : msg . key . fromMe ? parsePhoneNumber ( msg . key . remoteJid ) : whatsAppNo ,
to : msg . key . fromMe ? parsePhoneNumber ( msg . key . remoteJid ) : whatsAppNo ,
participant : {
participant : {
id : parsePhoneNumber ( msg . key . participant ) ,
id : parsePhoneNumber ( msg . key . participant ) ,
name : msg . pushName
name : msg . pushName ,
} ,
} ,
"updateTime" : formatTimestamp ( new Date ( ) . getTime ( ) / 1000 ) ,
updateTime : formatTimestamp ( new Date ( ) . getTime ( ) / 1000 ) ,
} ) ;
} ) ;
}
}
} )
} ) ;
waSocket . ev . on ( 'group-participants.update' , async GroupMetadata => {
console . info ( 'group-participants.update: ' , GroupMetadata ) ;
} ) ;
// 不绑定不会影响扫码登录
// 不绑定不会影响扫码登录
store ? . bind ( waSocket . ev )
store ? . bind ( waSocket . ev ) ;
// the process function lets you process all events that just occurred
// the process function lets you process all events that just occurred
// efficiently in a batch
// efficiently in a batch
waSocket . ev . process (
waSocket . ev . process (
// events is a map for event name => event data
// events is a map for event name => event data
async ( events ) => {
async events => {
// credentials updated -- save them
// credentials updated -- save them
if ( events [ 'creds.update' ] ) {
if ( events [ 'creds.update' ] ) {
await saveCreds ( )
await saveCreds ( ) ;
}
}
} ,
} ,
)
) ;
} )
} ) ;
}
} ;
return {
return {
createTimestamp : Date . now ( ) ,
createTimestamp : Date . now ( ) ,
status : 'offline' ,
status : 'offline' ,
version : waVersion ,
version : waVersion ,
channelId : channelId ,
channelId : channelId ,
phone : phone ,
phone : phone ,
start , sendTextMessage , sendImageMessage
start ,
}
sendTextMessage ,
}
sendImageMessage ,
} ;
} ;
module . exports = {
module . exports = {
createWhatsApp ,
createWhatsApp ,