feat: eventMessage sending / call link creation (#1677)
This commit is contained in:
@@ -10,6 +10,8 @@ const { version } = defaultVersion
|
|||||||
export const UNAUTHORIZED_CODES = [401, 403, 419]
|
export const UNAUTHORIZED_CODES = [401, 403, 419]
|
||||||
|
|
||||||
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com'
|
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_CALLBACK_PREFIX = 'CB:'
|
||||||
export const DEF_TAG_PREFIX = 'TAG:'
|
export const DEF_TAG_PREFIX = 'TAG:'
|
||||||
export const PHONE_CONNECTION_CB = 'CB:Pong'
|
export const PHONE_CONNECTION_CB = 'CB:Pong'
|
||||||
|
|||||||
@@ -633,6 +633,28 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
return child?.attrs?.url
|
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 sendPresenceUpdate = async (type: WAPresence, toJid?: string) => {
|
||||||
const me = authState.creds.me!
|
const me = authState.creds.me!
|
||||||
if (type === 'available' || type === 'unavailable') {
|
if (type === 'available' || type === 'unavailable') {
|
||||||
@@ -1139,6 +1161,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...sock,
|
...sock,
|
||||||
|
createCallLink,
|
||||||
getBotListV2,
|
getBotListV2,
|
||||||
processingMutex,
|
processingMutex,
|
||||||
fetchPrivacySettings,
|
fetchPrivacySettings,
|
||||||
|
|||||||
@@ -655,6 +655,10 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
return 'poll'
|
return 'poll'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.eventMessage) {
|
||||||
|
return 'event'
|
||||||
|
}
|
||||||
|
|
||||||
return 'text'
|
return 'text'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,12 +821,14 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
}),
|
}),
|
||||||
//TODO: CACHE
|
//TODO: CACHE
|
||||||
getProfilePicUrl: sock.profilePictureUrl,
|
getProfilePicUrl: sock.profilePictureUrl,
|
||||||
|
getCallLink: sock.createCallLink,
|
||||||
upload: waUploadToServer,
|
upload: waUploadToServer,
|
||||||
mediaCache: config.mediaCache,
|
mediaCache: config.mediaCache,
|
||||||
options: config.options,
|
options: config.options,
|
||||||
messageId: generateMessageIDV2(sock.user?.id),
|
messageId: generateMessageIDV2(sock.user?.id),
|
||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
|
const isEventMsg = 'event' in content && !!content.event
|
||||||
const isDeleteMsg = 'delete' in content && !!content.delete
|
const isDeleteMsg = 'delete' in content && !!content.delete
|
||||||
const isEditMsg = 'edit' in content && !!content.edit
|
const isEditMsg = 'edit' in content && !!content.edit
|
||||||
const isPinMsg = 'pin' in content && !!content.pin
|
const isPinMsg = 'pin' in content && !!content.pin
|
||||||
@@ -848,6 +854,13 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
|||||||
polltype: 'creation'
|
polltype: 'creation'
|
||||||
}
|
}
|
||||||
} as BinaryNode)
|
} as BinaryNode)
|
||||||
|
} else if (isEventMsg) {
|
||||||
|
additionalNodes.push({
|
||||||
|
tag: 'meta',
|
||||||
|
attrs: {
|
||||||
|
event_type: 'creation'
|
||||||
|
}
|
||||||
|
} as BinaryNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('cachedGroupMetadata' in options) {
|
if ('cachedGroupMetadata' in options) {
|
||||||
|
|||||||
@@ -131,6 +131,19 @@ export type PollMessageOptions = {
|
|||||||
toAnnouncementGroup?: boolean
|
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 = {
|
type SharePhoneNumber = {
|
||||||
sharePhoneNumber: boolean
|
sharePhoneNumber: boolean
|
||||||
}
|
}
|
||||||
@@ -203,6 +216,7 @@ export type AnyRegularMessageContent = (
|
|||||||
Contextable &
|
Contextable &
|
||||||
Editable)
|
Editable)
|
||||||
| AnyMediaMessageContent
|
| AnyMediaMessageContent
|
||||||
|
| { event: EventMessageOptions }
|
||||||
| ({
|
| ({
|
||||||
poll: PollMessageOptions
|
poll: PollMessageOptions
|
||||||
} & Mentionable &
|
} & Mentionable &
|
||||||
@@ -327,6 +341,7 @@ export type MediaGenerationOptions = {
|
|||||||
export type MessageContentGenerationOptions = MediaGenerationOptions & {
|
export type MessageContentGenerationOptions = MediaGenerationOptions & {
|
||||||
getUrlInfo?: (text: string) => Promise<WAUrlInfo | undefined>
|
getUrlInfo?: (text: string) => Promise<WAUrlInfo | undefined>
|
||||||
getProfilePicUrl?: (jid: string, type: 'image' | 'preview') => Promise<string | undefined>
|
getProfilePicUrl?: (jid: string, type: 'image' | 'preview') => Promise<string | undefined>
|
||||||
|
getCallLink?: (type: 'audio' | 'video', event?: { startTime: number }) => Promise<string | undefined>
|
||||||
jid?: string
|
jid?: string
|
||||||
}
|
}
|
||||||
export type MessageGenerationOptions = MessageContentGenerationOptions & MessageGenerationOptionsFromContent
|
export type MessageGenerationOptions = MessageContentGenerationOptions & MessageGenerationOptionsFromContent
|
||||||
|
|||||||
+23
-1
@@ -4,7 +4,7 @@ import { randomBytes } from 'crypto'
|
|||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import { type Transform } from 'stream'
|
import { type Transform } from 'stream'
|
||||||
import { proto } from '../../WAProto/index.js'
|
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 {
|
import type {
|
||||||
AnyMediaMessageContent,
|
AnyMediaMessageContent,
|
||||||
AnyMessageContent,
|
AnyMessageContent,
|
||||||
@@ -490,6 +490,28 @@ export const generateWAMessageContent = async (
|
|||||||
})
|
})
|
||||||
} else if ('listReply' in message) {
|
} else if ('listReply' in message) {
|
||||||
m.listResponseMessage = { ...message.listReply }
|
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) {
|
} else if ('poll' in message) {
|
||||||
message.poll.selectableCount ||= 0
|
message.poll.selectableCount ||= 0
|
||||||
message.poll.toAnnouncementGroup ||= false
|
message.poll.toAnnouncementGroup ||= false
|
||||||
|
|||||||
Reference in New Issue
Block a user