diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index a14af4e..5d73086 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -4,7 +4,7 @@ import NodeCache from 'node-cache' import { proto } from '../../WAProto' import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults' import { AnyMessageContent, MediaConnInfo, MessageReceiptType, MessageRelayOptions, MiscMessageGenerationOptions, SocketConfig, WAMessageKey } from '../Types' -import { aggregateMessageKeysNotFromMe, assertMediaContent, bindWaitForEvent, decryptMediaRetryData, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils' +import { aggregateMessageKeysNotFromMe, assertMediaContent, bindWaitForEvent, decryptMediaRetryData, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils' import { getUrlInfo } from '../Utils/link-preview' import { areJidsSameUser, BinaryNode, BinaryNodeAttributes, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, isJidUser, jidDecode, jidEncode, jidNormalizedUser, JidWithDevice, S_WHATSAPP_NET } from '../WABinary' import { makeGroupsSocket } from './groups' @@ -362,6 +362,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { } } + const extraAttrs = {} + if(participant) { // when the retry request is not for a group // only send to the specific device that asked for a retry @@ -377,6 +379,14 @@ export const makeMessagesSocket = (config: SocketConfig) => { await authState.keys.transaction( async() => { const mediaType = getMediaType(message) + if(mediaType) { + extraAttrs['mediatype'] = mediaType + } + + if(normalizeMessageContent(message)?.pinInChatMessage) { + extraAttrs['decrypt-fail'] = 'hide' + } + if(isGroup || isStatus) { const [groupData, senderKeyMap] = await Promise.all([ (async() => { @@ -445,7 +455,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { await assertSessions(senderKeyJids, false) - const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, mediaType ? { mediatype: mediaType } : undefined) + const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs) shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity participants.push(...result.nodes) @@ -496,8 +506,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { { nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 } ] = await Promise.all([ - createParticipantNodes(meJids, meMsg, mediaType ? { mediatype: mediaType } : undefined), - createParticipantNodes(otherJids, message, mediaType ? { mediatype: mediaType } : undefined) + createParticipantNodes(meJids, meMsg, extraAttrs), + createParticipantNodes(otherJids, message, extraAttrs) ]) participants.push(...meNodes) participants.push(...otherNodes) @@ -674,7 +684,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { } content.directPath = media.directPath - content.url = getUrlFromDirectPath(content.directPath!) + content.url = getUrlFromDirectPath(content.directPath) logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful') } catch(err) { @@ -745,6 +755,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { ) const isDeleteMsg = 'delete' in content && !!content.delete const isEditMsg = 'edit' in content && !!content.edit + const isPinMsg = 'pin' in content && !!content.pin const additionalAttributes: BinaryNodeAttributes = { } // required for delete if(isDeleteMsg) { @@ -756,6 +767,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { } } else if(isEditMsg) { additionalAttributes.edit = '1' + } else if(isPinMsg) { + additionalAttributes.edit = '2' } if('cachedGroupMetadata' in options) { diff --git a/src/Types/Message.ts b/src/Types/Message.ts index 4e7ad99..8c9a8f5 100644 --- a/src/Types/Message.ts +++ b/src/Types/Message.ts @@ -156,6 +156,14 @@ export type AnyRegularMessageContent = ( | { listReply: Omit } + | { + pin: WAMessageKey + type: proto.PinInChat.Type + /** + * 24 hours, 7 days, 30 days + */ + time?: 86400 | 604800 | 2592000 + } | { product: WASendableProduct businessOwnerJid?: string diff --git a/src/Utils/messages.ts b/src/Utils/messages.ts index 111ddd4..82adbef 100644 --- a/src/Utils/messages.ts +++ b/src/Utils/messages.ts @@ -124,7 +124,7 @@ export const prepareWAMessageMedia = async( !!uploadData.media.url && !!options.mediaCache && ( // generate the key - mediaType + ':' + uploadData.media.url!.toString() + mediaType + ':' + uploadData.media.url.toString() ) if(mediaType === 'document' && !uploadData.fileName) { @@ -394,6 +394,15 @@ export const generateWAMessageContent = async( (message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0) : message.disappearingMessagesInChat m = prepareDisappearingMessageSettingContent(exp) + } else if('pin' in message) { + m.pinInChatMessage = {} + m.messageContextInfo = {} + + m.pinInChatMessage.key = message.pin + m.pinInChatMessage.type = message.type + m.pinInChatMessage.senderTimestampMs = Date.now() + + m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0 } else if('buttonReply' in message) { switch (message.type) { case 'template': @@ -678,7 +687,7 @@ export const extractMessageContent = (content: WAMessageContent | undefined | nu content = normalizeMessageContent(content) if(content?.buttonsMessage) { - return extractFromTemplateMessage(content.buttonsMessage!) + return extractFromTemplateMessage(content.buttonsMessage) } if(content?.templateMessage?.hydratedFourRowTemplate) {