feat: eventMessage sending / call link creation (#1677)

This commit is contained in:
vini
2025-09-07 08:08:18 -03:00
committed by GitHub
parent 4d4339dae6
commit 084761dc31
5 changed files with 76 additions and 1 deletions
+2
View File
@@ -10,6 +10,8 @@ const { version } = defaultVersion
export const UNAUTHORIZED_CODES = [401, 403, 419]
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
export const CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/'
export const CALL_AUDIO_PREFIX = 'https://call.whatsapp.com/voice/'
export const DEF_CALLBACK_PREFIX = 'CB:'
export const DEF_TAG_PREFIX = 'TAG:'
export const PHONE_CONNECTION_CB = 'CB:Pong'
+23
View File
@@ -633,6 +633,28 @@ export const makeChatsSocket = (config: SocketConfig) => {
return child?.attrs?.url
}
const createCallLink = async (type: 'audio' | 'video', event?: { startTime: number }, timeoutMs?: number) => {
const result = await query(
{
tag: 'call',
attrs: {
id: generateMessageTag(),
to: '@call'
},
content: [
{
tag: 'link_create',
attrs: { media: type },
content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
}
]
},
timeoutMs
)
const child = getBinaryNodeChild(result, 'link_create')
return child?.attrs?.token
}
const sendPresenceUpdate = async (type: WAPresence, toJid?: string) => {
const me = authState.creds.me!
if (type === 'available' || type === 'unavailable') {
@@ -1139,6 +1161,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
return {
...sock,
createCallLink,
getBotListV2,
processingMutex,
fetchPrivacySettings,
+13
View File
@@ -655,6 +655,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
return 'poll'
}
if (message.eventMessage) {
return 'event'
}
return 'text'
}
@@ -817,12 +821,14 @@ export const makeMessagesSocket = (config: SocketConfig) => {
}),
//TODO: CACHE
getProfilePicUrl: sock.profilePictureUrl,
getCallLink: sock.createCallLink,
upload: waUploadToServer,
mediaCache: config.mediaCache,
options: config.options,
messageId: generateMessageIDV2(sock.user?.id),
...options
})
const isEventMsg = 'event' in content && !!content.event
const isDeleteMsg = 'delete' in content && !!content.delete
const isEditMsg = 'edit' in content && !!content.edit
const isPinMsg = 'pin' in content && !!content.pin
@@ -848,6 +854,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
polltype: 'creation'
}
} as BinaryNode)
} else if (isEventMsg) {
additionalNodes.push({
tag: 'meta',
attrs: {
event_type: 'creation'
}
} as BinaryNode)
}
if ('cachedGroupMetadata' in options) {
+15
View File
@@ -131,6 +131,19 @@ export type PollMessageOptions = {
toAnnouncementGroup?: boolean
}
export type EventMessageOptions = {
name: string
description?: string
startDate: Date
endDate?: Date
location?: WALocationMessage
call?: 'audio' | 'video'
isCancelled?: boolean
isScheduleCall?: boolean
extraGuestsAllowed?: boolean
messageSecret?: Uint8Array<ArrayBufferLike>
}
type SharePhoneNumber = {
sharePhoneNumber: boolean
}
@@ -203,6 +216,7 @@ export type AnyRegularMessageContent = (
Contextable &
Editable)
| AnyMediaMessageContent
| { event: EventMessageOptions }
| ({
poll: PollMessageOptions
} & Mentionable &
@@ -327,6 +341,7 @@ export type MediaGenerationOptions = {
export type MessageContentGenerationOptions = MediaGenerationOptions & {
getUrlInfo?: (text: string) => Promise<WAUrlInfo | undefined>
getProfilePicUrl?: (jid: string, type: 'image' | 'preview') => Promise<string | undefined>
getCallLink?: (type: 'audio' | 'video', event?: { startTime: number }) => Promise<string | undefined>
jid?: string
}
export type MessageGenerationOptions = MessageContentGenerationOptions & MessageGenerationOptionsFromContent
+23 -1
View File
@@ -4,7 +4,7 @@ import { randomBytes } from 'crypto'
import { promises as fs } from 'fs'
import { type Transform } from 'stream'
import { proto } from '../../WAProto/index.js'
import { MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults'
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults'
import type {
AnyMediaMessageContent,
AnyMessageContent,
@@ -490,6 +490,28 @@ export const generateWAMessageContent = async (
})
} else if ('listReply' in message) {
m.listResponseMessage = { ...message.listReply }
} else if ('event' in message) {
m.eventMessage = {}
const startTime = Math.floor(message.event.startDate.getTime() / 1000)
if (message.event.call && options.getCallLink) {
const token = await options.getCallLink(message.event.call, { startTime })
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token
}
m.messageContextInfo = {
// encKey
messageSecret: message.event.messageSecret || randomBytes(32)
}
m.eventMessage.name = message.event.name
m.eventMessage.description = message.event.description
m.eventMessage.startTime = startTime
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined
m.eventMessage.isCanceled = message.event.isCancelled ?? false
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false
m.eventMessage.location = message.event.location
} else if ('poll' in message) {
message.poll.selectableCount ||= 0
message.poll.toAnnouncementGroup ||= false