mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
feat: handle call events
This commit is contained in:
@@ -57,6 +57,7 @@ const startSock = async() => {
|
||||
await sock.sendMessage(jid, msg)
|
||||
}
|
||||
|
||||
sock.ev.on('call', item => console.log('recv call event', item))
|
||||
sock.ev.on('chats.set', item => console.log(`recv ${item.chats.length} chats (is latest: ${item.isLatest})`))
|
||||
sock.ev.on('messages.set', item => console.log(`recv ${item.messages.length} messages (is latest: ${item.isLatest})`))
|
||||
sock.ev.on('contacts.set', item => console.log(`recv ${item.contacts.length} contacts`))
|
||||
|
||||
@@ -226,6 +226,8 @@ export type BaileysEventMap = {
|
||||
|
||||
'blocklist.set': { blocklist: string[] }
|
||||
'blocklist.update': { blocklist: string[], type: 'add' | 'remove' }
|
||||
/** Receive an update on a call, including when the call was received, rejected, accepted */
|
||||
'call': WACallEvent[]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
import { proto } from '../../WAProto'
|
||||
import { KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaults'
|
||||
import { BaileysEventMap, MessageReceiptType, MessageUserReceipt, SocketConfig, WAMessageStubType } from '../Types'
|
||||
import { debouncedTimeout, decodeMessageStanza, delay, encodeBigEndian, generateSignalPubKey, getNextPreKeys, getStatusFromReceiptType, normalizeMessageContent, xmppPreKey, xmppSignedPreKey } from '../Utils'
|
||||
import { BaileysEventMap, MessageReceiptType, MessageUserReceipt, SocketConfig, WACallEvent, WAMessageStubType } from '../Types'
|
||||
import { debouncedTimeout, decodeMessageStanza, delay, encodeBigEndian, generateSignalPubKey, getCallStatusFromNode, getNextPreKeys, getStatusFromReceiptType, normalizeMessageContent, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils'
|
||||
import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex'
|
||||
import processMessage from '../Utils/process-message'
|
||||
import { areJidsSameUser, BinaryNode, BinaryNodeAttributes, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, isJidUser, jidDecode, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
||||
@@ -42,6 +42,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
)
|
||||
|
||||
const msgRetryMap = config.msgRetryCounterMap || { }
|
||||
const callOfferData: { [id: string]: WACallEvent } = { }
|
||||
|
||||
const historyCache = new Set<string>()
|
||||
|
||||
@@ -518,15 +519,43 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
})
|
||||
|
||||
ws.on('CB:call', async(node: BinaryNode) => {
|
||||
logger.info({ node }, 'recv call')
|
||||
|
||||
const [child] = getAllBinaryNodeChildren(node)
|
||||
if(!!child?.tag) {
|
||||
sendMessageAck(node, { class: 'call', type: child.tag })
|
||||
.catch(
|
||||
error => onUnexpectedError(error, 'ack call')
|
||||
)
|
||||
const { attrs } = node
|
||||
const [infoChild] = getAllBinaryNodeChildren(node)
|
||||
const callId = infoChild.attrs['call-id']
|
||||
const from = infoChild.attrs.from || infoChild.attrs['call-creator']
|
||||
const status = getCallStatusFromNode(infoChild)
|
||||
const call: WACallEvent = {
|
||||
chatId: attrs.from,
|
||||
from,
|
||||
id: callId,
|
||||
date: new Date(+attrs.t * 1000),
|
||||
offline: !!attrs.offline,
|
||||
status,
|
||||
}
|
||||
|
||||
if(status === 'offer') {
|
||||
call.isVideo = !!getBinaryNodeChild(infoChild, 'video')
|
||||
call.isGroup = infoChild.attrs.type === 'group'
|
||||
callOfferData[call.id] = call
|
||||
}
|
||||
|
||||
// use existing call info to populate this event
|
||||
if(callOfferData[call.id]) {
|
||||
call.isVideo = callOfferData[call.id].isVideo
|
||||
call.isGroup = callOfferData[call.id].isGroup
|
||||
}
|
||||
|
||||
// delete data once call has ended
|
||||
if(status === 'reject' || status === 'accept' || status === 'timeout') {
|
||||
delete callOfferData[call.id]
|
||||
}
|
||||
|
||||
ev.emit('call', [call])
|
||||
|
||||
await sendMessageAck(node, { class: 'call', type: infoChild.tag })
|
||||
.catch(
|
||||
error => onUnexpectedError(error, 'ack call')
|
||||
)
|
||||
})
|
||||
|
||||
ws.on('CB:receipt', node => {
|
||||
@@ -552,6 +581,35 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
)
|
||||
})
|
||||
|
||||
ev.on('call', ([ call ]) => {
|
||||
// missed call + group call notification message generation
|
||||
if(call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
|
||||
const msg: proto.IWebMessageInfo = {
|
||||
key: {
|
||||
remoteJid: call.chatId,
|
||||
id: call.id,
|
||||
fromMe: false
|
||||
},
|
||||
messageTimestamp: unixTimestampSeconds(call.date),
|
||||
}
|
||||
if(call.status === 'timeout') {
|
||||
if(call.isGroup) {
|
||||
msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_GROUP_VIDEO : WAMessageStubType.CALL_MISSED_GROUP_VOICE
|
||||
} else {
|
||||
msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_VIDEO : WAMessageStubType.CALL_MISSED_VOICE
|
||||
}
|
||||
} else {
|
||||
msg.message = { call: { callKey: Buffer.from(call.id) } }
|
||||
}
|
||||
|
||||
const protoMsg = proto.WebMessageInfo.fromObject(msg)
|
||||
ev.emit(
|
||||
'messages.upsert',
|
||||
{ messages: [protoMsg], type: call.offline ? 'append' : 'notify' }
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...sock,
|
||||
processMessage: processMessageLocal,
|
||||
|
||||
14
src/Types/Call.ts
Normal file
14
src/Types/Call.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
export type WACallUpdateType = 'offer' | 'ringing' | 'timeout' | 'reject' | 'accept'
|
||||
|
||||
export type WACallEvent = {
|
||||
chatId: string
|
||||
from: string
|
||||
isGroup?: boolean
|
||||
id: string
|
||||
date: Date
|
||||
isVideo?: boolean
|
||||
status: WACallUpdateType
|
||||
offline: boolean
|
||||
latencyMs?: number
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type EventEmitter from 'events'
|
||||
import { proto } from '../../WAProto'
|
||||
import { AuthenticationCreds } from './Auth'
|
||||
import { WACallEvent } from './Call'
|
||||
import { Chat, PresenceData } from './Chat'
|
||||
import { Contact } from './Contact'
|
||||
import { GroupMetadata, ParticipantAction } from './GroupMetadata'
|
||||
@@ -48,6 +49,8 @@ export type BaileysEventMap<T> = {
|
||||
|
||||
'blocklist.set': { blocklist: string[] }
|
||||
'blocklist.update': { blocklist: string[], type: 'add' | 'remove' }
|
||||
/** Receive an update on a call, including when the call was received, rejected, accepted */
|
||||
'call': WACallEvent[]
|
||||
}
|
||||
|
||||
export interface CommonBaileysEventEmitter<Creds> extends EventEmitter {
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './Legacy'
|
||||
export * from './Socket'
|
||||
export * from './Events'
|
||||
export * from './Product'
|
||||
export * from './Call'
|
||||
|
||||
import type NodeCache from 'node-cache'
|
||||
import { proto } from '../../WAProto'
|
||||
|
||||
@@ -5,7 +5,7 @@ import { platform, release } from 'os'
|
||||
import { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { version as baileysVersion } from '../Defaults/baileys-version.json'
|
||||
import { CommonBaileysEventEmitter, ConnectionState, DisconnectReason, WAVersion } from '../Types'
|
||||
import { CommonBaileysEventEmitter, ConnectionState, DisconnectReason, WACallUpdateType, WAVersion } from '../Types'
|
||||
import { BinaryNode, getAllBinaryNodeChildren } from '../WABinary'
|
||||
|
||||
const PLATFORM_MAP = {
|
||||
@@ -287,4 +287,33 @@ export const getErrorCodeFromStreamError = (node: BinaryNode) => {
|
||||
reason,
|
||||
statusCode
|
||||
}
|
||||
}
|
||||
|
||||
export const getCallStatusFromNode = ({ tag, attrs }: BinaryNode) => {
|
||||
let status: WACallUpdateType
|
||||
switch (tag) {
|
||||
case 'offer':
|
||||
case 'offer_notice':
|
||||
status = 'offer'
|
||||
break
|
||||
case 'terminate':
|
||||
if(attrs.reason === 'timeout') {
|
||||
status = 'timeout'
|
||||
} else {
|
||||
status = 'reject'
|
||||
}
|
||||
|
||||
break
|
||||
case 'reject':
|
||||
status = 'reject'
|
||||
break
|
||||
case 'accept':
|
||||
status = 'accept'
|
||||
break
|
||||
default:
|
||||
status = 'ringing'
|
||||
break
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
Reference in New Issue
Block a user