kage-baileys 3.0.6 → 3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Example/connection.cjs +4 -0
- package/README.MD +4726 -48
- package/WAProto/WAProto.proto +3395 -415
- package/WAProto/compat.cjs +12 -0
- package/WAProto/index.d.ts +47104 -19888
- package/WAProto/index.js +131979 -57361
- package/lib/Addon/http-error.js +43 -0
- package/lib/Addon/index.js +32 -0
- package/lib/Addon/keyed-db.js +250 -0
- package/lib/Addon/link-preview.js +82 -0
- package/lib/Defaults/baileys-version.json +3 -0
- package/lib/Defaults/connection.js +110 -0
- package/lib/Defaults/constants.js +156 -0
- package/lib/Defaults/history.js +46 -0
- package/lib/Defaults/index.js +230 -0
- package/lib/Defaults/media.js +104 -0
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Defaults/prefix.js +44 -0
- package/lib/Extensions/business-advanced.js +382 -0
- package/lib/Extensions/business.js +159 -0
- package/lib/Extensions/chats-advanced.js +1106 -0
- package/lib/Extensions/chats.js +349 -0
- package/lib/Extensions/communities-advanced.js +430 -0
- package/lib/Extensions/community.js +94 -0
- package/lib/Extensions/groups-advanced.js +343 -0
- package/lib/Extensions/index.js +30 -0
- package/lib/Extensions/messages-recv-advanced.js +1690 -0
- package/lib/Extensions/messages-send-advanced.js +622 -0
- package/lib/Extensions/messages-send.js +1380 -0
- package/lib/Extensions/newsletter-advanced.js +228 -0
- package/lib/Extensions/newsletter.js +72 -0
- package/lib/Extensions/socket.js +389 -0
- package/lib/Modded/message_builder.js +2084 -4
- package/lib/Signal/Group/ciphertext-message.js +54 -0
- package/lib/Signal/Group/group-session-builder.js +124 -0
- package/lib/Signal/Group/group_cipher.js +216 -0
- package/lib/Signal/Group/index.js +124 -0
- package/lib/Signal/Group/keyhelper.js +108 -0
- package/lib/Signal/Group/sender-chain-key.js +83 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +157 -0
- package/lib/Signal/Group/sender-key-message.js +166 -0
- package/lib/Signal/Group/sender-key-name.js +130 -0
- package/lib/Signal/Group/sender-key-record.js +113 -0
- package/lib/Signal/Group/sender-key-state.js +208 -0
- package/lib/Signal/Group/sender-message-key.js +82 -0
- package/lib/Signal/libsignal.js +935 -0
- package/lib/Signal/lid-mapping.js +545 -1
- package/lib/Socket/Client/index.js +60 -0
- package/lib/Socket/Client/types.js +51 -0
- package/lib/Socket/Client/websocket.js +152 -0
- package/lib/Socket/business.js +831 -0
- package/lib/Socket/chats.js +2535 -49
- package/lib/Socket/communities.js +450 -1
- package/lib/Socket/community.js +1022 -0
- package/lib/Socket/dugong.js +1 -1
- package/lib/Socket/groups.js +747 -0
- package/lib/Socket/index.js +60 -2
- package/lib/Socket/messages-recv.js +3450 -5
- package/lib/Socket/messages-send.js +3123 -0
- package/lib/Socket/mex.js +126 -1
- package/lib/Socket/newsletter.js +465 -0
- package/lib/Socket/socket.js +2217 -1
- package/lib/Store/index.js +79 -0
- package/lib/Store/make-cache-manager-store.js +186 -0
- package/lib/Store/make-in-memory-store.js +878 -0
- package/lib/Store/make-ordered-dictionary.js +187 -0
- package/lib/Store/object-repository.js +78 -0
- package/lib/Types/Auth.js +25 -0
- package/lib/Types/Bussines.js +25 -1
- package/lib/Types/Call.js +25 -0
- package/lib/Types/Chat.js +39 -0
- package/lib/Types/Contact.js +25 -0
- package/lib/Types/Events.js +25 -0
- package/lib/Types/GroupMetadata.js +25 -0
- package/lib/Types/Label.js +76 -0
- package/lib/Types/LabelAssociation.js +40 -0
- package/lib/Types/Message.js +57 -0
- package/lib/Types/Mex.js +56 -1
- package/lib/Types/MexUpdates.js +46 -0
- package/lib/Types/Newsletter.js +56 -0
- package/lib/Types/Product.js +25 -0
- package/lib/Types/RichType.js +23 -0
- package/lib/Types/Signal.js +25 -0
- package/lib/Types/Socket.js +25 -0
- package/lib/Types/State.js +92 -0
- package/lib/Types/USync.js +25 -0
- package/lib/Types/index.js +133 -0
- package/lib/Utils/auth-utils.js +703 -0
- package/lib/Utils/automation-tools.js +51 -16
- package/lib/Utils/baileys-event-stream.js +146 -0
- package/lib/Utils/browser-utils.js +95 -0
- package/lib/Utils/business.js +525 -0
- package/lib/Utils/chat-utils.js +1861 -0
- package/lib/Utils/companion-reg-client-utils.js +55 -1
- package/lib/Utils/crypto.js +355 -0
- package/lib/Utils/decode-wa-message.js +709 -0
- package/lib/Utils/event-buffer.js +1323 -8
- package/lib/Utils/generics.js +945 -1
- package/lib/Utils/history.js +266 -0
- package/lib/Utils/identity-change-handler.js +62 -1
- package/lib/Utils/index.js +114 -0
- package/lib/Utils/link-preview.js +179 -0
- package/lib/Utils/logger.js +42 -0
- package/lib/Utils/lt-hash.js +73 -0
- package/lib/Utils/make-mutex.js +104 -0
- package/lib/Utils/message-inspector.js +110 -0
- package/lib/Utils/message-retry-manager.js +507 -2
- package/lib/Utils/messages-media.js +1931 -1
- package/lib/Utils/messages.js +3748 -0
- package/lib/Utils/noise-handler.js +423 -0
- package/lib/Utils/offline-node-processor.js +53 -1
- package/lib/Utils/pre-key-manager.js +255 -1
- package/lib/Utils/process-message.js +1592 -0
- package/lib/Utils/reporting-utils.js +272 -1
- package/lib/Utils/rich-message-utils.js +377 -0
- package/lib/Utils/rich-messages.js +1 -1
- package/lib/Utils/signal.js +437 -0
- package/lib/Utils/stanza-ack.js +51 -1
- package/lib/Utils/status-refresh.js +37 -0
- package/lib/Utils/status-tools.js +4 -2
- package/lib/Utils/sync-action-utils.js +63 -1
- package/lib/Utils/tc-token-utils.js +185 -1
- package/lib/Utils/terminal-banner.js +31 -0
- package/lib/Utils/use-multi-file-auth-state.js +271 -0
- package/lib/Utils/use-single-file-auth-state.js +108 -0
- package/lib/Utils/use-sqlite-auth-state.js +142 -0
- package/lib/Utils/validate-connection.js +497 -1
- package/lib/Utils/view-once-compat.js +13 -5
- package/lib/VoIP/audio-feeder.js +150 -0
- package/lib/VoIP/index.js +289 -0
- package/lib/VoIP/relay-transport.js +608 -0
- package/lib/VoIP/signaling.js +583 -0
- package/lib/VoIP/types.js +30 -0
- package/lib/VoIP/wasm-engine.js +1261 -2
- package/lib/VoIP/worker-bootstrap.js +1090 -2
- package/lib/WABinary/constants.js +2805 -0
- package/lib/WABinary/decode.js +649 -2
- package/lib/WABinary/encode.js +564 -2
- package/lib/WABinary/generic-utils.js +401 -0
- package/lib/WABinary/index.js +82 -0
- package/lib/WABinary/jid-utils.js +290 -0
- package/lib/WABinary/types.js +25 -0
- package/lib/WAM/BinaryInfo.js +50 -0
- package/lib/WAM/constants.js +45747 -0
- package/lib/WAM/encode.js +393 -0
- package/lib/WAM/index.js +78 -0
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +135 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +110 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +142 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +85 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +69 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +107 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +38 -1
- package/lib/WAUSync/Protocols/index.js +83 -0
- package/lib/WAUSync/USyncQuery.js +241 -2
- package/lib/WAUSync/USyncUser.js +86 -0
- package/lib/WAUSync/index.js +78 -0
- package/lib/assets/wasm/loader.js +1875 -1
- package/lib/assets/wasm/worker-modules.js +11047 -1
- package/lib/index.d.ts +126 -0
- package/lib/index.js +53 -21
- package/lib/index.mjs +437 -0
- package/licenses/upstream-MIT.txt +22 -0
- package/maintenance/api-coverage.json +1642 -0
- package/maintenance/archivos-sha256.json +207 -0
- package/maintenance/consumer-validation.log +3 -0
- package/maintenance/dependency-audit.json +4 -4
- package/maintenance/extension-inventory.json +57 -0
- package/maintenance/protocol-update.json +1101 -0
- package/maintenance/protocol-wire-audit.json +4 -0
- package/maintenance/reference-socket-surface.json +209 -0
- package/maintenance/resumen-validacion.json +50 -0
- package/maintenance/validation.log +2725 -0
- package/maintenance/verification.json +566 -0
- package/package.json +107 -9
- package/scripts/check-imports.cjs +10 -0
- package/scripts/check-package.cjs +40 -0
- package/scripts/check-syntax.cjs +1 -1
- package/scripts/generate-protobuf.cjs +6 -1
- package/scripts/generate-public-types.cjs +9 -0
- package/scripts/kage-base64-fix.cjs +45 -0
- package/scripts/maintenance.cjs +10 -0
- package/test/advanced.test.cjs +24 -0
- package/test/base64-fix.test.cjs +49 -0
- package/test/maintenance.test.cjs +1 -1
- package/test/npm-publication.test.cjs +141 -0
- package/test/recepcion-reparada.test.cjs +98 -0
- package/test/resilience-binary.test.cjs +69 -0
- package/test/resilience-transport.test.cjs +34 -0
- package/test/types.test.ts +5 -0
- package/test/upgrade.test.cjs +104 -0
- package/tsconfig.json +20 -0
- package/WAProto/p.html +0 -1
- package/lib/KageExtra/Modded/message_builder.js +0 -2330
- package/lib/KageExtra/VoIP/audio-feeder.js +0 -136
- package/lib/KageExtra/VoIP/index.js +0 -282
- package/lib/KageExtra/VoIP/relay-transport.js +0 -559
- package/lib/KageExtra/VoIP/signaling.js +0 -603
- package/lib/KageExtra/VoIP/types.js +0 -17
- package/lib/KageExtra/VoIP/wasm-engine.js +0 -1214
- package/lib/KageExtra/VoIP/worker-bootstrap.js +0 -1042
- package/lib/KageExtra/assets/wasm/loader.js +0 -5
- package/lib/KageExtra/assets/wasm/whatsapp.wasm +0 -0
- package/lib/KageExtra/assets/wasm/worker-modules.js +0 -273
- package/lib/KageExtra/index.js +0 -3
- package/lib/KageExtra/package.json +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -53
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -24
- package/maintenance/revision-full-check.txt +0 -1097
- package/maintenance/revision-library-tests.tap +0 -1088
- package/maintenance/revision-protobuf.txt +0 -5
- package/maintenance/revision-syntax.txt +0 -5
- package/maintenance/schema-coverage.json +0 -23
- package/maintenance/schema-merge.json +0 -30
- package/maintenance/validation-node20.txt +0 -191
- package/maintenance/validation-node22.txt +0 -237
package/lib/Utils/messages.js
CHANGED
|
@@ -1047,3 +1047,3751 @@ const getSenderLid = (message) => {
|
|
|
1047
1047
|
return { jid: sender, lid };
|
|
1048
1048
|
};
|
|
1049
1049
|
exports.getSenderLid = getSenderLid;
|
|
1050
|
+
|
|
1051
|
+
// Kage: compatibilidad de API.
|
|
1052
|
+
const __kageModule74 = (() => {
|
|
1053
|
+
const module = { exports: {} }; const exports = module.exports;
|
|
1054
|
+
"use strict"
|
|
1055
|
+
|
|
1056
|
+
Object.defineProperty(exports, "__esModule", { value: true })
|
|
1057
|
+
|
|
1058
|
+
const { Boom } = require("@hapi/boom")
|
|
1059
|
+
const { randomBytes } = require("crypto")
|
|
1060
|
+
const { promises } = require("fs")
|
|
1061
|
+
const { proto } = require("../../WAProto")
|
|
1062
|
+
const {
|
|
1063
|
+
URL_REGEX,
|
|
1064
|
+
WA_DEFAULT_EPHEMERAL
|
|
1065
|
+
} = require("../Defaults/constants")
|
|
1066
|
+
const { MEDIA_KEYS } = require("../Defaults/media")
|
|
1067
|
+
const {
|
|
1068
|
+
WAProto,
|
|
1069
|
+
WAMessageStatus
|
|
1070
|
+
} = require("../Types")
|
|
1071
|
+
const {
|
|
1072
|
+
isJidGroup,
|
|
1073
|
+
isJidNewsletter,
|
|
1074
|
+
isJidStatusBroadcast,
|
|
1075
|
+
jidNormalizedUser
|
|
1076
|
+
} = require("../WABinary")
|
|
1077
|
+
const { sha256 } = require("./crypto")
|
|
1078
|
+
const {
|
|
1079
|
+
generateMessageID,
|
|
1080
|
+
getKeyAuthor,
|
|
1081
|
+
unixTimestampSeconds
|
|
1082
|
+
} = require("./generics")
|
|
1083
|
+
const {
|
|
1084
|
+
downloadContentFromMessage,
|
|
1085
|
+
encryptedStream,
|
|
1086
|
+
generateThumbnail,
|
|
1087
|
+
getAudioDuration,
|
|
1088
|
+
getAudioWaveform,
|
|
1089
|
+
getRawMediaUploadData,
|
|
1090
|
+
getStream,
|
|
1091
|
+
toBuffer
|
|
1092
|
+
} = require("./messages-media")
|
|
1093
|
+
|
|
1094
|
+
const MIMETYPE_MAP = {
|
|
1095
|
+
image: 'image/jpeg',
|
|
1096
|
+
video: 'video/mp4',
|
|
1097
|
+
document: 'application/pdf',
|
|
1098
|
+
audio: 'audio/ogg codecs=opus',
|
|
1099
|
+
sticker: 'image/webp',
|
|
1100
|
+
'product-catalog-image': 'image/jpeg'
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const MessageTypeProto = {
|
|
1104
|
+
'image': WAProto.Message.ImageMessage,
|
|
1105
|
+
'video': WAProto.Message.VideoMessage,
|
|
1106
|
+
'audio': WAProto.Message.AudioMessage,
|
|
1107
|
+
'sticker': WAProto.Message.StickerMessage,
|
|
1108
|
+
'document': WAProto.Message.DocumentMessage
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
1113
|
+
* @param text eg. hello https://google.com
|
|
1114
|
+
* @returns the URL, eg. https://google.com
|
|
1115
|
+
*/
|
|
1116
|
+
const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0]
|
|
1117
|
+
|
|
1118
|
+
const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
1119
|
+
const url = extractUrlFromText(text)
|
|
1120
|
+
|
|
1121
|
+
if (!!getUrlInfo && url) {
|
|
1122
|
+
try {
|
|
1123
|
+
const urlInfo = await getUrlInfo(url)
|
|
1124
|
+
return urlInfo
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
catch (error) {
|
|
1128
|
+
logger?.warn({ trace: error.stack }, 'url generation failed')
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const assertColor = async (color) => {
|
|
1134
|
+
let assertedColor
|
|
1135
|
+
|
|
1136
|
+
if (typeof color === 'number') {
|
|
1137
|
+
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
else {
|
|
1141
|
+
let hex = color.trim().replace('#', '')
|
|
1142
|
+
if (hex.length <= 6) {
|
|
1143
|
+
hex = 'FF' + hex.padStart(6, '0')
|
|
1144
|
+
}
|
|
1145
|
+
assertedColor = parseInt(hex, 16)
|
|
1146
|
+
return assertedColor
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
const prepareWAMessageMedia = async (message, options) => {
|
|
1151
|
+
const logger = options.logger
|
|
1152
|
+
|
|
1153
|
+
let mediaType
|
|
1154
|
+
|
|
1155
|
+
for (const key of MEDIA_KEYS) {
|
|
1156
|
+
if (key in message) {
|
|
1157
|
+
mediaType = key
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (!mediaType) {
|
|
1162
|
+
throw new Boom('Invalid media type', { statusCode: 400 })
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
const uploadData = {
|
|
1166
|
+
...message,
|
|
1167
|
+
media: message[mediaType]
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
delete uploadData[mediaType]
|
|
1171
|
+
|
|
1172
|
+
// check if cacheable + generate cache key
|
|
1173
|
+
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
1174
|
+
'url' in uploadData.media &&
|
|
1175
|
+
!!uploadData.media.url &&
|
|
1176
|
+
!!options.mediaCache &&
|
|
1177
|
+
mediaType + ':' + uploadData.media.url.toString()
|
|
1178
|
+
|
|
1179
|
+
if (mediaType === 'document' && !uploadData.fileName) {
|
|
1180
|
+
uploadData.fileName = 'file'
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if (!uploadData.mimetype) {
|
|
1184
|
+
uploadData.mimetype = MIMETYPE_MAP[mediaType]
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (cacheableKey) {
|
|
1188
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey)
|
|
1189
|
+
|
|
1190
|
+
if (mediaBuff) {
|
|
1191
|
+
logger?.debug({ cacheableKey }, 'got media cache hit')
|
|
1192
|
+
|
|
1193
|
+
const obj = proto.Message.decode(mediaBuff)
|
|
1194
|
+
const key = `${mediaType}Message`
|
|
1195
|
+
|
|
1196
|
+
Object.assign(obj[key], { ...uploadData, media: undefined })
|
|
1197
|
+
|
|
1198
|
+
return obj
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const isNewsletter = !!options.jid && isJidNewsletter(options.jid)
|
|
1203
|
+
|
|
1204
|
+
if (isNewsletter) {
|
|
1205
|
+
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter')
|
|
1206
|
+
const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger)
|
|
1207
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
1208
|
+
const { mediaUrl, directPath } = await options.upload(filePath, {
|
|
1209
|
+
fileEncSha256B64: fileSha256B64,
|
|
1210
|
+
mediaType: mediaType,
|
|
1211
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
1212
|
+
})
|
|
1213
|
+
|
|
1214
|
+
await promises.unlink(filePath)
|
|
1215
|
+
|
|
1216
|
+
const obj = WAProto.Message.fromObject({
|
|
1217
|
+
// todo: add more support here
|
|
1218
|
+
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
1219
|
+
url: mediaUrl,
|
|
1220
|
+
directPath,
|
|
1221
|
+
fileSha256,
|
|
1222
|
+
fileLength,
|
|
1223
|
+
...uploadData,
|
|
1224
|
+
media: undefined
|
|
1225
|
+
})
|
|
1226
|
+
})
|
|
1227
|
+
|
|
1228
|
+
if (uploadData.ptv) {
|
|
1229
|
+
obj.ptvMessage = obj.videoMessage
|
|
1230
|
+
delete obj.videoMessage
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (obj.stickerMessage) {
|
|
1234
|
+
obj.stickerMessage.stickerSentTs = Date.now()
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (cacheableKey) {
|
|
1238
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
1239
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
return obj
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
|
|
1246
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
|
|
1247
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
|
|
1248
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
|
|
1249
|
+
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
|
1250
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
1251
|
+
logger,
|
|
1252
|
+
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
1253
|
+
opts: options.options
|
|
1254
|
+
})
|
|
1255
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64')
|
|
1256
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
1257
|
+
(async () => {
|
|
1258
|
+
const result = await options.upload(encFilePath, {
|
|
1259
|
+
fileEncSha256B64,
|
|
1260
|
+
mediaType,
|
|
1261
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
1262
|
+
})
|
|
1263
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
|
|
1264
|
+
return result
|
|
1265
|
+
})(),
|
|
1266
|
+
(async () => {
|
|
1267
|
+
try {
|
|
1268
|
+
if (requiresThumbnailComputation) {
|
|
1269
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options)
|
|
1270
|
+
|
|
1271
|
+
uploadData.jpegThumbnail = thumbnail
|
|
1272
|
+
|
|
1273
|
+
if (!uploadData.width && originalImageDimensions) {
|
|
1274
|
+
uploadData.width = originalImageDimensions.width
|
|
1275
|
+
uploadData.height = originalImageDimensions.height
|
|
1276
|
+
logger?.debug('set dimensions')
|
|
1277
|
+
}
|
|
1278
|
+
logger?.debug('generated thumbnail')
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if (requiresDurationComputation) {
|
|
1282
|
+
uploadData.seconds = await getAudioDuration(originalFilePath)
|
|
1283
|
+
logger?.debug('computed audio duration')
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (requiresWaveformProcessing) {
|
|
1287
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger)
|
|
1288
|
+
logger?.debug('processed waveform')
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (requiresAudioBackground) {
|
|
1292
|
+
uploadData.backgroundArgb = await assertColor(options.backgroundColor)
|
|
1293
|
+
logger?.debug('computed backgroundColor audio status')
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
catch (error) {
|
|
1297
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
|
|
1298
|
+
}
|
|
1299
|
+
})()
|
|
1300
|
+
]).finally(async () => {
|
|
1301
|
+
try {
|
|
1302
|
+
await promises.unlink(encFilePath)
|
|
1303
|
+
|
|
1304
|
+
if (originalFilePath) {
|
|
1305
|
+
await promises.unlink(originalFilePath)
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
logger?.debug('removed tmp files')
|
|
1309
|
+
}
|
|
1310
|
+
catch (error) {
|
|
1311
|
+
logger?.warn('failed to remove tmp file')
|
|
1312
|
+
}
|
|
1313
|
+
})
|
|
1314
|
+
|
|
1315
|
+
const obj = WAProto.Message.fromObject({
|
|
1316
|
+
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
1317
|
+
url: mediaUrl,
|
|
1318
|
+
directPath,
|
|
1319
|
+
mediaKey,
|
|
1320
|
+
fileEncSha256,
|
|
1321
|
+
fileSha256,
|
|
1322
|
+
fileLength,
|
|
1323
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
1324
|
+
...uploadData,
|
|
1325
|
+
media: undefined
|
|
1326
|
+
})
|
|
1327
|
+
})
|
|
1328
|
+
|
|
1329
|
+
if (uploadData.ptv) {
|
|
1330
|
+
obj.ptvMessage = obj.videoMessage
|
|
1331
|
+
delete obj.videoMessage
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (cacheableKey) {
|
|
1335
|
+
logger?.debug({ cacheableKey }, 'set cache')
|
|
1336
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
return obj
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const prepareAlbumMessageContent = async (jid, albums, options) => {
|
|
1343
|
+
if (!Array.isArray(albums)) {
|
|
1344
|
+
throw new Error("albums must be an array containing media objects.")
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (albums.length === 0) {
|
|
1348
|
+
throw new Error("albums cannot be empty. At least one media item is required.")
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const validCount = albums.filter(m => ('image' in m) || ('video' in m)).length
|
|
1352
|
+
|
|
1353
|
+
if (validCount === 0) {
|
|
1354
|
+
throw new Error("albums contains no valid media. Use 'image' or 'video' keys.")
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
let mediaHandle
|
|
1358
|
+
let mediaMsg
|
|
1359
|
+
const message = []
|
|
1360
|
+
|
|
1361
|
+
const albumMsg = generateWAMessageFromContent(jid, {
|
|
1362
|
+
albumMessage: {
|
|
1363
|
+
expectedImageCount: albums.filter(item => 'image' in item).length,
|
|
1364
|
+
expectedVideoCount: albums.filter(item => 'video' in item).length
|
|
1365
|
+
}
|
|
1366
|
+
}, options)
|
|
1367
|
+
|
|
1368
|
+
await options.suki.relayMessage(jid, albumMsg.message, {
|
|
1369
|
+
messageId: albumMsg.key.id
|
|
1370
|
+
})
|
|
1371
|
+
|
|
1372
|
+
for (const media of albums) {
|
|
1373
|
+
let content = {}
|
|
1374
|
+
if ('image' in media) {
|
|
1375
|
+
content = { image: media.image }
|
|
1376
|
+
} else if ('video' in media) {
|
|
1377
|
+
content = { video: media.video }
|
|
1378
|
+
} else {
|
|
1379
|
+
continue
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
mediaMsg = await generateWAMessage(
|
|
1383
|
+
jid,
|
|
1384
|
+
{
|
|
1385
|
+
...content,
|
|
1386
|
+
...media
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
userJid: options.userJid,
|
|
1390
|
+
upload: async (encFilePath, opts) => {
|
|
1391
|
+
const up = await options.suki.waUploadToServer(
|
|
1392
|
+
encFilePath,
|
|
1393
|
+
{ ...opts, newsletter: isJidNewsletter(jid) }
|
|
1394
|
+
)
|
|
1395
|
+
mediaHandle = up.handle
|
|
1396
|
+
return up
|
|
1397
|
+
},
|
|
1398
|
+
...options
|
|
1399
|
+
}
|
|
1400
|
+
)
|
|
1401
|
+
|
|
1402
|
+
if (mediaMsg) {
|
|
1403
|
+
mediaMsg.message.messageContextInfo = {
|
|
1404
|
+
messageSecret: randomBytes(32),
|
|
1405
|
+
messageAssociation: {
|
|
1406
|
+
associationType: proto.MessageAssociation.AssociationType.MEDIA_ALBUM,
|
|
1407
|
+
parentMessageKey: albumMsg.key
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
message.push(mediaMsg)
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
return message
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
const prepareDisappearingMessageSettingContent = (expiration) => {
|
|
1419
|
+
const content = {
|
|
1420
|
+
ephemeralMessage: {
|
|
1421
|
+
message: {
|
|
1422
|
+
protocolMessage: {
|
|
1423
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
1424
|
+
ephemeralExpiration: expiration ? expiration : 0
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
return WAProto.Message.fromObject(content)
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* Generate forwarded message content like WA does
|
|
1435
|
+
* @param message the message to forward
|
|
1436
|
+
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
1437
|
+
*/
|
|
1438
|
+
const generateForwardMessageContent = (message, forceForward) => {
|
|
1439
|
+
let content = message.message
|
|
1440
|
+
|
|
1441
|
+
if (!content) {
|
|
1442
|
+
throw new Boom('no content in message', { statusCode: 400 })
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// hacky copy
|
|
1446
|
+
content = normalizeMessageContent(content)
|
|
1447
|
+
content = proto.Message.decode(proto.Message.encode(content).finish())
|
|
1448
|
+
|
|
1449
|
+
let key = Object.keys(content)[0]
|
|
1450
|
+
let score = content[key].contextInfo?.forwardingScore || 0
|
|
1451
|
+
|
|
1452
|
+
if (forceForward) score += forceForward ? forceForward : 1
|
|
1453
|
+
|
|
1454
|
+
if (key === 'conversation') {
|
|
1455
|
+
content.extendedTextMessage = { text: content[key] }
|
|
1456
|
+
delete content.conversation
|
|
1457
|
+
key = 'extendedTextMessage'
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
if (score > 0) {
|
|
1461
|
+
content[key].contextInfo = { forwardingScore: score, isForwarded: true }
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
else {
|
|
1465
|
+
content[key].contextInfo = {}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
return content
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
const hasNonNullishProperty = (message, key) => {
|
|
1472
|
+
return (
|
|
1473
|
+
typeof message === 'object' &&
|
|
1474
|
+
message !== null &&
|
|
1475
|
+
key in message &&
|
|
1476
|
+
message[key] !== null &&
|
|
1477
|
+
message[key] !== undefined
|
|
1478
|
+
)
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function hasOptionalProperty(obj, key) {
|
|
1482
|
+
return (
|
|
1483
|
+
typeof obj === 'object' &&
|
|
1484
|
+
obj !== null &&
|
|
1485
|
+
key in obj &&
|
|
1486
|
+
obj[key] !== null
|
|
1487
|
+
)
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
const generateWAMessageContent = async (message, options) => {
|
|
1491
|
+
let m = {}
|
|
1492
|
+
|
|
1493
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
1494
|
+
const extContent = { text: message.text }
|
|
1495
|
+
|
|
1496
|
+
let urlInfo = message.linkPreview
|
|
1497
|
+
|
|
1498
|
+
if (typeof urlInfo === "undefined") {
|
|
1499
|
+
urlInfo = await generateLinkPreviewIfRequired(
|
|
1500
|
+
message.text,
|
|
1501
|
+
options.getUrlInfo,
|
|
1502
|
+
options.logger
|
|
1503
|
+
)
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
if (urlInfo) {
|
|
1507
|
+
extContent.canonicalUrl = urlInfo["canonical-url"]
|
|
1508
|
+
extContent.matchedText = urlInfo["matched-text"]
|
|
1509
|
+
extContent.jpegThumbnail = urlInfo.jpegThumbnail
|
|
1510
|
+
extContent.description = urlInfo.description
|
|
1511
|
+
extContent.title = urlInfo.title
|
|
1512
|
+
extContent.previewType = 0
|
|
1513
|
+
|
|
1514
|
+
const img = urlInfo.highQualityThumbnail
|
|
1515
|
+
|
|
1516
|
+
if (img) {
|
|
1517
|
+
extContent.thumbnailDirectPath = img.directPath
|
|
1518
|
+
extContent.mediaKey = img.mediaKey
|
|
1519
|
+
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
|
|
1520
|
+
extContent.thumbnailWidth = img.width
|
|
1521
|
+
extContent.thumbnailHeight = img.height
|
|
1522
|
+
extContent.thumbnailSha256 = img.fileSha256
|
|
1523
|
+
extContent.thumbnailEncSha256 = img.fileEncSha256
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (options.backgroundColor) {
|
|
1528
|
+
extContent.backgroundArgb = await assertColor(
|
|
1529
|
+
options.backgroundColor
|
|
1530
|
+
)
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (options.textColor) {
|
|
1534
|
+
extContent.textArgb = await assertColor(options.textColor)
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if (options.font) {
|
|
1538
|
+
extContent.font = options.font
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
m.extendedTextMessage = extContent
|
|
1542
|
+
} else if (hasNonNullishProperty(message, "contacts")) {
|
|
1543
|
+
const contactLen = message.contacts.contacts.length
|
|
1544
|
+
|
|
1545
|
+
let contactMessage
|
|
1546
|
+
|
|
1547
|
+
if (!contactLen) {
|
|
1548
|
+
throw new Boom("require atleast 1 contact", { statusCode: 400 })
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
if (contactLen === 1) {
|
|
1552
|
+
contactMessage = {
|
|
1553
|
+
contactMessage: WAProto.Message.ContactMessage.fromObject(
|
|
1554
|
+
message.contacts.contacts[0]
|
|
1555
|
+
),
|
|
1556
|
+
}
|
|
1557
|
+
} else {
|
|
1558
|
+
contactMessage = {
|
|
1559
|
+
contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
|
|
1560
|
+
message.contacts
|
|
1561
|
+
),
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
m = contactMessage
|
|
1566
|
+
} else if (hasNonNullishProperty(message, "contacts")) {
|
|
1567
|
+
const contactLen = message.contacts.contacts.length
|
|
1568
|
+
|
|
1569
|
+
let contactMessage
|
|
1570
|
+
|
|
1571
|
+
if (!contactLen) {
|
|
1572
|
+
throw new Boom("require atleast 1 contact", { statusCode: 400 })
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
if (contactLen === 1) {
|
|
1576
|
+
contactMessage = {
|
|
1577
|
+
contactMessage: WAProto.Message.ContactMessage.fromObject(
|
|
1578
|
+
message.contacts.contacts[0]
|
|
1579
|
+
),
|
|
1580
|
+
}
|
|
1581
|
+
} else {
|
|
1582
|
+
contactMessage = {
|
|
1583
|
+
contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
|
|
1584
|
+
message.contacts
|
|
1585
|
+
),
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
m = contactMessage
|
|
1590
|
+
} else if (hasNonNullishProperty(message, "location")) {
|
|
1591
|
+
let locationMessage
|
|
1592
|
+
|
|
1593
|
+
if (message.live) {
|
|
1594
|
+
locationMessage = {
|
|
1595
|
+
liveLocationMessage: WAProto.Message.LiveLocationMessage.fromObject(
|
|
1596
|
+
message.location
|
|
1597
|
+
),
|
|
1598
|
+
}
|
|
1599
|
+
} else {
|
|
1600
|
+
locationMessage = {
|
|
1601
|
+
locationMessage: WAProto.Message.LocationMessage.fromObject(
|
|
1602
|
+
message.location
|
|
1603
|
+
),
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
m = locationMessage
|
|
1608
|
+
} else if (hasNonNullishProperty(message, "react")) {
|
|
1609
|
+
if (!message.react.senderTimestampMs) {
|
|
1610
|
+
message.react.senderTimestampMs = Date.now()
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.fromObject(
|
|
1614
|
+
message.react
|
|
1615
|
+
)
|
|
1616
|
+
} else if (hasNonNullishProperty(message, "delete")) {
|
|
1617
|
+
m.protocolMessage = {
|
|
1618
|
+
key: message.delete,
|
|
1619
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE,
|
|
1620
|
+
}
|
|
1621
|
+
} else if (hasNonNullishProperty(message, "sharePhoneNumber")) {
|
|
1622
|
+
m.protocolMessage = {
|
|
1623
|
+
type: WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER,
|
|
1624
|
+
}
|
|
1625
|
+
} else if (hasNonNullishProperty(message, "requestPhoneNumber")) {
|
|
1626
|
+
m.requestPhoneNumberMessage = {}
|
|
1627
|
+
} else if (hasNonNullishProperty(message, "forward")) {
|
|
1628
|
+
m = generateForwardMessageContent(message.forward, message.force)
|
|
1629
|
+
} else if (hasNonNullishProperty(message, "disappearingMessagesInChat")) {
|
|
1630
|
+
const exp =
|
|
1631
|
+
typeof message.disappearingMessagesInChat === "boolean"
|
|
1632
|
+
? message.disappearingMessagesInChat
|
|
1633
|
+
? WA_DEFAULT_EPHEMERAL
|
|
1634
|
+
: 0
|
|
1635
|
+
: message.disappearingMessagesInChat
|
|
1636
|
+
m = prepareDisappearingMessageSettingContent(exp)
|
|
1637
|
+
} else if (hasNonNullishProperty(message, "groupInvite")) {
|
|
1638
|
+
m.groupInviteMessage = {}
|
|
1639
|
+
|
|
1640
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.code
|
|
1641
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.expiration
|
|
1642
|
+
m.groupInviteMessage.caption = message.groupInvite.caption
|
|
1643
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid
|
|
1644
|
+
m.groupInviteMessage.groupName = message.groupInvite.name
|
|
1645
|
+
m.groupInviteMessage.contextInfo = message.contextInfo
|
|
1646
|
+
|
|
1647
|
+
if (options.getProfilePicUrl) {
|
|
1648
|
+
const pfpUrl = await options.getProfilePicUrl(
|
|
1649
|
+
message.groupInvite.jid
|
|
1650
|
+
)
|
|
1651
|
+
const { thumbnail } = await generateThumbnail(pfpUrl, "image")
|
|
1652
|
+
m.groupInviteMessage.jpegThumbnail = thumbnail
|
|
1653
|
+
}
|
|
1654
|
+
} else if (hasNonNullishProperty(message, "adminInvite")) {
|
|
1655
|
+
m.newsletterAdminInviteMessage = {}
|
|
1656
|
+
|
|
1657
|
+
m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid
|
|
1658
|
+
m.newsletterAdminInviteMessage.newsletterName =
|
|
1659
|
+
message.adminInvite.name
|
|
1660
|
+
m.newsletterAdminInviteMessage.caption = message.adminInvite.caption
|
|
1661
|
+
m.newsletterAdminInviteMessage.inviteExpiration =
|
|
1662
|
+
message.adminInvite.expiration
|
|
1663
|
+
m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
|
|
1664
|
+
|
|
1665
|
+
if (options.getProfilePicUrl) {
|
|
1666
|
+
const pfpUrl = await options.getProfilePicUrl(
|
|
1667
|
+
message.adminInvite.jid
|
|
1668
|
+
)
|
|
1669
|
+
const { thumbnail } = await generateThumbnail(pfpUrl, "image")
|
|
1670
|
+
m.newsletterAdminInviteMessage.jpegThumbnail = thumbnail
|
|
1671
|
+
}
|
|
1672
|
+
} else if (hasNonNullishProperty(message, "keep")) {
|
|
1673
|
+
m.keepInChatMessage = {}
|
|
1674
|
+
|
|
1675
|
+
m.keepInChatMessage.key = message.keep.key
|
|
1676
|
+
m.keepInChatMessage.keepType = message.keep?.type || 1
|
|
1677
|
+
m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
|
|
1678
|
+
} else if (hasNonNullishProperty(message, "call")) {
|
|
1679
|
+
m.scheduledCallCreationMessage = {}
|
|
1680
|
+
|
|
1681
|
+
m.scheduledCallCreationMessage.scheduledTimestampMs =
|
|
1682
|
+
message.call?.time || Date.now()
|
|
1683
|
+
m.scheduledCallCreationMessage.callType = message.call?.type || 1
|
|
1684
|
+
m.scheduledCallCreationMessage.title =
|
|
1685
|
+
message.call?.name || "Call Creation"
|
|
1686
|
+
} else if (hasNonNullishProperty(message, "paymentInvite")) {
|
|
1687
|
+
m.messageContextInfo = {}
|
|
1688
|
+
m.paymentInviteMessage = {}
|
|
1689
|
+
|
|
1690
|
+
m.paymentInviteMessage.expiryTimestamp =
|
|
1691
|
+
message.paymentInvite?.expiry || 0
|
|
1692
|
+
m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
|
|
1693
|
+
} else if (hasNonNullishProperty(message, "ptv")) {
|
|
1694
|
+
const { videoMessage } = await prepareWAMessageMedia(
|
|
1695
|
+
{ video: message.video },
|
|
1696
|
+
options
|
|
1697
|
+
)
|
|
1698
|
+
|
|
1699
|
+
m.ptvMessage = videoMessage
|
|
1700
|
+
} else if (hasNonNullishProperty(message, "order")) {
|
|
1701
|
+
m.orderMessage = WAProto.Message.OrderMessage.fromObject(message.order)
|
|
1702
|
+
} else if (hasNonNullishProperty(message, "product")) {
|
|
1703
|
+
const { imageMessage } = await prepareWAMessageMedia(
|
|
1704
|
+
{ image: message.product.productImage },
|
|
1705
|
+
options
|
|
1706
|
+
)
|
|
1707
|
+
|
|
1708
|
+
m.productMessage = WAProto.Message.ProductMessage.fromObject({
|
|
1709
|
+
...message,
|
|
1710
|
+
product: {
|
|
1711
|
+
...message.product,
|
|
1712
|
+
productImage: imageMessage,
|
|
1713
|
+
},
|
|
1714
|
+
})
|
|
1715
|
+
} else if (hasNonNullishProperty(message, "album")) {
|
|
1716
|
+
const imageMessages = message.album.filter((item) => "image" in item)
|
|
1717
|
+
const videoMessages = message.album.filter((item) => "video" in item)
|
|
1718
|
+
|
|
1719
|
+
m.albumMessage = WAProto.Message.AlbumMessage.fromObject({
|
|
1720
|
+
expectedImageCount: imageMessages.length,
|
|
1721
|
+
expectedVideoCount: videoMessages.length,
|
|
1722
|
+
})
|
|
1723
|
+
} else if (hasNonNullishProperty(message, "event")) {
|
|
1724
|
+
m.eventMessage = WAProto.Message.EventMessage.fromObject(message.event)
|
|
1725
|
+
|
|
1726
|
+
if (!message.event.startTime) {
|
|
1727
|
+
m.eventMessage.startTime = unixTimestampSeconds() + 86400
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
if (options.getCallLink && message.event.call) {
|
|
1731
|
+
const link = await options.getCallLink(
|
|
1732
|
+
message.event.call,
|
|
1733
|
+
m.eventMessage.startTime
|
|
1734
|
+
)
|
|
1735
|
+
m.eventMessage.joinLink = link
|
|
1736
|
+
}
|
|
1737
|
+
} else if (hasNonNullishProperty(message, "pollResult")) {
|
|
1738
|
+
if (!Array.isArray(message.pollResult.values)) {
|
|
1739
|
+
throw new Boom("Invalid pollResult values", { statusCode: 400 })
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
const pollResultSnapshotMessage = {
|
|
1743
|
+
name: message.pollResult.name,
|
|
1744
|
+
pollVotes: message.pollResult.values.map(
|
|
1745
|
+
([optionName, optionVoteCount]) => ({
|
|
1746
|
+
optionName,
|
|
1747
|
+
optionVoteCount,
|
|
1748
|
+
})
|
|
1749
|
+
),
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage
|
|
1753
|
+
} else if (hasNonNullishProperty(message, "poll")) {
|
|
1754
|
+
if (!Array.isArray(message.poll.values)) {
|
|
1755
|
+
throw new Boom("Invalid poll values", { statusCode: 400 })
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
if (
|
|
1759
|
+
message.poll.selectableCount < 0 ||
|
|
1760
|
+
message.poll.selectableCount > message.poll.values.length
|
|
1761
|
+
) {
|
|
1762
|
+
throw new Boom(
|
|
1763
|
+
`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`,
|
|
1764
|
+
{ statusCode: 400 }
|
|
1765
|
+
)
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
const pollCreationMessage = {
|
|
1769
|
+
name: message.poll.name,
|
|
1770
|
+
selectableOptionsCount: message.poll?.selectableCount || 0,
|
|
1771
|
+
options: message.poll.values.map((optionName) => ({ optionName })),
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
if (message.poll?.toAnnouncementGroup) {
|
|
1775
|
+
m.pollCreationMessageV2 = pollCreationMessage
|
|
1776
|
+
} else {
|
|
1777
|
+
if (message.poll.selectableCount > 0) {
|
|
1778
|
+
m.pollCreationMessageV3 = pollCreationMessage
|
|
1779
|
+
} else {
|
|
1780
|
+
m.pollCreationMessage = pollCreationMessage
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
} else if (hasNonNullishProperty(message, "payment")) {
|
|
1784
|
+
const requestPaymentMessage = {
|
|
1785
|
+
amount: {
|
|
1786
|
+
currencyCode: message.payment?.currency || "IDR",
|
|
1787
|
+
offset: message.payment?.offset || 0,
|
|
1788
|
+
value: message.payment?.amount || 999999999,
|
|
1789
|
+
},
|
|
1790
|
+
expiryTimestamp: message.payment?.expiry || 0,
|
|
1791
|
+
amount1000: message.payment?.amount || 999999999 * 1000,
|
|
1792
|
+
currencyCodeIso4217: message.payment?.currency || "IDR",
|
|
1793
|
+
requestFrom: message.payment?.from || "0@s.whatsapp.net",
|
|
1794
|
+
noteMessage: {
|
|
1795
|
+
extendedTextMessage: {
|
|
1796
|
+
text: message.payment?.note || "Notes",
|
|
1797
|
+
},
|
|
1798
|
+
},
|
|
1799
|
+
background: {
|
|
1800
|
+
placeholderArgb:
|
|
1801
|
+
message.payment?.image?.placeholderArgb || 4278190080,
|
|
1802
|
+
textArgb: message.payment?.image?.textArgb || 4294967295,
|
|
1803
|
+
subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
|
|
1804
|
+
type: 1,
|
|
1805
|
+
},
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
m.requestPaymentMessage = requestPaymentMessage
|
|
1809
|
+
} else if (hasNonNullishProperty(message, "stickerPack")) {
|
|
1810
|
+
const {
|
|
1811
|
+
stickers,
|
|
1812
|
+
cover,
|
|
1813
|
+
name,
|
|
1814
|
+
publisher,
|
|
1815
|
+
packId,
|
|
1816
|
+
description,
|
|
1817
|
+
} = message.stickerPack
|
|
1818
|
+
|
|
1819
|
+
const { zip } = require("fflate")
|
|
1820
|
+
|
|
1821
|
+
const stickerData = {}
|
|
1822
|
+
const stickerPromises = stickers.map(async (s, i) => {
|
|
1823
|
+
const { stream } = await getStream(s.sticker)
|
|
1824
|
+
const buffer = await toBuffer(stream)
|
|
1825
|
+
const hash = sha256(buffer).toString("base64url")
|
|
1826
|
+
const fileName = `${i.toString().padStart(2, "0")}_${hash}.webp`
|
|
1827
|
+
|
|
1828
|
+
stickerData[fileName] = [new Uint8Array(buffer), { level: 0 }]
|
|
1829
|
+
|
|
1830
|
+
return {
|
|
1831
|
+
fileName,
|
|
1832
|
+
mimetype: "image/webp",
|
|
1833
|
+
isAnimated: s.isAnimated || false,
|
|
1834
|
+
isLottie: s.isLottie || false,
|
|
1835
|
+
emojis: s.emojis || [],
|
|
1836
|
+
accessibilityLabel: s.accessibilityLabel || "",
|
|
1837
|
+
}
|
|
1838
|
+
})
|
|
1839
|
+
|
|
1840
|
+
const stickerMetadata = await Promise.all(stickerPromises)
|
|
1841
|
+
|
|
1842
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
1843
|
+
zip(stickerData, (err, data) => {
|
|
1844
|
+
if (err) {
|
|
1845
|
+
reject(err)
|
|
1846
|
+
} else {
|
|
1847
|
+
resolve(Buffer.from(data))
|
|
1848
|
+
}
|
|
1849
|
+
})
|
|
1850
|
+
})
|
|
1851
|
+
|
|
1852
|
+
const coverBuffer = await toBuffer((await getStream(cover)).stream)
|
|
1853
|
+
|
|
1854
|
+
const [stickerPackUpload, coverUpload] = await Promise.all([
|
|
1855
|
+
encryptedStream(zipBuffer, "sticker-pack", {
|
|
1856
|
+
logger: options.logger,
|
|
1857
|
+
opts: options.options,
|
|
1858
|
+
}),
|
|
1859
|
+
prepareWAMessageMedia(
|
|
1860
|
+
{ image: coverBuffer },
|
|
1861
|
+
{ ...options, mediaTypeOverride: "image" }
|
|
1862
|
+
),
|
|
1863
|
+
])
|
|
1864
|
+
|
|
1865
|
+
const stickerPackUploadResult = await options.upload(
|
|
1866
|
+
stickerPackUpload.encFilePath,
|
|
1867
|
+
{
|
|
1868
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString(
|
|
1869
|
+
"base64"
|
|
1870
|
+
),
|
|
1871
|
+
mediaType: "sticker-pack",
|
|
1872
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
1873
|
+
}
|
|
1874
|
+
)
|
|
1875
|
+
|
|
1876
|
+
const coverImage = coverUpload.imageMessage
|
|
1877
|
+
const imageDataHash = sha256(coverBuffer).toString("base64")
|
|
1878
|
+
const stickerPackId = packId || generateMessageID()
|
|
1879
|
+
|
|
1880
|
+
m.stickerPackMessage = {
|
|
1881
|
+
name,
|
|
1882
|
+
publisher,
|
|
1883
|
+
stickerPackId,
|
|
1884
|
+
packDescription: description,
|
|
1885
|
+
stickerPackOrigin:
|
|
1886
|
+
proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
|
|
1887
|
+
stickerPackSize: stickerPackUpload.fileLength,
|
|
1888
|
+
stickers: stickerMetadata,
|
|
1889
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
1890
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
1891
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
1892
|
+
directPath: stickerPackUploadResult.directPath,
|
|
1893
|
+
fileLength: stickerPackUpload.fileLength,
|
|
1894
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
1895
|
+
trayIconFileName: `${stickerPackId}.png`,
|
|
1896
|
+
imageDataHash,
|
|
1897
|
+
thumbnailDirectPath: coverImage.directPath,
|
|
1898
|
+
thumbnailFileSha256: coverImage.fileSha256,
|
|
1899
|
+
thumbnailFileEncSha256: coverImage.fileEncSha256,
|
|
1900
|
+
thumbnailHeight: coverImage.height,
|
|
1901
|
+
thumbnailWidth: coverImage.width,
|
|
1902
|
+
}
|
|
1903
|
+
} else {
|
|
1904
|
+
m = await prepareWAMessageMedia(message, options)
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (hasNonNullishProperty(message, "buttonReply")) {
|
|
1908
|
+
switch (message.type) {
|
|
1909
|
+
case "list":
|
|
1910
|
+
m.listResponseMessage = {
|
|
1911
|
+
title: message.buttonReply.title,
|
|
1912
|
+
description: message.buttonReply.description,
|
|
1913
|
+
singleSelectReply: {
|
|
1914
|
+
selectedRowId: message.buttonReply.rowId,
|
|
1915
|
+
},
|
|
1916
|
+
lisType:
|
|
1917
|
+
proto.Message.ListResponseMessage.ListType
|
|
1918
|
+
.SINGLE_SELECT,
|
|
1919
|
+
}
|
|
1920
|
+
break
|
|
1921
|
+
case "template":
|
|
1922
|
+
m.templateButtonReplyMessage = {
|
|
1923
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
1924
|
+
selectedId: message.buttonReply.id,
|
|
1925
|
+
selectedIndex: message.buttonReply.index,
|
|
1926
|
+
}
|
|
1927
|
+
break
|
|
1928
|
+
case "plain":
|
|
1929
|
+
m.buttonsResponseMessage = {
|
|
1930
|
+
selectedButtonId: message.buttonReply.id,
|
|
1931
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
1932
|
+
type:
|
|
1933
|
+
proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
|
|
1934
|
+
}
|
|
1935
|
+
break
|
|
1936
|
+
case "interactive":
|
|
1937
|
+
m.interactiveResponseMessage = {
|
|
1938
|
+
body: {
|
|
1939
|
+
text: message.buttonReply.displayText,
|
|
1940
|
+
format:
|
|
1941
|
+
proto.Message.InteractiveResponseMessage.Body.Format
|
|
1942
|
+
.EXTENSIONS_1,
|
|
1943
|
+
},
|
|
1944
|
+
nativeFlowResponseMessage: {
|
|
1945
|
+
name: message.buttonReply.nativeFlows.name,
|
|
1946
|
+
paramsJson: message.buttonReply.nativeFlows.paramsJson,
|
|
1947
|
+
version: message.buttonReply.nativeFlows.version,
|
|
1948
|
+
},
|
|
1949
|
+
}
|
|
1950
|
+
break
|
|
1951
|
+
}
|
|
1952
|
+
} else if (hasNonNullishProperty(message, "sections")) {
|
|
1953
|
+
m.listMessage = {
|
|
1954
|
+
title: message.title,
|
|
1955
|
+
buttonText: message.buttonText,
|
|
1956
|
+
footerText: message.footer,
|
|
1957
|
+
description: message.text,
|
|
1958
|
+
sections: message.sections,
|
|
1959
|
+
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
|
|
1960
|
+
}
|
|
1961
|
+
} else if (hasNonNullishProperty(message, "productList")) {
|
|
1962
|
+
const thumbnail = message.thumbnail
|
|
1963
|
+
? await generateThumbnail(message.thumbnail, "image")
|
|
1964
|
+
: null
|
|
1965
|
+
|
|
1966
|
+
m.listMessage = {
|
|
1967
|
+
title: message.title,
|
|
1968
|
+
buttonText: message.buttonText,
|
|
1969
|
+
footerText: message.footer,
|
|
1970
|
+
description: message.text,
|
|
1971
|
+
productListInfo: {
|
|
1972
|
+
productSections: message.productList,
|
|
1973
|
+
headerImage: {
|
|
1974
|
+
productId: message.productList[0].products[0].productId,
|
|
1975
|
+
jpegThumbnail: thumbnail?.thumbnail || null,
|
|
1976
|
+
},
|
|
1977
|
+
businessOwnerJid: message.businessOwnerJid,
|
|
1978
|
+
},
|
|
1979
|
+
listType: proto.Message.ListMessage.ListType.PRODUCT_LIST,
|
|
1980
|
+
}
|
|
1981
|
+
} else if (hasNonNullishProperty(message, "buttons")) {
|
|
1982
|
+
const buttonsMessage = {
|
|
1983
|
+
buttons: message.buttons.map((b) => ({
|
|
1984
|
+
...b,
|
|
1985
|
+
type: proto.Message.ButtonsMessage.Button.Type.RESPONSE,
|
|
1986
|
+
})),
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
1990
|
+
buttonsMessage.contentText = message.text
|
|
1991
|
+
buttonsMessage.headerType =
|
|
1992
|
+
proto.Message.ButtonsMessage.HeaderType.EMPTY
|
|
1993
|
+
} else {
|
|
1994
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
1995
|
+
buttonsMessage.contentText = message.caption
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
const type = Object.keys(m)[0].replace("Message", "").toUpperCase()
|
|
1999
|
+
|
|
2000
|
+
buttonsMessage.headerType =
|
|
2001
|
+
proto.Message.ButtonsMessage.HeaderType[type]
|
|
2002
|
+
|
|
2003
|
+
Object.assign(buttonsMessage, m)
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2007
|
+
buttonsMessage.text = message.title
|
|
2008
|
+
buttonsMessage.headerType =
|
|
2009
|
+
proto.Message.ButtonsMessage.HeaderType.TEXT
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2013
|
+
buttonsMessage.footerText = message.footer
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
m = { buttonsMessage }
|
|
2017
|
+
} else if (hasNonNullishProperty(message, "templateButtons")) {
|
|
2018
|
+
const hydratedTemplate = {
|
|
2019
|
+
hydratedButtons: message.templateButtons,
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
2023
|
+
hydratedTemplate.hydratedContentText = message.text
|
|
2024
|
+
} else {
|
|
2025
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
2026
|
+
hydratedTemplate.hydratedContentText = message.caption
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
Object.assign(msg, m)
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2033
|
+
hydratedTemplate.hydratedFooterText = message.footer
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
m = { templateMessage: { hydratedTemplate } }
|
|
2037
|
+
} else if (hasNonNullishProperty(message, "interactiveButtons")) {
|
|
2038
|
+
const interactiveMessage = {
|
|
2039
|
+
nativeFlowMessage: {
|
|
2040
|
+
buttons: message.interactiveButtons,
|
|
2041
|
+
},
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
2045
|
+
interactiveMessage.body = {
|
|
2046
|
+
text: message.text,
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2051
|
+
interactiveMessage.header = {
|
|
2052
|
+
title: message.title,
|
|
2053
|
+
subtitle: null,
|
|
2054
|
+
hasMediaAttachment: false,
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2058
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2059
|
+
}
|
|
2060
|
+
} else {
|
|
2061
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
2062
|
+
interactiveMessage.body = {
|
|
2063
|
+
text: message.caption,
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
interactiveMessage.header = {
|
|
2067
|
+
title: null,
|
|
2068
|
+
subtitle: null,
|
|
2069
|
+
hasMediaAttachment: false,
|
|
2070
|
+
...Object.assign(interactiveMessage, m),
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2074
|
+
interactiveMessage.header.title = message.title
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2078
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
if (hasNonNullishProperty(message, "hasMediaAttachment")) {
|
|
2082
|
+
interactiveMessage.header.hasMediaAttachment = Boolean(
|
|
2083
|
+
message.hasMediaAttachment
|
|
2084
|
+
)
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2090
|
+
interactiveMessage.footer = {
|
|
2091
|
+
text: message.footer,
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
m = { interactiveMessage }
|
|
2096
|
+
} else if (hasNonNullishProperty(message, "shop")) {
|
|
2097
|
+
const interactiveMessage = {
|
|
2098
|
+
shopStorefrontMessage: {
|
|
2099
|
+
surface: message.shop.surface,
|
|
2100
|
+
id: message.shop.id,
|
|
2101
|
+
},
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
2105
|
+
interactiveMessage.body = {
|
|
2106
|
+
text: message.text,
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2111
|
+
interactiveMessage.header = {
|
|
2112
|
+
title: message.title,
|
|
2113
|
+
subtitle: null,
|
|
2114
|
+
hasMediaAttachment: false,
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2118
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2119
|
+
}
|
|
2120
|
+
} else {
|
|
2121
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
2122
|
+
interactiveMessage.body = {
|
|
2123
|
+
text: message.caption,
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
interactiveMessage.header = {
|
|
2127
|
+
title: null,
|
|
2128
|
+
subtitle: null,
|
|
2129
|
+
hasMediaAttachment: false,
|
|
2130
|
+
...Object.assign(interactiveMessage, m),
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2134
|
+
interactiveMessage.header.title = message.title
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2138
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
if (hasNonNullishProperty(message, "hasMediaAttachment")) {
|
|
2142
|
+
interactiveMessage.header.hasMediaAttachment = Boolean(
|
|
2143
|
+
message.hasMediaAttachment
|
|
2144
|
+
)
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2150
|
+
interactiveMessage.footer = {
|
|
2151
|
+
text: message.footer,
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
m = { interactiveMessage }
|
|
2156
|
+
} else if (hasNonNullishProperty(message, "collection")) {
|
|
2157
|
+
const interactiveMessage = {
|
|
2158
|
+
collectionMessage: {
|
|
2159
|
+
bizJid: message.collection.bizJid,
|
|
2160
|
+
id: message.collection.id,
|
|
2161
|
+
messageVersion: message?.collection?.version,
|
|
2162
|
+
},
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
2166
|
+
interactiveMessage.body = {
|
|
2167
|
+
text: message.text,
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2172
|
+
interactiveMessage.header = {
|
|
2173
|
+
title: message.title,
|
|
2174
|
+
subtitle: null,
|
|
2175
|
+
hasMediaAttachment: false,
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2179
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2180
|
+
}
|
|
2181
|
+
} else {
|
|
2182
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
2183
|
+
interactiveMessage.body = {
|
|
2184
|
+
text: message.caption,
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
interactiveMessage.header = {
|
|
2188
|
+
title: null,
|
|
2189
|
+
subtitle: null,
|
|
2190
|
+
hasMediaAttachment: false,
|
|
2191
|
+
...Object.assign(interactiveMessage, m),
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2195
|
+
interactiveMessage.header.title = message.title
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2199
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
if (hasNonNullishProperty(message, "hasMediaAttachment")) {
|
|
2203
|
+
interactiveMessage.header.hasMediaAttachment = Boolean(
|
|
2204
|
+
message.hasMediaAttachment
|
|
2205
|
+
)
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2211
|
+
interactiveMessage.footer = {
|
|
2212
|
+
text: message.footer,
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
m = { interactiveMessage }
|
|
2217
|
+
} else if (hasNonNullishProperty(message, "cards")) {
|
|
2218
|
+
const slides = await Promise.all(
|
|
2219
|
+
message.cards.map(async (slide) => {
|
|
2220
|
+
const {
|
|
2221
|
+
image,
|
|
2222
|
+
video,
|
|
2223
|
+
product,
|
|
2224
|
+
title,
|
|
2225
|
+
body,
|
|
2226
|
+
footer,
|
|
2227
|
+
buttons,
|
|
2228
|
+
} = slide
|
|
2229
|
+
|
|
2230
|
+
let header
|
|
2231
|
+
|
|
2232
|
+
if (product) {
|
|
2233
|
+
const { imageMessage } = await prepareWAMessageMedia(
|
|
2234
|
+
{ image: product.productImage, ...options },
|
|
2235
|
+
options
|
|
2236
|
+
)
|
|
2237
|
+
header = {
|
|
2238
|
+
productMessage: {
|
|
2239
|
+
product: {
|
|
2240
|
+
...product,
|
|
2241
|
+
productImage: imageMessage,
|
|
2242
|
+
},
|
|
2243
|
+
...slide,
|
|
2244
|
+
},
|
|
2245
|
+
}
|
|
2246
|
+
} else if (image) {
|
|
2247
|
+
header = await prepareWAMessageMedia(
|
|
2248
|
+
{ image: image, ...options },
|
|
2249
|
+
options
|
|
2250
|
+
)
|
|
2251
|
+
} else if (video) {
|
|
2252
|
+
header = await prepareWAMessageMedia(
|
|
2253
|
+
{ video: video, ...options },
|
|
2254
|
+
options
|
|
2255
|
+
)
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
const msg = {
|
|
2259
|
+
header: {
|
|
2260
|
+
title,
|
|
2261
|
+
hasMediaAttachment: true,
|
|
2262
|
+
...header,
|
|
2263
|
+
},
|
|
2264
|
+
body: {
|
|
2265
|
+
text: body,
|
|
2266
|
+
},
|
|
2267
|
+
footer: {
|
|
2268
|
+
text: footer,
|
|
2269
|
+
},
|
|
2270
|
+
nativeFlowMessage: {
|
|
2271
|
+
buttons,
|
|
2272
|
+
},
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
return msg
|
|
2276
|
+
})
|
|
2277
|
+
)
|
|
2278
|
+
|
|
2279
|
+
const interactiveMessage = {
|
|
2280
|
+
carouselMessage: {
|
|
2281
|
+
cards: slides,
|
|
2282
|
+
},
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
if (hasNonNullishProperty(message, "text")) {
|
|
2286
|
+
interactiveMessage.body = {
|
|
2287
|
+
text: message.text,
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2292
|
+
interactiveMessage.header = {
|
|
2293
|
+
title: message.title,
|
|
2294
|
+
subtitle: null,
|
|
2295
|
+
hasMediaAttachment: false,
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2299
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2300
|
+
}
|
|
2301
|
+
} else {
|
|
2302
|
+
if (hasNonNullishProperty(message, "caption")) {
|
|
2303
|
+
interactiveMessage.body = {
|
|
2304
|
+
text: message.caption,
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
interactiveMessage.header = {
|
|
2308
|
+
title: null,
|
|
2309
|
+
subtitle: null,
|
|
2310
|
+
hasMediaAttachment: false,
|
|
2311
|
+
...Object.assign(interactiveMessage, m),
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
if (hasNonNullishProperty(message, "title")) {
|
|
2315
|
+
interactiveMessage.header.title = message.title
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
if (hasNonNullishProperty(message, "subtitle")) {
|
|
2319
|
+
interactiveMessage.header.subtitle = message.subtitle
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
if (hasNonNullishProperty(message, "hasMediaAttachment")) {
|
|
2323
|
+
interactiveMessage.header.hasMediaAttachment = Boolean(
|
|
2324
|
+
message.hasMediaAttachment
|
|
2325
|
+
)
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
if (hasNonNullishProperty(message, "footer")) {
|
|
2331
|
+
interactiveMessage.footer = {
|
|
2332
|
+
text: message.footer,
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
m = { interactiveMessage }
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
if (hasOptionalProperty(message, "ephemeral")) {
|
|
2340
|
+
m = { ephemeralMessage: { message: m } }
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
if (hasOptionalProperty(message, "mentions") && message.mentions?.length) {
|
|
2344
|
+
const messageType = Object.keys(m)[0]
|
|
2345
|
+
const key = m[messageType]
|
|
2346
|
+
|
|
2347
|
+
if ("contextInfo" in key && !!key.contextInfo) {
|
|
2348
|
+
key.contextInfo.mentionedJid = message.mentions
|
|
2349
|
+
} else if (key) {
|
|
2350
|
+
key.contextInfo = {
|
|
2351
|
+
mentionedJid: message.mentions,
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
if (hasOptionalProperty(message, "contextInfo") && !!message.contextInfo) {
|
|
2357
|
+
const messageType = Object.keys(m)[0]
|
|
2358
|
+
const key = m[messageType]
|
|
2359
|
+
|
|
2360
|
+
if ("contextInfo" in key && !!key.contextInfo) {
|
|
2361
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
|
|
2362
|
+
} else if (key) {
|
|
2363
|
+
key.contextInfo = message.contextInfo
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
if (hasOptionalProperty(message, "edit")) {
|
|
2368
|
+
m = {
|
|
2369
|
+
protocolMessage: {
|
|
2370
|
+
key: message.edit,
|
|
2371
|
+
editedMessage: m,
|
|
2372
|
+
timestampMs: Date.now(),
|
|
2373
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
|
|
2374
|
+
},
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
return WAProto.Message.fromObject(m)
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
const generateWAMessageFromContent = (jid, message, options) => {
|
|
2382
|
+
if (!options.timestamp) {
|
|
2383
|
+
options.timestamp = new Date()
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
const innerMessage = normalizeMessageContent(message)
|
|
2387
|
+
const key = getContentType(innerMessage)
|
|
2388
|
+
const timestamp = unixTimestampSeconds(options.timestamp)
|
|
2389
|
+
const threadId = []
|
|
2390
|
+
const { quoted, userJid } = options
|
|
2391
|
+
|
|
2392
|
+
if (quoted && !isJidNewsletter(jid)) {
|
|
2393
|
+
const participant = quoted.key.fromMe
|
|
2394
|
+
? userJid
|
|
2395
|
+
: quoted.participant || quoted.key.participant || quoted.key.remoteJid
|
|
2396
|
+
|
|
2397
|
+
let quotedMsg = normalizeMessageContent(quoted.message)
|
|
2398
|
+
const msgType = getContentType(quotedMsg)
|
|
2399
|
+
|
|
2400
|
+
quotedMsg = proto.Message.fromObject({ [msgType]: quotedMsg[msgType] })
|
|
2401
|
+
|
|
2402
|
+
const quotedContent = quotedMsg[msgType]
|
|
2403
|
+
|
|
2404
|
+
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
2405
|
+
delete quotedContent.contextInfo
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
let requestPayment
|
|
2409
|
+
|
|
2410
|
+
if (key === 'requestPaymentMessage') {
|
|
2411
|
+
if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage) {
|
|
2412
|
+
requestPayment = innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage
|
|
2413
|
+
} else if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.stickerMessage) {
|
|
2414
|
+
requestPayment = innerMessage.requestPaymentMessage?.noteMessage?.stickerMessage
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
const contextInfo = (key === 'requestPaymentMessage' ? requestPayment?.contextInfo : innerMessage[key].contextInfo) || {}
|
|
2419
|
+
|
|
2420
|
+
contextInfo.participant = jidNormalizedUser(participant)
|
|
2421
|
+
contextInfo.stanzaId = quoted.key.id
|
|
2422
|
+
contextInfo.quotedMessage = quotedMsg
|
|
2423
|
+
|
|
2424
|
+
if (jid !== quoted.key.remoteJid) {
|
|
2425
|
+
contextInfo.remoteJid = quoted.key.remoteJid
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
if (contextInfo.quotedMessage) {
|
|
2429
|
+
contextInfo.quotedType = proto.ContextInfo.QuotedType.EXPLICIT
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
if (contextInfo.quotedMessage && isJidGroup(jid)) {
|
|
2433
|
+
threadId.push({
|
|
2434
|
+
threadType: proto.ThreadID.ThreadType.VIEW_REPLIES,
|
|
2435
|
+
threadKey: {
|
|
2436
|
+
remoteJid: quoted?.key?.remoteJid,
|
|
2437
|
+
fromMe: quoted?.key?.fromMe,
|
|
2438
|
+
id: generateMessageID(),
|
|
2439
|
+
...(quoted?.key?.fromMe ? {} : { participant: quoted?.key?.participant })
|
|
2440
|
+
}
|
|
2441
|
+
})
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
if (key === 'requestPaymentMessage' && requestPayment) {
|
|
2445
|
+
requestPayment.contextInfo = contextInfo
|
|
2446
|
+
} else {
|
|
2447
|
+
innerMessage[key].contextInfo = contextInfo
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
if (key !== 'protocolMessage' &&
|
|
2452
|
+
key !== 'ephemeralMessage' &&
|
|
2453
|
+
!isJidNewsletter(jid)) {
|
|
2454
|
+
message.messageContextInfo = {
|
|
2455
|
+
threadId: threadId.length > 0 ? threadId : [],
|
|
2456
|
+
messageSecret: randomBytes(32),
|
|
2457
|
+
...message.messageContextInfo
|
|
2458
|
+
}
|
|
2459
|
+
innerMessage[key].contextInfo = {
|
|
2460
|
+
...(innerMessage[key].contextInfo || {}),
|
|
2461
|
+
expiration: options.ephemeralExpiration ? options.ephemeralExpiration : 0
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
message = WAProto.Message.fromObject(message)
|
|
2466
|
+
|
|
2467
|
+
const messageJSON = {
|
|
2468
|
+
key: {
|
|
2469
|
+
remoteJid: jid,
|
|
2470
|
+
fromMe: true,
|
|
2471
|
+
id: options?.messageId || generateMessageID()
|
|
2472
|
+
},
|
|
2473
|
+
message: message,
|
|
2474
|
+
messageTimestamp: timestamp,
|
|
2475
|
+
messageStubParameters: [],
|
|
2476
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
2477
|
+
status: WAMessageStatus.PENDING
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON)
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
const generateWAMessage = async (jid, content, options) => {
|
|
2484
|
+
options.logger = options?.logger?.child({ msgId: options.messageId })
|
|
2485
|
+
|
|
2486
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { newsletter: isJidNewsletter(jid), ...options }), options)
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
const getContentType = (content) => {
|
|
2490
|
+
if (content) {
|
|
2491
|
+
const keys = Object.keys(content)
|
|
2492
|
+
const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3') || k.endsWith('V4')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
|
|
2493
|
+
|
|
2494
|
+
return key
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
/**
|
|
2499
|
+
* Normalizes ephemeral, view once messages to regular message content
|
|
2500
|
+
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
2501
|
+
* @param content
|
|
2502
|
+
* @returns
|
|
2503
|
+
*/
|
|
2504
|
+
const normalizeMessageContent = (content) => {
|
|
2505
|
+
if (!content) {
|
|
2506
|
+
return undefined
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
for (let i = 0; i < 5; i++) {
|
|
2510
|
+
const inner = getFutureProofMessage(content)
|
|
2511
|
+
if (!inner) {
|
|
2512
|
+
break
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
content = inner.message
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
return content
|
|
2519
|
+
|
|
2520
|
+
function getFutureProofMessage(message) {
|
|
2521
|
+
return (
|
|
2522
|
+
(message?.editedMessage)
|
|
2523
|
+
|| (message?.statusAddYours)
|
|
2524
|
+
|| (message?.botTaskMessage)
|
|
2525
|
+
|| (message?.eventCoverImage)
|
|
2526
|
+
|| (message?.questionMessage)
|
|
2527
|
+
|| (message?.viewOnceMessage)
|
|
2528
|
+
|| (message?.botInvokeMessage)
|
|
2529
|
+
|| (message?.ephemeralMessage)
|
|
2530
|
+
|| (message?.limitSharingMessage)
|
|
2531
|
+
|| (message?.viewOnceMessageV2)
|
|
2532
|
+
|| (message?.lottieStickerMessage)
|
|
2533
|
+
|| (message?.questionReplyMessage)
|
|
2534
|
+
|| (message?.botForwardedMessage)
|
|
2535
|
+
|| (message?.statusMentionMessage)
|
|
2536
|
+
|| (message?.pollCreationMessageV4)
|
|
2537
|
+
|| (message?.associatedChildMessage)
|
|
2538
|
+
|| (message?.groupMentionedMessage)
|
|
2539
|
+
|| (message?.groupStatusMentionMessage)
|
|
2540
|
+
|| (message?.viewOnceMessageV2Extension)
|
|
2541
|
+
|| (message?.documentWithCaptionMessage)
|
|
2542
|
+
|| (message?.pollCreationOptionImageMessage))
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
/**
|
|
2547
|
+
* Extract the true message content from a message
|
|
2548
|
+
* Eg. extracts the inner message from a disappearing message/view once message
|
|
2549
|
+
*/
|
|
2550
|
+
const extractMessageContent = (content) => {
|
|
2551
|
+
const extractFromButtonsMessage = (msg) => {
|
|
2552
|
+
const header = typeof msg.header === 'object' && msg.header !== null
|
|
2553
|
+
|
|
2554
|
+
if (header ? msg.header?.imageMessage : msg.imageMessage) {
|
|
2555
|
+
return { imageMessage: header ? msg.header.imageMessage : msg.imageMessage }
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
else if (header ? msg.header?.documentMessage : msg.documentMessage) {
|
|
2559
|
+
return { documentMessage: header ? msg.header.documentMessage : msg.documentMessage }
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
else if (header ? msg.header?.videoMessage : msg.videoMessage) {
|
|
2563
|
+
return { videoMessage: header ? msg.header.videoMessage: msg.videoMessage }
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
else if (header ? msg.header?.locationMessage : msg.locationMessage) {
|
|
2567
|
+
return { locationMessage: header ? msg.header.locationMessage : msg.locationMessage }
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
else if (header ? msg.header?.productMessage : msg.productMessage) {
|
|
2571
|
+
return { productMessage: header ? msg.header.productMessage : msg.productMessage }
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
else {
|
|
2575
|
+
return {
|
|
2576
|
+
conversation: 'contentText' in msg
|
|
2577
|
+
? msg.contentText
|
|
2578
|
+
: ('hydratedContentText' in msg ? msg.hydratedContentText : 'body' in msg ? msg.body.text : '')
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
content = normalizeMessageContent(content)
|
|
2584
|
+
|
|
2585
|
+
if (content?.buttonsMessage) {
|
|
2586
|
+
return extractFromButtonsMessage(content.buttonsMessage)
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
if (content?.interactiveMessage) {
|
|
2590
|
+
return extractFromButtonsMessage(content.interactiveMessage)
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
if (content?.templateMessage?.interactiveMessageTemplate) {
|
|
2594
|
+
return extractFromButtonsMessage(content?.templateMessage?.interactiveMessageTemplate)
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
2598
|
+
return extractFromButtonsMessage(content?.templateMessage?.hydratedFourRowTemplate)
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
2602
|
+
return extractFromButtonsMessage(content?.templateMessage?.hydratedTemplate)
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
2606
|
+
return extractFromButtonsMessage(content?.templateMessage?.fourRowTemplate)
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
return content
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
/**
|
|
2613
|
+
* Returns the device predicted by message ID
|
|
2614
|
+
*/
|
|
2615
|
+
const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' :
|
|
2616
|
+
/^3E.{20}$/.test(id) ? 'web' :
|
|
2617
|
+
/^(.{21}|.{32})$/.test(id) ? 'android' :
|
|
2618
|
+
/^(3F|.{18}$)/.test(id) ? 'desktop' :
|
|
2619
|
+
'baileys'
|
|
2620
|
+
|
|
2621
|
+
/** Upserts a receipt in the message */
|
|
2622
|
+
const updateMessageWithReceipt = (msg, receipt) => {
|
|
2623
|
+
msg.userReceipt = msg.userReceipt || []
|
|
2624
|
+
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
|
|
2625
|
+
|
|
2626
|
+
if (recp) {
|
|
2627
|
+
Object.assign(recp, receipt)
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
else {
|
|
2631
|
+
msg.userReceipt.push(receipt)
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
/** Update the message with a new reaction */
|
|
2636
|
+
const updateMessageWithReaction = (msg, reaction) => {
|
|
2637
|
+
const authorID = getKeyAuthor(reaction.key)
|
|
2638
|
+
const reactions = (msg.reactions || [])
|
|
2639
|
+
.filter(r => getKeyAuthor(r.key) !== authorID)
|
|
2640
|
+
|
|
2641
|
+
reaction.text = reaction.text || ''
|
|
2642
|
+
reactions.push(reaction)
|
|
2643
|
+
msg.reactions = reactions
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
/** Update the message with a new poll update */
|
|
2647
|
+
const updateMessageWithPollUpdate = (msg, update) => {
|
|
2648
|
+
const authorID = getKeyAuthor(update.pollUpdateMessageKey)
|
|
2649
|
+
const votes = (msg.pollUpdates || [])
|
|
2650
|
+
.filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID)
|
|
2651
|
+
|
|
2652
|
+
if (update.vote?.selectedOptions?.length) {
|
|
2653
|
+
votes.push(update)
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
msg.pollUpdates = votes
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
/** Update the message with a new event response*/
|
|
2660
|
+
const updateMessageWithEventResponse = (msg, update) => {
|
|
2661
|
+
const authorID = getKeyAuthor(update.eventResponseMessageKey)
|
|
2662
|
+
const responses = (msg.eventResponses || [])
|
|
2663
|
+
.filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID)
|
|
2664
|
+
|
|
2665
|
+
responses.push(update)
|
|
2666
|
+
msg.eventResponses = responses
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
/**
|
|
2670
|
+
* Aggregates all poll updates in a poll.
|
|
2671
|
+
* @param msg the poll creation message
|
|
2672
|
+
* @param meId your jid
|
|
2673
|
+
* @returns A list of options & their voters
|
|
2674
|
+
*/
|
|
2675
|
+
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
2676
|
+
message = normalizeMessageContent(message)
|
|
2677
|
+
|
|
2678
|
+
const opts = message?.pollCreationMessage?.options || message?.pollCreationMessageV2?.options || message?.pollCreationMessageV3?.options || []
|
|
2679
|
+
|
|
2680
|
+
const voteHashMap = opts.reduce((acc, opt) => {
|
|
2681
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString()
|
|
2682
|
+
acc[hash] = {
|
|
2683
|
+
name: opt.optionName || '',
|
|
2684
|
+
voters: []
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
return acc
|
|
2688
|
+
}, {})
|
|
2689
|
+
|
|
2690
|
+
for (const update of pollUpdates || []) {
|
|
2691
|
+
const { vote } = update
|
|
2692
|
+
|
|
2693
|
+
if (!vote) {
|
|
2694
|
+
continue
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
for (const option of vote.selectedOptions || []) {
|
|
2698
|
+
const hash = option.toString()
|
|
2699
|
+
let data = voteHashMap[hash]
|
|
2700
|
+
|
|
2701
|
+
if (!data) {
|
|
2702
|
+
voteHashMap[hash] = {
|
|
2703
|
+
name: 'Unknown',
|
|
2704
|
+
voters: []
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
data = voteHashMap[hash]
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId))
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
return Object.values(voteHashMap)
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
/**
|
|
2718
|
+
* Aggregates all event responses in an event message.
|
|
2719
|
+
* @param msg the event creation message
|
|
2720
|
+
* @param meLid your lid
|
|
2721
|
+
* @returns A list of response types & their responders
|
|
2722
|
+
*/
|
|
2723
|
+
function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
|
|
2724
|
+
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
|
|
2725
|
+
const responseMap = {}
|
|
2726
|
+
|
|
2727
|
+
for (const type of responseTypes) {
|
|
2728
|
+
responseMap[type] = {
|
|
2729
|
+
response: type,
|
|
2730
|
+
responders: []
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
for (const update of eventResponses) {
|
|
2735
|
+
const { response } = update.response || 0
|
|
2736
|
+
const responseType = proto.Message.EventResponseMessage.EventResponseType[response]
|
|
2737
|
+
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
2738
|
+
responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meLid))
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
return Object.values(responseMap)
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
2746
|
+
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
2747
|
+
const keyMap = {}
|
|
2748
|
+
|
|
2749
|
+
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
2750
|
+
if (!fromMe) {
|
|
2751
|
+
const uqKey = `${remoteJid}:${participant || ''}`
|
|
2752
|
+
|
|
2753
|
+
if (!keyMap[uqKey]) {
|
|
2754
|
+
keyMap[uqKey] = {
|
|
2755
|
+
jid: remoteJid,
|
|
2756
|
+
participant: participant,
|
|
2757
|
+
messageIds: []
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
keyMap[uqKey].messageIds.push(id)
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
return Object.values(keyMap)
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
const REUPLOAD_REQUIRED_STATUS = [410, 404]
|
|
2769
|
+
|
|
2770
|
+
/**
|
|
2771
|
+
* Downloads the given message. Throws an error if it's not a media message
|
|
2772
|
+
*/
|
|
2773
|
+
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
2774
|
+
const result = await downloadMsg().catch(async (error) => {
|
|
2775
|
+
if (ctx &&
|
|
2776
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
2777
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
2778
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...')
|
|
2779
|
+
|
|
2780
|
+
// request reupload
|
|
2781
|
+
message = await ctx.reuploadRequest(message)
|
|
2782
|
+
|
|
2783
|
+
const result = await downloadMsg()
|
|
2784
|
+
return result
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
throw error
|
|
2788
|
+
})
|
|
2789
|
+
|
|
2790
|
+
return result
|
|
2791
|
+
|
|
2792
|
+
async function downloadMsg() {
|
|
2793
|
+
const mContent = extractMessageContent(message.message)
|
|
2794
|
+
|
|
2795
|
+
if (!mContent) {
|
|
2796
|
+
throw new Boom('No message present', { statusCode: 400, data: message })
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
const contentType = getContentType(mContent)
|
|
2800
|
+
|
|
2801
|
+
let mediaType = contentType?.replace('Message', '')
|
|
2802
|
+
|
|
2803
|
+
const media = contentType === 'productMessage' ? mContent[contentType]?.product?.productImage : mContent[contentType]
|
|
2804
|
+
|
|
2805
|
+
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
2806
|
+
throw new Boom(`"${contentType}" message is not a media message`)
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
let download
|
|
2810
|
+
|
|
2811
|
+
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
2812
|
+
download = {
|
|
2813
|
+
directPath: media.thumbnailDirectPath,
|
|
2814
|
+
mediaKey: media.mediaKey
|
|
2815
|
+
}
|
|
2816
|
+
mediaType = 'thumbnail-link'
|
|
2817
|
+
}
|
|
2818
|
+
else {
|
|
2819
|
+
download = media
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
const stream = await downloadContentFromMessage(download, mediaType, options)
|
|
2823
|
+
|
|
2824
|
+
if (type === 'buffer') {
|
|
2825
|
+
const bufferArray = []
|
|
2826
|
+
for await (const chunk of stream) {
|
|
2827
|
+
bufferArray.push(chunk);
|
|
2828
|
+
}
|
|
2829
|
+
return Buffer.concat(bufferArray);
|
|
2830
|
+
}
|
|
2831
|
+
return stream
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
2836
|
+
const assertMediaContent = (content) => {
|
|
2837
|
+
content = extractMessageContent(content)
|
|
2838
|
+
|
|
2839
|
+
const mediaContent = content?.documentMessage ||
|
|
2840
|
+
content?.imageMessage ||
|
|
2841
|
+
content?.videoMessage ||
|
|
2842
|
+
content?.audioMessage ||
|
|
2843
|
+
content?.stickerMessage
|
|
2844
|
+
|
|
2845
|
+
if (!mediaContent) {
|
|
2846
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
return mediaContent
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
module.exports = {
|
|
2853
|
+
extractUrlFromText,
|
|
2854
|
+
generateLinkPreviewIfRequired,
|
|
2855
|
+
prepareWAMessageMedia,
|
|
2856
|
+
prepareAlbumMessageContent,
|
|
2857
|
+
prepareDisappearingMessageSettingContent,
|
|
2858
|
+
generateForwardMessageContent,
|
|
2859
|
+
generateWAMessageContent,
|
|
2860
|
+
generateWAMessageFromContent,
|
|
2861
|
+
generateWAMessage,
|
|
2862
|
+
getContentType,
|
|
2863
|
+
hasNonNullishProperty,
|
|
2864
|
+
normalizeMessageContent,
|
|
2865
|
+
extractMessageContent,
|
|
2866
|
+
getDevice,
|
|
2867
|
+
updateMessageWithReceipt,
|
|
2868
|
+
updateMessageWithReaction,
|
|
2869
|
+
updateMessageWithPollUpdate,
|
|
2870
|
+
updateMessageWithEventResponse,
|
|
2871
|
+
getAggregateVotesInPollMessage,
|
|
2872
|
+
getAggregateResponsesInEventMessage,
|
|
2873
|
+
aggregateMessageKeysNotFromMe,
|
|
2874
|
+
downloadMediaMessage,
|
|
2875
|
+
assertMediaContent
|
|
2876
|
+
}
|
|
2877
|
+
return module.exports;
|
|
2878
|
+
})();
|
|
2879
|
+
for (const key of Object.keys(__kageModule74)) {
|
|
2880
|
+
if (key !== '__esModule' && !Object.prototype.hasOwnProperty.call(exports,key)) Object.defineProperty(exports,key,{enumerable:true,configurable:true,get:()=>__kageModule74[key]});
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
// Kage: funciones avanzadas.
|
|
2884
|
+
const __kageModule178 = (() => {
|
|
2885
|
+
const module = { exports: {} }; const exports = module.exports;
|
|
2886
|
+
"use strict";
|
|
2887
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2888
|
+
exports.shouldIncludeBizBinaryNode = exports.assertMediaContent = exports.downloadMediaMessage = exports.aggregateMessageKeysNotFromMe = exports.updateMessageWithEventResponse = exports.updateMessageWithPollUpdate = exports.updateMessageWithReaction = exports.updateMessageWithReceipt = exports.getDevice = exports.extractMessageContent = exports.normalizeMessageContent = exports.getContentType = exports.generateWAMessage = exports.generateWAMessageFromContent = exports.generateWAMessageContent = exports.markViewOnce = exports.hasValidCarouselHeader = exports.hasValidInteractiveHeader = exports.hasValidAlbumMedia = exports.hasOptionalProperty = exports.hasNonNullishProperty = exports.generateForwardMessageContent = exports.prepareDisappearingMessageSettingContent = exports.prepareWAMessageMedia = exports.generateLinkPreviewIfRequired = exports.extractUrlFromText = void 0;
|
|
2889
|
+
exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage;
|
|
2890
|
+
exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage;
|
|
2891
|
+
const node_crypto_1 = require("node:crypto");
|
|
2892
|
+
const node_fs_1 = require("node:fs");
|
|
2893
|
+
const index_js_1 = require("../../WAProto/index.js");
|
|
2894
|
+
const index_js_2 = require("../Addon/index.js");
|
|
2895
|
+
const index_js_3 = require("../Defaults/index.js");
|
|
2896
|
+
const index_js_4 = require("../Types/index.js");
|
|
2897
|
+
const index_js_5 = require("../WABinary/index.js");
|
|
2898
|
+
const crypto_js_1 = require("./crypto.js");
|
|
2899
|
+
const generics_js_1 = require("./generics.js");
|
|
2900
|
+
const messages_media_js_1 = require("./messages-media.js");
|
|
2901
|
+
const rich_message_utils_js_1 = require("./rich-message-utils.js");
|
|
2902
|
+
const reporting_utils_js_1 = require("./reporting-utils.js");
|
|
2903
|
+
const MIMETYPE_MAP = {
|
|
2904
|
+
image: 'image/jpeg',
|
|
2905
|
+
video: 'video/mp4',
|
|
2906
|
+
document: 'application/pdf',
|
|
2907
|
+
audio: 'audio/ogg; codecs=opus',
|
|
2908
|
+
sticker: 'image/webp',
|
|
2909
|
+
'product-catalog-image': 'image/jpeg'
|
|
2910
|
+
};
|
|
2911
|
+
/**
|
|
2912
|
+
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
2913
|
+
* @param text eg. hello https://google.com
|
|
2914
|
+
* @returns the URL, eg. https://google.com
|
|
2915
|
+
*/
|
|
2916
|
+
const extractUrlFromText = (text) => text.match(index_js_3.URL_REGEX)?.[0];
|
|
2917
|
+
exports.extractUrlFromText = extractUrlFromText;
|
|
2918
|
+
const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
2919
|
+
const url = (0, exports.extractUrlFromText)(text);
|
|
2920
|
+
if (!!getUrlInfo && url) {
|
|
2921
|
+
try {
|
|
2922
|
+
const urlInfo = await getUrlInfo(url);
|
|
2923
|
+
return urlInfo;
|
|
2924
|
+
}
|
|
2925
|
+
catch (error) {
|
|
2926
|
+
// ignore if fails
|
|
2927
|
+
logger?.warn({ trace: error.stack }, 'url generation failed');
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
|
|
2932
|
+
const ensureMessageProto = (m) => {
|
|
2933
|
+
if (m instanceof index_js_4.WAProto.Message)
|
|
2934
|
+
return m;
|
|
2935
|
+
return index_js_4.WAProto.Message.fromObject(m);
|
|
2936
|
+
};
|
|
2937
|
+
const assertColor = (color) => {
|
|
2938
|
+
if (typeof color === 'number') {
|
|
2939
|
+
return color < 0
|
|
2940
|
+
? 0xffffffff + color + 1
|
|
2941
|
+
: color;
|
|
2942
|
+
}
|
|
2943
|
+
let hex = color.trim();
|
|
2944
|
+
if (hex.charCodeAt(0) === 35) {
|
|
2945
|
+
hex = hex.slice(1);
|
|
2946
|
+
}
|
|
2947
|
+
if (hex.length <= 6) {
|
|
2948
|
+
hex = hex.padStart(6, '0');
|
|
2949
|
+
hex = 'FF' + hex;
|
|
2950
|
+
}
|
|
2951
|
+
return parseInt(hex, 16);
|
|
2952
|
+
};
|
|
2953
|
+
const prepareWAMessageMedia = async (message, options) => {
|
|
2954
|
+
const logger = options.logger;
|
|
2955
|
+
let mediaType;
|
|
2956
|
+
for (const key of index_js_3.MEDIA_KEYS) {
|
|
2957
|
+
if (key in message) {
|
|
2958
|
+
mediaType = key;
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
if (!mediaType) {
|
|
2962
|
+
throw new index_js_2.HttpError('Invalid media type', { statusCode: 400 });
|
|
2963
|
+
}
|
|
2964
|
+
const uploadData = {
|
|
2965
|
+
...message,
|
|
2966
|
+
media: message[mediaType]
|
|
2967
|
+
};
|
|
2968
|
+
delete uploadData[mediaType];
|
|
2969
|
+
// check if cacheable + generate cache key
|
|
2970
|
+
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
2971
|
+
'url' in uploadData.media &&
|
|
2972
|
+
!!uploadData.media.url &&
|
|
2973
|
+
!!options.mediaCache &&
|
|
2974
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
2975
|
+
if (mediaType === 'document' && !uploadData.fileName) {
|
|
2976
|
+
uploadData.fileName = 'file';
|
|
2977
|
+
}
|
|
2978
|
+
if (!uploadData.mimetype) {
|
|
2979
|
+
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
2980
|
+
}
|
|
2981
|
+
if (cacheableKey) {
|
|
2982
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
2983
|
+
if (mediaBuff) {
|
|
2984
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
2985
|
+
const obj = index_js_1.proto.Message.decode(mediaBuff);
|
|
2986
|
+
const key = `${mediaType}Message`;
|
|
2987
|
+
Object.assign(obj[key], { ...uploadData, media: undefined });
|
|
2988
|
+
return obj;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
const isNewsletter = !!options.jid && (0, index_js_5.isJidNewsletter)(options.jid);
|
|
2992
|
+
if (isNewsletter) {
|
|
2993
|
+
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
2994
|
+
const { filePath, fileSha256, fileLength } = await (0, messages_media_js_1.getRawMediaUploadData)(uploadData.media, options.mediaTypeOverride || mediaType, logger);
|
|
2995
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
2996
|
+
const { mediaUrl, directPath, thumbnailDirectPath, thumbnailSha256 } = await options.upload(filePath, {
|
|
2997
|
+
fileEncSha256B64: fileSha256B64,
|
|
2998
|
+
mediaType: mediaType,
|
|
2999
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
3000
|
+
newsletter: isNewsletter
|
|
3001
|
+
});
|
|
3002
|
+
await node_fs_1.promises.unlink(filePath);
|
|
3003
|
+
const obj = ensureMessageProto({
|
|
3004
|
+
// todo: add more support here
|
|
3005
|
+
[`${mediaType}Message`]: {
|
|
3006
|
+
url: mediaUrl,
|
|
3007
|
+
directPath,
|
|
3008
|
+
fileSha256,
|
|
3009
|
+
fileLength,
|
|
3010
|
+
thumbnailDirectPath,
|
|
3011
|
+
thumbnailSha256,
|
|
3012
|
+
...uploadData,
|
|
3013
|
+
media: undefined
|
|
3014
|
+
}
|
|
3015
|
+
});
|
|
3016
|
+
if (uploadData.ptv) {
|
|
3017
|
+
obj.ptvMessage = obj.videoMessage;
|
|
3018
|
+
delete obj.videoMessage;
|
|
3019
|
+
}
|
|
3020
|
+
if (obj.stickerMessage) {
|
|
3021
|
+
obj.stickerMessage.stickerSentTs = Date.now();
|
|
3022
|
+
}
|
|
3023
|
+
if (cacheableKey) {
|
|
3024
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
3025
|
+
await options.mediaCache.set(cacheableKey, index_js_4.WAProto.Message.encode(obj).finish());
|
|
3026
|
+
}
|
|
3027
|
+
return obj;
|
|
3028
|
+
}
|
|
3029
|
+
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
3030
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
3031
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
3032
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
3033
|
+
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
3034
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await (0, messages_media_js_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
3035
|
+
logger,
|
|
3036
|
+
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
3037
|
+
opts: options.options
|
|
3038
|
+
});
|
|
3039
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
3040
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
3041
|
+
(async () => {
|
|
3042
|
+
const result = await options.upload(encFilePath, {
|
|
3043
|
+
fileEncSha256B64,
|
|
3044
|
+
mediaType,
|
|
3045
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
3046
|
+
});
|
|
3047
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
3048
|
+
return result;
|
|
3049
|
+
})(),
|
|
3050
|
+
(async () => {
|
|
3051
|
+
try {
|
|
3052
|
+
if (requiresThumbnailComputation) {
|
|
3053
|
+
const { thumbnail, originalImageDimensions } = await (0, messages_media_js_1.generateThumbnail)(originalFilePath, mediaType, options);
|
|
3054
|
+
uploadData.jpegThumbnail = thumbnail;
|
|
3055
|
+
if (!uploadData.width && originalImageDimensions) {
|
|
3056
|
+
uploadData.width = originalImageDimensions.width;
|
|
3057
|
+
uploadData.height = originalImageDimensions.height;
|
|
3058
|
+
logger?.debug('set dimensions');
|
|
3059
|
+
}
|
|
3060
|
+
logger?.debug('generated thumbnail');
|
|
3061
|
+
}
|
|
3062
|
+
if (requiresDurationComputation || requiresWaveformProcessing) {
|
|
3063
|
+
const computedData = await (0, messages_media_js_1.getAudioData)(originalFilePath, logger);
|
|
3064
|
+
uploadData.seconds = computedData.durationSeconds;
|
|
3065
|
+
uploadData.waveform = computedData.waveform;
|
|
3066
|
+
logger?.debug('computed audio data');
|
|
3067
|
+
}
|
|
3068
|
+
if (requiresAudioBackground) {
|
|
3069
|
+
uploadData.backgroundArgb = assertColor(options.backgroundColor);
|
|
3070
|
+
logger?.debug('computed backgroundColor audio status');
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
catch (error) {
|
|
3074
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
3075
|
+
}
|
|
3076
|
+
})()
|
|
3077
|
+
]).finally(async () => {
|
|
3078
|
+
try {
|
|
3079
|
+
await node_fs_1.promises.unlink(encFilePath);
|
|
3080
|
+
if (originalFilePath) {
|
|
3081
|
+
await node_fs_1.promises.unlink(originalFilePath);
|
|
3082
|
+
}
|
|
3083
|
+
logger?.debug('removed tmp files');
|
|
3084
|
+
}
|
|
3085
|
+
catch (error) {
|
|
3086
|
+
logger?.warn('failed to remove tmp file');
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
const obj = ensureMessageProto({
|
|
3090
|
+
[`${mediaType}Message`]: {
|
|
3091
|
+
url: mediaUrl,
|
|
3092
|
+
directPath,
|
|
3093
|
+
mediaKey,
|
|
3094
|
+
fileEncSha256,
|
|
3095
|
+
fileSha256,
|
|
3096
|
+
fileLength,
|
|
3097
|
+
mediaKeyTimestamp: (0, generics_js_1.unixTimestampSeconds)(),
|
|
3098
|
+
...uploadData,
|
|
3099
|
+
media: undefined
|
|
3100
|
+
}
|
|
3101
|
+
});
|
|
3102
|
+
if (uploadData.ptv) {
|
|
3103
|
+
obj.ptvMessage = obj.videoMessage;
|
|
3104
|
+
delete obj.videoMessage;
|
|
3105
|
+
}
|
|
3106
|
+
if (cacheableKey) {
|
|
3107
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
3108
|
+
await options.mediaCache.set(cacheableKey, index_js_4.WAProto.Message.encode(obj).finish());
|
|
3109
|
+
}
|
|
3110
|
+
return obj;
|
|
3111
|
+
};
|
|
3112
|
+
exports.prepareWAMessageMedia = prepareWAMessageMedia;
|
|
3113
|
+
// Kage: actualización de compatibilidad.
|
|
3114
|
+
const prepareProductMessage = async (message, options) => {
|
|
3115
|
+
if (!message.businessOwnerJid) {
|
|
3116
|
+
throw new index_js_2.HttpError('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
3117
|
+
}
|
|
3118
|
+
const { imageMessage } = await (0, exports.prepareWAMessageMedia)({ image: message.image || message.product.productImage }, options);
|
|
3119
|
+
// Kage: actualización de compatibilidad.
|
|
3120
|
+
const { image, ...content } = message;
|
|
3121
|
+
content.product = {
|
|
3122
|
+
currencyCode: 'IDR',
|
|
3123
|
+
priceAmount1000: 1000,
|
|
3124
|
+
title: index_js_3.LIBRARY_NAME,
|
|
3125
|
+
...message.product,
|
|
3126
|
+
productImage: imageMessage
|
|
3127
|
+
};
|
|
3128
|
+
return content;
|
|
3129
|
+
};
|
|
3130
|
+
/**
|
|
3131
|
+
* Kage: nota de compatibilidad.
|
|
3132
|
+
* ---
|
|
3133
|
+
* Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
|
|
3134
|
+
* based on https://github.com/WhiskeySockets/Baileys/pull/1561
|
|
3135
|
+
*/
|
|
3136
|
+
// Kage: actualización de compatibilidad.
|
|
3137
|
+
const prepareStickerPackMessage = async (message, options) => {
|
|
3138
|
+
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ kage-baileys' } = message;
|
|
3139
|
+
if (stickers.length > 60) {
|
|
3140
|
+
throw new index_js_2.HttpError('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
3141
|
+
}
|
|
3142
|
+
if (stickers.length === 0) {
|
|
3143
|
+
throw new index_js_2.HttpError('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
3144
|
+
}
|
|
3145
|
+
if (!cover) {
|
|
3146
|
+
throw new index_js_2.HttpError('Sticker pack must contain a cover', { statusCode: 400 });
|
|
3147
|
+
}
|
|
3148
|
+
const logger = options.logger;
|
|
3149
|
+
// Kage: actualización de compatibilidad.
|
|
3150
|
+
let cacheableKey = false;
|
|
3151
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
3152
|
+
const urls = [];
|
|
3153
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
3154
|
+
const data = stickers[i].data;
|
|
3155
|
+
if (typeof data === 'object' && data?.url) {
|
|
3156
|
+
urls.push(data.url);
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
if (urls.length > 0) {
|
|
3160
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
if (cacheableKey) {
|
|
3164
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
3165
|
+
if (mediaBuff) {
|
|
3166
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
3167
|
+
return index_js_1.proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
const lib = await (0, messages_media_js_1.getImageProcessingLibrary)();
|
|
3171
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
3172
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
3173
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
3174
|
+
if (!hasSharp && !hasImage) {
|
|
3175
|
+
throw new index_js_2.HttpError('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
3176
|
+
}
|
|
3177
|
+
const stickerPackIdValue = (0, generics_js_1.generateMessageIDV2)();
|
|
3178
|
+
const stickerData = new Array(stickers.length);
|
|
3179
|
+
const stickerMetadata = new Array(stickers.length);
|
|
3180
|
+
await Promise.all(stickers.map(async (sticker, index) => {
|
|
3181
|
+
const { stream } = await (0, messages_media_js_1.getStream)(sticker.data);
|
|
3182
|
+
const buffer = await (0, messages_media_js_1.toBuffer)(stream);
|
|
3183
|
+
let webpBuffer;
|
|
3184
|
+
let isAnimated = false;
|
|
3185
|
+
if (isWebPBuffer(buffer)) {
|
|
3186
|
+
webpBuffer = buffer;
|
|
3187
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
3188
|
+
}
|
|
3189
|
+
else if (hasSharp) {
|
|
3190
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
3191
|
+
.resize(512, 512, { fit: 'inside' })
|
|
3192
|
+
.webp({ quality: 80 })
|
|
3193
|
+
.toBuffer();
|
|
3194
|
+
}
|
|
3195
|
+
else {
|
|
3196
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
3197
|
+
.resize(512, 512)
|
|
3198
|
+
.webp(80);
|
|
3199
|
+
}
|
|
3200
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
3201
|
+
throw new index_js_2.HttpError(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
3202
|
+
}
|
|
3203
|
+
const hash = (0, crypto_js_1.sha256)(webpBuffer)
|
|
3204
|
+
.toString('base64')
|
|
3205
|
+
.replace(/\//g, '-');
|
|
3206
|
+
const fileName = `${hash}.webp`;
|
|
3207
|
+
stickerData[index] = {
|
|
3208
|
+
name: fileName,
|
|
3209
|
+
data: webpBuffer
|
|
3210
|
+
};
|
|
3211
|
+
stickerMetadata[index] = {
|
|
3212
|
+
fileName,
|
|
3213
|
+
mimetype: 'image/webp',
|
|
3214
|
+
isAnimated,
|
|
3215
|
+
isLottie: sticker.isLottie || false,
|
|
3216
|
+
emojis: sticker.emojis || ['✨'],
|
|
3217
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
3218
|
+
};
|
|
3219
|
+
}));
|
|
3220
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
3221
|
+
const { stream: coverStream } = await (0, messages_media_js_1.getStream)(cover);
|
|
3222
|
+
const coverBuffer = await (0, messages_media_js_1.toBuffer)(coverStream);
|
|
3223
|
+
let coverWebpBuffer;
|
|
3224
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
3225
|
+
coverWebpBuffer = coverBuffer;
|
|
3226
|
+
}
|
|
3227
|
+
else if (hasSharp) {
|
|
3228
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
3229
|
+
.resize(512, 512, { fit: 'inside' })
|
|
3230
|
+
.webp({ quality: 80 })
|
|
3231
|
+
.toBuffer();
|
|
3232
|
+
}
|
|
3233
|
+
else {
|
|
3234
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
3235
|
+
.resize(512, 512)
|
|
3236
|
+
.webp(80);
|
|
3237
|
+
}
|
|
3238
|
+
stickerData[stickers.length] = {
|
|
3239
|
+
name: trayIconFileName,
|
|
3240
|
+
data: coverWebpBuffer
|
|
3241
|
+
};
|
|
3242
|
+
const zipBuffer = await (0, index_js_2.createZip)(stickerData);
|
|
3243
|
+
const stickerPackUpload = await (0, messages_media_js_1.encryptedStream)(zipBuffer, 'sticker-pack', {
|
|
3244
|
+
logger,
|
|
3245
|
+
opts: options.options
|
|
3246
|
+
});
|
|
3247
|
+
let stickerPackUploadResult;
|
|
3248
|
+
try {
|
|
3249
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
3250
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
3251
|
+
mediaType: 'sticker-pack',
|
|
3252
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
finally {
|
|
3256
|
+
node_fs_1.promises.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
3257
|
+
}
|
|
3258
|
+
const obj = {
|
|
3259
|
+
name,
|
|
3260
|
+
publisher,
|
|
3261
|
+
stickerPackId: stickerPackIdValue,
|
|
3262
|
+
packDescription: description,
|
|
3263
|
+
stickerPackOrigin: index_js_1.proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
3264
|
+
stickerPackSize: zipBuffer.length,
|
|
3265
|
+
stickers: stickerMetadata,
|
|
3266
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
3267
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
3268
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
3269
|
+
directPath: stickerPackUploadResult.directPath,
|
|
3270
|
+
fileLength: stickerPackUpload.fileLength,
|
|
3271
|
+
mediaKeyTimestamp: (0, generics_js_1.unixTimestampSeconds)(),
|
|
3272
|
+
trayIconFileName
|
|
3273
|
+
};
|
|
3274
|
+
try {
|
|
3275
|
+
let thumbnailBuffer;
|
|
3276
|
+
if (hasSharp) {
|
|
3277
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
3278
|
+
}
|
|
3279
|
+
else if (hasImage) {
|
|
3280
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
3281
|
+
}
|
|
3282
|
+
else if (hasJimp) {
|
|
3283
|
+
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
3284
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
3285
|
+
}
|
|
3286
|
+
else {
|
|
3287
|
+
throw new Error('No image processing library available for thumbnail generation');
|
|
3288
|
+
}
|
|
3289
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
3290
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
3291
|
+
}
|
|
3292
|
+
const thumbUpload = await (0, messages_media_js_1.encryptedStream)(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
3293
|
+
logger,
|
|
3294
|
+
opts: options.options,
|
|
3295
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
3296
|
+
});
|
|
3297
|
+
let thumbUploadResult;
|
|
3298
|
+
try {
|
|
3299
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
3300
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
3301
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
3302
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
finally {
|
|
3306
|
+
node_fs_1.promises.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
3307
|
+
}
|
|
3308
|
+
Object.assign(obj, {
|
|
3309
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
3310
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
3311
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
3312
|
+
thumbnailHeight: 252,
|
|
3313
|
+
thumbnailWidth: 252,
|
|
3314
|
+
imageDataHash: (0, crypto_js_1.sha256)(thumbnailBuffer).toString('base64')
|
|
3315
|
+
});
|
|
3316
|
+
}
|
|
3317
|
+
catch (error) {
|
|
3318
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
3319
|
+
}
|
|
3320
|
+
if (cacheableKey) {
|
|
3321
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
3322
|
+
options.mediaCache.set(cacheableKey, index_js_4.WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
3323
|
+
}
|
|
3324
|
+
return index_js_4.WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
3325
|
+
};
|
|
3326
|
+
// Kage: actualización de compatibilidad.
|
|
3327
|
+
const prepareNativeFlowButtons = (message) => {
|
|
3328
|
+
const buttons = message.nativeFlow;
|
|
3329
|
+
const isButtonsFieldArray = Array.isArray(buttons);
|
|
3330
|
+
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
3331
|
+
const messageParamsJson = {};
|
|
3332
|
+
// Kage: actualización de compatibilidad.
|
|
3333
|
+
if ((0, exports.hasOptionalProperty)(message, 'offerText') && !!message.offerText) {
|
|
3334
|
+
Object.assign(messageParamsJson, {
|
|
3335
|
+
limited_time_offer: {
|
|
3336
|
+
text: message.offerText || index_js_3.LIBRARY_NAME,
|
|
3337
|
+
url: message.offerUrl || index_js_3.DONATE_URL, // Kage: nota de compatibilidad.
|
|
3338
|
+
copy_code: message.offerCode,
|
|
3339
|
+
expiration_time: message.offerExpiration
|
|
3340
|
+
}
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
if ((0, exports.hasOptionalProperty)(message, 'optionText') && !!message.optionText) {
|
|
3344
|
+
Object.assign(messageParamsJson, {
|
|
3345
|
+
bottom_sheet: {
|
|
3346
|
+
in_thread_buttons_limit: 1,
|
|
3347
|
+
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
3348
|
+
list_title: message.optionTitle || '📄 Select Options',
|
|
3349
|
+
button_title: message.optionText
|
|
3350
|
+
}
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
return {
|
|
3354
|
+
buttons: correctedField.map(button => {
|
|
3355
|
+
const buttonText = button.text || button.buttonText;
|
|
3356
|
+
const buttonIcon = button.icon?.toUpperCase();
|
|
3357
|
+
if ((0, exports.hasOptionalProperty)(button, 'id') && !!button.id) {
|
|
3358
|
+
return {
|
|
3359
|
+
name: 'quick_reply',
|
|
3360
|
+
buttonParamsJson: JSON.stringify({
|
|
3361
|
+
display_text: buttonText || '👉🏻 Click',
|
|
3362
|
+
id: button.id,
|
|
3363
|
+
icon: buttonIcon
|
|
3364
|
+
})
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
else if ((0, exports.hasOptionalProperty)(button, 'copy') && !!button.copy) {
|
|
3368
|
+
return {
|
|
3369
|
+
name: 'cta_copy',
|
|
3370
|
+
buttonParamsJson: JSON.stringify({
|
|
3371
|
+
display_text: buttonText || '📋 Copy',
|
|
3372
|
+
copy_code: button.copy,
|
|
3373
|
+
icon: buttonIcon
|
|
3374
|
+
})
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
else if ((0, exports.hasOptionalProperty)(button, 'url') && !!button.url) {
|
|
3378
|
+
return {
|
|
3379
|
+
name: 'cta_url',
|
|
3380
|
+
buttonParamsJson: JSON.stringify({
|
|
3381
|
+
display_text: buttonText || '🌐 Visit',
|
|
3382
|
+
url: button.url,
|
|
3383
|
+
merchant_url: button.url,
|
|
3384
|
+
webview_interaction: button.useWebview,
|
|
3385
|
+
icon: buttonIcon
|
|
3386
|
+
})
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
else if ((0, exports.hasOptionalProperty)(button, 'call') && !!button.call) {
|
|
3390
|
+
return {
|
|
3391
|
+
name: 'cta_call',
|
|
3392
|
+
buttonParamsJson: JSON.stringify({
|
|
3393
|
+
display_text: buttonText || '📞 Call',
|
|
3394
|
+
phone_number: button.call,
|
|
3395
|
+
icon: buttonIcon
|
|
3396
|
+
})
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3399
|
+
// Kage: actualización de compatibilidad.
|
|
3400
|
+
else if ((0, exports.hasOptionalProperty)(button, 'sections') && !!button.sections) {
|
|
3401
|
+
return {
|
|
3402
|
+
name: 'single_select',
|
|
3403
|
+
buttonParamsJson: JSON.stringify({
|
|
3404
|
+
title: buttonText || '📋 Select',
|
|
3405
|
+
sections: button.sections,
|
|
3406
|
+
icon: buttonIcon
|
|
3407
|
+
})
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
return button;
|
|
3411
|
+
}),
|
|
3412
|
+
messageParamsJson: JSON.stringify(messageParamsJson)
|
|
3413
|
+
};
|
|
3414
|
+
};
|
|
3415
|
+
const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
3416
|
+
ephemeralExpiration = ephemeralExpiration || 0;
|
|
3417
|
+
const content = {
|
|
3418
|
+
ephemeralMessage: {
|
|
3419
|
+
message: {
|
|
3420
|
+
protocolMessage: {
|
|
3421
|
+
type: index_js_4.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
3422
|
+
ephemeralExpiration
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
};
|
|
3427
|
+
return index_js_4.WAProto.Message.fromObject(content);
|
|
3428
|
+
};
|
|
3429
|
+
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
|
|
3430
|
+
/**
|
|
3431
|
+
* Generate forwarded message content like WA does
|
|
3432
|
+
* @param message the message to forward
|
|
3433
|
+
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
3434
|
+
*/
|
|
3435
|
+
const generateForwardMessageContent = (message, forceForward) => {
|
|
3436
|
+
let content = message.message;
|
|
3437
|
+
if (!content) {
|
|
3438
|
+
throw new index_js_2.HttpError('no content in message', { statusCode: 400 });
|
|
3439
|
+
}
|
|
3440
|
+
// hacky copy
|
|
3441
|
+
content = (0, exports.normalizeMessageContent)(content);
|
|
3442
|
+
content = index_js_1.proto.Message.decode(index_js_1.proto.Message.encode(content).finish());
|
|
3443
|
+
let key = Object.keys(content)[0];
|
|
3444
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
3445
|
+
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
3446
|
+
if (key === 'conversation') {
|
|
3447
|
+
content.extendedTextMessage = { text: content[key] };
|
|
3448
|
+
delete content.conversation;
|
|
3449
|
+
key = 'extendedTextMessage';
|
|
3450
|
+
}
|
|
3451
|
+
const key_ = content?.[key];
|
|
3452
|
+
if (score > 0) {
|
|
3453
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
3454
|
+
}
|
|
3455
|
+
else {
|
|
3456
|
+
key_.contextInfo = {};
|
|
3457
|
+
}
|
|
3458
|
+
return content;
|
|
3459
|
+
};
|
|
3460
|
+
exports.generateForwardMessageContent = generateForwardMessageContent;
|
|
3461
|
+
const hasNonNullishProperty = (message, key) => {
|
|
3462
|
+
return message != null &&
|
|
3463
|
+
typeof message === 'object' &&
|
|
3464
|
+
key in message &&
|
|
3465
|
+
message[key] != null;
|
|
3466
|
+
};
|
|
3467
|
+
exports.hasNonNullishProperty = hasNonNullishProperty;
|
|
3468
|
+
const hasOptionalProperty = (obj, key) => {
|
|
3469
|
+
return obj != null &&
|
|
3470
|
+
typeof obj === 'object' &&
|
|
3471
|
+
key in obj &&
|
|
3472
|
+
obj[key] != null;
|
|
3473
|
+
};
|
|
3474
|
+
exports.hasOptionalProperty = hasOptionalProperty;
|
|
3475
|
+
// Kage: actualización de compatibilidad.
|
|
3476
|
+
const hasValidAlbumMedia = (message) => {
|
|
3477
|
+
return !!(message.imageMessage ||
|
|
3478
|
+
message.videoMessage);
|
|
3479
|
+
};
|
|
3480
|
+
exports.hasValidAlbumMedia = hasValidAlbumMedia;
|
|
3481
|
+
const hasValidInteractiveHeader = (message) => {
|
|
3482
|
+
return !!(message.imageMessage ||
|
|
3483
|
+
message.videoMessage ||
|
|
3484
|
+
message.documentMessage ||
|
|
3485
|
+
message.productMessage ||
|
|
3486
|
+
message.locationMessage);
|
|
3487
|
+
};
|
|
3488
|
+
exports.hasValidInteractiveHeader = hasValidInteractiveHeader;
|
|
3489
|
+
// Kage: actualización de compatibilidad.
|
|
3490
|
+
const hasValidCarouselHeader = (message) => {
|
|
3491
|
+
return !!(message.imageMessage ||
|
|
3492
|
+
message.videoMessage ||
|
|
3493
|
+
message.productMessage);
|
|
3494
|
+
};
|
|
3495
|
+
exports.hasValidCarouselHeader = hasValidCarouselHeader;
|
|
3496
|
+
const markViewOnce = (m) => {
|
|
3497
|
+
const type = (0, exports.getContentType)(m);
|
|
3498
|
+
if (!type)
|
|
3499
|
+
return;
|
|
3500
|
+
const content = m[type];
|
|
3501
|
+
if (!content || typeof content !== 'object')
|
|
3502
|
+
return;
|
|
3503
|
+
const MessageType = index_js_4.WAProto.Message[type.charAt(0).toUpperCase() + type.slice(1)];
|
|
3504
|
+
if (!MessageType?.prototype || !('viewOnce' in MessageType.prototype))
|
|
3505
|
+
return;
|
|
3506
|
+
content.viewOnce = true;
|
|
3507
|
+
};
|
|
3508
|
+
exports.markViewOnce = markViewOnce;
|
|
3509
|
+
const generateWAMessageContent = async (message, options) => {
|
|
3510
|
+
var _a, _b;
|
|
3511
|
+
let m = {};
|
|
3512
|
+
// Kage: actualización de compatibilidad.
|
|
3513
|
+
if ((0, exports.hasNonNullishProperty)(message, 'raw')) {
|
|
3514
|
+
delete message.raw;
|
|
3515
|
+
return message;
|
|
3516
|
+
}
|
|
3517
|
+
// Kage: actualización de compatibilidad.
|
|
3518
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'code') ||
|
|
3519
|
+
(0, exports.hasNonNullishProperty)(message, 'links') ||
|
|
3520
|
+
(0, exports.hasNonNullishProperty)(message, 'table') ||
|
|
3521
|
+
(0, exports.hasNonNullishProperty)(message, 'richResponse')) {
|
|
3522
|
+
m = (0, rich_message_utils_js_1.prepareRichResponseMessage)(message);
|
|
3523
|
+
}
|
|
3524
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'text')) {
|
|
3525
|
+
const extContent = { text: message.text };
|
|
3526
|
+
let urlInfo = message.linkPreview;
|
|
3527
|
+
if (urlInfo == null) {
|
|
3528
|
+
urlInfo = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger);
|
|
3529
|
+
}
|
|
3530
|
+
if (urlInfo) {
|
|
3531
|
+
extContent.matchedText = urlInfo['matched-text'];
|
|
3532
|
+
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
3533
|
+
extContent.description = urlInfo.description;
|
|
3534
|
+
extContent.title = urlInfo.title;
|
|
3535
|
+
extContent.previewType = urlInfo.previewType ?? urlInfo['preview-type'] ?? 0;
|
|
3536
|
+
extContent.linkPreviewMetadata = urlInfo.linkPreviewMetadata;
|
|
3537
|
+
const img = urlInfo.highQualityThumbnail;
|
|
3538
|
+
if (img) {
|
|
3539
|
+
extContent.thumbnailDirectPath = img.directPath;
|
|
3540
|
+
extContent.mediaKey = img.mediaKey;
|
|
3541
|
+
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
|
|
3542
|
+
extContent.thumbnailWidth = img.width;
|
|
3543
|
+
extContent.thumbnailHeight = img.height;
|
|
3544
|
+
extContent.thumbnailSha256 = img.fileSha256;
|
|
3545
|
+
extContent.thumbnailEncSha256 = img.fileEncSha256;
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
const customFavicon = message.favicon;
|
|
3549
|
+
const favicon = customFavicon && typeof options.upload === 'function'
|
|
3550
|
+
? (await (0, exports.prepareWAMessageMedia)({ image: customFavicon }, options)).imageMessage
|
|
3551
|
+
: urlInfo?.favicon;
|
|
3552
|
+
if (favicon) {
|
|
3553
|
+
extContent.faviconMmsMetadata = {
|
|
3554
|
+
thumbnailDirectPath: favicon.directPath,
|
|
3555
|
+
mediaKey: favicon.mediaKey,
|
|
3556
|
+
mediaKeyTimestamp: favicon.mediaKeyTimestamp,
|
|
3557
|
+
thumbnailWidth: 32,
|
|
3558
|
+
thumbnailHeight: 32,
|
|
3559
|
+
thumbnailSha256: favicon.fileSha256,
|
|
3560
|
+
thumbnailEncSha256: favicon.fileEncSha256
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
if (options.backgroundColor) {
|
|
3564
|
+
extContent.backgroundArgb = assertColor(options.backgroundColor);
|
|
3565
|
+
}
|
|
3566
|
+
if (options.font) {
|
|
3567
|
+
extContent.font = options.font;
|
|
3568
|
+
}
|
|
3569
|
+
m.extendedTextMessage = extContent;
|
|
3570
|
+
}
|
|
3571
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'contacts')) {
|
|
3572
|
+
const contactLen = message.contacts.contacts.length;
|
|
3573
|
+
if (!contactLen) {
|
|
3574
|
+
throw new index_js_2.HttpError('require atleast 1 contact', { statusCode: 400 });
|
|
3575
|
+
}
|
|
3576
|
+
if (contactLen === 1) {
|
|
3577
|
+
m.contactMessage = index_js_4.WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
3578
|
+
}
|
|
3579
|
+
else {
|
|
3580
|
+
m.contactsArrayMessage = index_js_4.WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'location')) {
|
|
3584
|
+
m.locationMessage = index_js_4.WAProto.Message.LocationMessage.create(message.location);
|
|
3585
|
+
}
|
|
3586
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'react')) {
|
|
3587
|
+
if (!message.react.senderTimestampMs) {
|
|
3588
|
+
message.react.senderTimestampMs = Date.now();
|
|
3589
|
+
}
|
|
3590
|
+
m.reactionMessage = index_js_4.WAProto.Message.ReactionMessage.create(message.react);
|
|
3591
|
+
}
|
|
3592
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'delete')) {
|
|
3593
|
+
m.protocolMessage = {
|
|
3594
|
+
key: message.delete,
|
|
3595
|
+
type: index_js_4.WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'forward')) {
|
|
3599
|
+
m = (0, exports.generateForwardMessageContent)(message.forward, message.force);
|
|
3600
|
+
}
|
|
3601
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'disappearingMessagesInChat')) {
|
|
3602
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
3603
|
+
? message.disappearingMessagesInChat
|
|
3604
|
+
? index_js_3.WA_DEFAULT_EPHEMERAL
|
|
3605
|
+
: 0
|
|
3606
|
+
: message.disappearingMessagesInChat;
|
|
3607
|
+
m = (0, exports.prepareDisappearingMessageSettingContent)(exp);
|
|
3608
|
+
}
|
|
3609
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'groupInvite')) {
|
|
3610
|
+
m.groupInviteMessage = {};
|
|
3611
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
|
|
3612
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
|
|
3613
|
+
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
3614
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
3615
|
+
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
3616
|
+
//TODO: use built-in interface and get disappearing mode info etc.
|
|
3617
|
+
//TODO: cache / use store!?
|
|
3618
|
+
if (options.getProfilePicUrl) {
|
|
3619
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
3620
|
+
if (pfpUrl) {
|
|
3621
|
+
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
3622
|
+
if (resp.ok) {
|
|
3623
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
3624
|
+
m.groupInviteMessage.jpegThumbnail = buf;
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'stickers')) {
|
|
3630
|
+
m.stickerPackMessage = await prepareStickerPackMessage(message, options);
|
|
3631
|
+
}
|
|
3632
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'pin')) {
|
|
3633
|
+
m.pinInChatMessage = {};
|
|
3634
|
+
m.messageContextInfo = {};
|
|
3635
|
+
m.pinInChatMessage.key = message.pin;
|
|
3636
|
+
m.pinInChatMessage.type = message.type;
|
|
3637
|
+
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
3638
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
3639
|
+
}
|
|
3640
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'keep')) {
|
|
3641
|
+
m.keepInChatMessage = {};
|
|
3642
|
+
m.keepInChatMessage.key = message.keep;
|
|
3643
|
+
m.keepInChatMessage.keepType = message.type;
|
|
3644
|
+
m.keepInChatMessage.timestampMs = Date.now();
|
|
3645
|
+
}
|
|
3646
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'flowReply')) {
|
|
3647
|
+
m.interactiveResponseMessage = {
|
|
3648
|
+
body: {
|
|
3649
|
+
format: message.flowReply.format || index_js_1.proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
|
|
3650
|
+
text: message.flowReply.text
|
|
3651
|
+
},
|
|
3652
|
+
nativeFlowResponseMessage: {
|
|
3653
|
+
name: message.flowReply.name,
|
|
3654
|
+
paramsJson: message.flowReply.paramsJson || '{}',
|
|
3655
|
+
version: message.flowReply.version || 1
|
|
3656
|
+
}
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3659
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'buttonReply')) {
|
|
3660
|
+
switch (message.type) {
|
|
3661
|
+
case 'template':
|
|
3662
|
+
m.templateButtonReplyMessage = {
|
|
3663
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
3664
|
+
selectedId: message.buttonReply.id,
|
|
3665
|
+
selectedIndex: message.buttonReply.index
|
|
3666
|
+
};
|
|
3667
|
+
break;
|
|
3668
|
+
case 'plain':
|
|
3669
|
+
m.buttonsResponseMessage = {
|
|
3670
|
+
selectedButtonId: message.buttonReply.id,
|
|
3671
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
3672
|
+
type: index_js_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
3673
|
+
};
|
|
3674
|
+
break;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'listReply')) {
|
|
3678
|
+
m.listResponseMessage = {
|
|
3679
|
+
description: message.listReply.description,
|
|
3680
|
+
listType: index_js_1.proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
|
|
3681
|
+
singleSelectReply: {
|
|
3682
|
+
selectedRowId: message.listReply.id
|
|
3683
|
+
},
|
|
3684
|
+
title: message.listReply.title
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
else if ((0, exports.hasOptionalProperty)(message, 'ptv') && message.ptv) {
|
|
3688
|
+
const { videoMessage } = await (0, exports.prepareWAMessageMedia)({ video: message.video }, options);
|
|
3689
|
+
m.ptvMessage = videoMessage;
|
|
3690
|
+
}
|
|
3691
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'product')) {
|
|
3692
|
+
m.productMessage = await prepareProductMessage(message, options);
|
|
3693
|
+
}
|
|
3694
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'event')) {
|
|
3695
|
+
m.eventMessage = {};
|
|
3696
|
+
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
3697
|
+
if (message.event.call && options.getCallLink) {
|
|
3698
|
+
const token = await options.getCallLink(message.event.call, { startTime });
|
|
3699
|
+
m.eventMessage.joinLink = (message.event.call === 'audio' ? index_js_3.CALL_AUDIO_PREFIX : index_js_3.CALL_VIDEO_PREFIX) + token;
|
|
3700
|
+
}
|
|
3701
|
+
m.messageContextInfo = {
|
|
3702
|
+
// encKey
|
|
3703
|
+
messageSecret: message.event.messageSecret || (0, node_crypto_1.randomBytes)(32)
|
|
3704
|
+
};
|
|
3705
|
+
m.eventMessage.name = message.event.name;
|
|
3706
|
+
m.eventMessage.description = message.event.description;
|
|
3707
|
+
m.eventMessage.startTime = startTime;
|
|
3708
|
+
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
3709
|
+
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
3710
|
+
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
3711
|
+
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
3712
|
+
m.eventMessage.location = message.event.location;
|
|
3713
|
+
}
|
|
3714
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'poll')) {
|
|
3715
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
3716
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
3717
|
+
if (!Array.isArray(message.poll.values)) {
|
|
3718
|
+
throw new index_js_2.HttpError('Invalid poll values', { statusCode: 400 });
|
|
3719
|
+
}
|
|
3720
|
+
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
3721
|
+
throw new index_js_2.HttpError(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
3722
|
+
statusCode: 400
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
const pollCreationMessage = {
|
|
3726
|
+
name: message.poll.name,
|
|
3727
|
+
selectableOptionsCount: message.poll.selectableCount,
|
|
3728
|
+
options: message.poll.values.map(optionName => ({ optionName })),
|
|
3729
|
+
endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
|
|
3730
|
+
hideParticipantName: message.poll.hideVoter ?? false,
|
|
3731
|
+
allowAddOption: message.poll.canAddOption ?? false
|
|
3732
|
+
};
|
|
3733
|
+
if (message.poll.toAnnouncementGroup) {
|
|
3734
|
+
// poll v2 is for community announcement groups (single select and multiple)
|
|
3735
|
+
m.pollCreationMessageV2 = pollCreationMessage;
|
|
3736
|
+
}
|
|
3737
|
+
else {
|
|
3738
|
+
// Kage: actualización de compatibilidad.
|
|
3739
|
+
if (message.poll.pollType === 1) {
|
|
3740
|
+
if (!message.poll.correctAnswer) {
|
|
3741
|
+
throw new index_js_2.HttpError('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
3742
|
+
}
|
|
3743
|
+
m.pollCreationMessageV5 = {
|
|
3744
|
+
// Kage: nota de compatibilidad.
|
|
3745
|
+
...pollCreationMessage,
|
|
3746
|
+
correctAnswer: {
|
|
3747
|
+
optionName: message.poll.correctAnswer.toString()
|
|
3748
|
+
},
|
|
3749
|
+
pollType: 1,
|
|
3750
|
+
selectableOptionsCount: 1
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
else if (message.poll.selectableCount === 1) {
|
|
3754
|
+
//poll v3 is for single select polls
|
|
3755
|
+
m.pollCreationMessageV3 = pollCreationMessage;
|
|
3756
|
+
}
|
|
3757
|
+
else {
|
|
3758
|
+
// poll for multiple choice polls
|
|
3759
|
+
m.pollCreationMessage = pollCreationMessage;
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
m.messageContextInfo = {
|
|
3763
|
+
// encKey
|
|
3764
|
+
messageSecret: message.poll.messageSecret || (0, node_crypto_1.randomBytes)(32)
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
// Kage: actualización de compatibilidad.
|
|
3768
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'pollResult')) {
|
|
3769
|
+
const pollResultSnapshotMessage = {
|
|
3770
|
+
name: message.pollResult.name,
|
|
3771
|
+
pollVotes: message.pollResult.votes.map(vote => ({
|
|
3772
|
+
optionName: vote.name,
|
|
3773
|
+
optionVoteCount: parseInt(vote.voteCount)
|
|
3774
|
+
}))
|
|
3775
|
+
};
|
|
3776
|
+
if (message.pollResult.pollType === 1) {
|
|
3777
|
+
pollResultSnapshotMessage.pollType = index_js_1.proto.Message.PollType.QUIZ;
|
|
3778
|
+
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
3779
|
+
}
|
|
3780
|
+
else {
|
|
3781
|
+
pollResultSnapshotMessage.pollType = index_js_1.proto.Message.PollType.POLL;
|
|
3782
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
// Kage: actualización de compatibilidad.
|
|
3786
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'pollUpdate')) {
|
|
3787
|
+
if (!message.pollUpdate.key) {
|
|
3788
|
+
throw new index_js_2.HttpError('Message key is required', { statusCode: 400 });
|
|
3789
|
+
}
|
|
3790
|
+
if (!message.pollUpdate.vote) {
|
|
3791
|
+
throw new index_js_2.HttpError('Encrypted vote payload is required', { statusCode: 400 });
|
|
3792
|
+
}
|
|
3793
|
+
m.pollUpdateMessage = {
|
|
3794
|
+
metadata: message.pollUpdate.metadata,
|
|
3795
|
+
pollCreationMessageKey: message.pollUpdate.key,
|
|
3796
|
+
senderTimestampMs: Date.now(),
|
|
3797
|
+
vote: message.pollUpdate.vote
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
// Kage: actualización de compatibilidad.
|
|
3801
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'paymentInviteServiceType')) {
|
|
3802
|
+
m.paymentInviteMessage = {
|
|
3803
|
+
expiryTimestamp: Date.now(),
|
|
3804
|
+
serviceType: message.paymentInviteServiceType
|
|
3805
|
+
};
|
|
3806
|
+
}
|
|
3807
|
+
// Kage: actualización de compatibilidad.
|
|
3808
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'orderText')) {
|
|
3809
|
+
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
3810
|
+
throw new index_js_2.HttpError('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
3811
|
+
}
|
|
3812
|
+
m.orderMessage = {
|
|
3813
|
+
itemCount: 1,
|
|
3814
|
+
messageVersion: 1,
|
|
3815
|
+
orderTitle: index_js_3.LIBRARY_NAME,
|
|
3816
|
+
status: index_js_1.proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
3817
|
+
surface: index_js_1.proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
3818
|
+
token: (0, generics_js_1.generateMessageIDV2)(),
|
|
3819
|
+
totalAmount1000: 1000,
|
|
3820
|
+
totalCurrencyCode: 'IDR',
|
|
3821
|
+
...message,
|
|
3822
|
+
message: message.orderText
|
|
3823
|
+
};
|
|
3824
|
+
delete m.orderMessage.orderText;
|
|
3825
|
+
}
|
|
3826
|
+
// Kage: actualización de compatibilidad.
|
|
3827
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'album')) {
|
|
3828
|
+
if (!Array.isArray(message.album)) {
|
|
3829
|
+
throw new index_js_2.HttpError('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
3830
|
+
}
|
|
3831
|
+
let videoCount = 0;
|
|
3832
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
3833
|
+
if (message.album[i].video)
|
|
3834
|
+
videoCount++;
|
|
3835
|
+
}
|
|
3836
|
+
;
|
|
3837
|
+
let imageCount = 0;
|
|
3838
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
3839
|
+
if (message.album[i].image)
|
|
3840
|
+
imageCount++;
|
|
3841
|
+
}
|
|
3842
|
+
;
|
|
3843
|
+
if ((videoCount + imageCount) < 2) {
|
|
3844
|
+
throw new index_js_2.HttpError('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
3845
|
+
}
|
|
3846
|
+
m.albumMessage = {
|
|
3847
|
+
expectedImageCount: imageCount,
|
|
3848
|
+
expectedVideoCount: videoCount
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
|
|
3852
|
+
m.protocolMessage = {
|
|
3853
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
|
|
3857
|
+
m.requestPhoneNumberMessage = {};
|
|
3858
|
+
}
|
|
3859
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
|
|
3860
|
+
m.protocolMessage = {
|
|
3861
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
3862
|
+
limitSharing: {
|
|
3863
|
+
sharingLimited: message.limitSharing === true,
|
|
3864
|
+
trigger: 1,
|
|
3865
|
+
limitSharingSettingTimestamp: Date.now(),
|
|
3866
|
+
initiatedByMe: true
|
|
3867
|
+
}
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
else {
|
|
3871
|
+
m = await (0, exports.prepareWAMessageMedia)(message, options);
|
|
3872
|
+
}
|
|
3873
|
+
// Kage: actualización de compatibilidad.
|
|
3874
|
+
if ((0, exports.hasNonNullishProperty)(message, 'buttons')) {
|
|
3875
|
+
const buttonsMessage = {
|
|
3876
|
+
buttons: message.buttons.map(button => {
|
|
3877
|
+
// Kage: actualización de compatibilidad.
|
|
3878
|
+
const buttonText = button.text || button.buttonText;
|
|
3879
|
+
if ((0, exports.hasOptionalProperty)(button, 'sections')) {
|
|
3880
|
+
return {
|
|
3881
|
+
nativeFlowInfo: {
|
|
3882
|
+
name: 'single_select',
|
|
3883
|
+
paramsJson: JSON.stringify({
|
|
3884
|
+
title: buttonText,
|
|
3885
|
+
sections: button.sections
|
|
3886
|
+
})
|
|
3887
|
+
},
|
|
3888
|
+
type: index_js_4.ButtonType.NATIVE_FLOW
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
else if ((0, exports.hasOptionalProperty)(button, 'name')) {
|
|
3892
|
+
return {
|
|
3893
|
+
nativeFlowInfo: {
|
|
3894
|
+
name: button.name,
|
|
3895
|
+
paramsJson: button.paramsJson
|
|
3896
|
+
},
|
|
3897
|
+
type: index_js_4.ButtonType.NATIVE_FLOW
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
return {
|
|
3901
|
+
buttonId: button.id || button.buttonId,
|
|
3902
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
3903
|
+
type: button.type || index_js_4.ButtonType.RESPONSE
|
|
3904
|
+
};
|
|
3905
|
+
})
|
|
3906
|
+
};
|
|
3907
|
+
if ((0, exports.hasOptionalProperty)(message, 'text')) {
|
|
3908
|
+
buttonsMessage.contentText = message.text;
|
|
3909
|
+
buttonsMessage.headerType = index_js_4.ButtonHeaderType.EMPTY;
|
|
3910
|
+
}
|
|
3911
|
+
else {
|
|
3912
|
+
if ((0, exports.hasOptionalProperty)(message, 'caption')) {
|
|
3913
|
+
buttonsMessage.contentText = message.caption;
|
|
3914
|
+
}
|
|
3915
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
3916
|
+
buttonsMessage.headerType = index_js_4.ButtonHeaderType[type];
|
|
3917
|
+
Object.assign(buttonsMessage, m);
|
|
3918
|
+
}
|
|
3919
|
+
if ((0, exports.hasOptionalProperty)(message, 'footer')) {
|
|
3920
|
+
buttonsMessage.footerText = message.footer;
|
|
3921
|
+
}
|
|
3922
|
+
m = { buttonsMessage };
|
|
3923
|
+
}
|
|
3924
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'sections')) {
|
|
3925
|
+
const listMessage = {
|
|
3926
|
+
sections: message.sections,
|
|
3927
|
+
buttonText: message.buttonText,
|
|
3928
|
+
title: message.title,
|
|
3929
|
+
footerText: message.footer,
|
|
3930
|
+
description: message.text,
|
|
3931
|
+
listType: index_js_4.ListType.SINGLE_SELECT
|
|
3932
|
+
};
|
|
3933
|
+
m = { listMessage };
|
|
3934
|
+
}
|
|
3935
|
+
// Kage: nota de compatibilidad.
|
|
3936
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'templateButtons')) {
|
|
3937
|
+
const hydratedTemplate = {
|
|
3938
|
+
hydratedButtons: message.templateButtons.map((button, i) => {
|
|
3939
|
+
const buttonText = button.text || button.buttonText;
|
|
3940
|
+
if ((0, exports.hasOptionalProperty)(button, 'id')) {
|
|
3941
|
+
return {
|
|
3942
|
+
index: i,
|
|
3943
|
+
quickReplyButton: {
|
|
3944
|
+
displayText: buttonText || '👉🏻 Click',
|
|
3945
|
+
id: button.id
|
|
3946
|
+
}
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
else if ((0, exports.hasOptionalProperty)(button, 'url')) {
|
|
3950
|
+
return {
|
|
3951
|
+
index: i,
|
|
3952
|
+
urlButton: {
|
|
3953
|
+
displayText: buttonText || '🌐 Visit',
|
|
3954
|
+
url: button.url
|
|
3955
|
+
}
|
|
3956
|
+
};
|
|
3957
|
+
}
|
|
3958
|
+
else if ((0, exports.hasOptionalProperty)(button, 'call')) {
|
|
3959
|
+
return {
|
|
3960
|
+
index: i,
|
|
3961
|
+
callButton: {
|
|
3962
|
+
displayText: buttonText || '📞 Call',
|
|
3963
|
+
phoneNumber: button.call
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
button.index = button.index || i;
|
|
3968
|
+
return button;
|
|
3969
|
+
})
|
|
3970
|
+
};
|
|
3971
|
+
if ((0, exports.hasOptionalProperty)(message, 'text')) {
|
|
3972
|
+
hydratedTemplate.hydratedContentText = message.text;
|
|
3973
|
+
}
|
|
3974
|
+
else {
|
|
3975
|
+
if ((0, exports.hasOptionalProperty)(message, 'caption')) {
|
|
3976
|
+
hydratedTemplate.hydratedTitleText = message.title;
|
|
3977
|
+
hydratedTemplate.hydratedContentText = message.caption;
|
|
3978
|
+
}
|
|
3979
|
+
;
|
|
3980
|
+
Object.assign(hydratedTemplate, m);
|
|
3981
|
+
}
|
|
3982
|
+
if ((0, exports.hasOptionalProperty)(message, 'footer')) {
|
|
3983
|
+
hydratedTemplate.hydratedFooterText = message.footer;
|
|
3984
|
+
}
|
|
3985
|
+
hydratedTemplate.templateId = message.id || 'template-' + Date.now(); // Kage: nota de compatibilidad.
|
|
3986
|
+
m = {
|
|
3987
|
+
templateMessage: {
|
|
3988
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
3989
|
+
hydratedTemplate: hydratedTemplate
|
|
3990
|
+
}
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'nativeFlow')) {
|
|
3994
|
+
const interactiveMessage = {
|
|
3995
|
+
nativeFlowMessage: prepareNativeFlowButtons(message)
|
|
3996
|
+
};
|
|
3997
|
+
if ((0, exports.hasOptionalProperty)(message, 'bizJid')) {
|
|
3998
|
+
interactiveMessage.collectionMessage = {
|
|
3999
|
+
bizJid: message.bizJid,
|
|
4000
|
+
id: message.id,
|
|
4001
|
+
messageVersion: 1
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
else if ((0, exports.hasOptionalProperty)(message, 'shopSurface')) {
|
|
4005
|
+
interactiveMessage.shopStorefrontMessage = {
|
|
4006
|
+
surface: message.shopSurface,
|
|
4007
|
+
id: message.id,
|
|
4008
|
+
messageVersion: 1
|
|
4009
|
+
};
|
|
4010
|
+
}
|
|
4011
|
+
if ((0, exports.hasOptionalProperty)(message, 'text')) {
|
|
4012
|
+
interactiveMessage.body = { text: message.text };
|
|
4013
|
+
}
|
|
4014
|
+
else {
|
|
4015
|
+
if ((0, exports.hasOptionalProperty)(message, 'caption')) {
|
|
4016
|
+
const isValidHeader = (0, exports.hasValidInteractiveHeader)(m);
|
|
4017
|
+
if (!isValidHeader) {
|
|
4018
|
+
throw new index_js_2.HttpError('Invalid media type for interactive message header', { statusCode: 400 });
|
|
4019
|
+
}
|
|
4020
|
+
interactiveMessage.header = {
|
|
4021
|
+
title: message.title || '',
|
|
4022
|
+
subtitle: message.subtitle || '',
|
|
4023
|
+
hasMediaAttachment: isValidHeader
|
|
4024
|
+
};
|
|
4025
|
+
interactiveMessage.body = { text: message.caption };
|
|
4026
|
+
}
|
|
4027
|
+
if ((0, exports.hasOptionalProperty)(message, 'thumbnail') && !!message.thumbnail) {
|
|
4028
|
+
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
4029
|
+
}
|
|
4030
|
+
Object.assign(interactiveMessage.header, m);
|
|
4031
|
+
}
|
|
4032
|
+
if ((0, exports.hasOptionalProperty)(message, 'audioFooter')) {
|
|
4033
|
+
const { audioMessage } = await (0, exports.prepareWAMessageMedia)({
|
|
4034
|
+
audio: message.audioFooter
|
|
4035
|
+
}, options);
|
|
4036
|
+
interactiveMessage.footer = {
|
|
4037
|
+
audioMessage,
|
|
4038
|
+
hasMediaAttachment: true
|
|
4039
|
+
};
|
|
4040
|
+
}
|
|
4041
|
+
else if ((0, exports.hasOptionalProperty)(message, 'footer')) {
|
|
4042
|
+
interactiveMessage.footer = { text: message.footer };
|
|
4043
|
+
}
|
|
4044
|
+
m = { interactiveMessage };
|
|
4045
|
+
}
|
|
4046
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'cards')) {
|
|
4047
|
+
const interactiveMessage = {
|
|
4048
|
+
carouselMessage: {
|
|
4049
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
4050
|
+
let carouselHeader = {};
|
|
4051
|
+
if ((0, exports.hasNonNullishProperty)(card, 'product')) {
|
|
4052
|
+
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
4053
|
+
}
|
|
4054
|
+
else {
|
|
4055
|
+
carouselHeader = await (0, exports.prepareWAMessageMedia)(card, options).catch(() => ({}));
|
|
4056
|
+
}
|
|
4057
|
+
const isValidHeader = (0, exports.hasValidCarouselHeader)(carouselHeader);
|
|
4058
|
+
if (!isValidHeader) {
|
|
4059
|
+
throw new index_js_2.HttpError('Invalid media type for carousel card', { statusCode: 400 });
|
|
4060
|
+
}
|
|
4061
|
+
const carouselCard = {
|
|
4062
|
+
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
4063
|
+
};
|
|
4064
|
+
if ((0, exports.hasOptionalProperty)(card, 'text')) {
|
|
4065
|
+
carouselCard.body = { text: card.text };
|
|
4066
|
+
}
|
|
4067
|
+
else {
|
|
4068
|
+
if ((0, exports.hasOptionalProperty)(card, 'caption')) {
|
|
4069
|
+
carouselCard.header = {
|
|
4070
|
+
title: card.title || '',
|
|
4071
|
+
subtitle: card.subtitle || '',
|
|
4072
|
+
hasMediaAttachment: isValidHeader
|
|
4073
|
+
};
|
|
4074
|
+
carouselCard.body = { text: card.caption };
|
|
4075
|
+
}
|
|
4076
|
+
if ((0, exports.hasOptionalProperty)(card, 'thumbnail') && !!card.thumbnail) {
|
|
4077
|
+
carouselCard.jpegThumbnail = card.thumbnail;
|
|
4078
|
+
}
|
|
4079
|
+
Object.assign(carouselCard.header, carouselHeader);
|
|
4080
|
+
}
|
|
4081
|
+
if ((0, exports.hasOptionalProperty)(card, 'audioFooter')) {
|
|
4082
|
+
const { audioMessage } = await (0, exports.prepareWAMessageMedia)({
|
|
4083
|
+
audio: card.audioFooter
|
|
4084
|
+
}, options);
|
|
4085
|
+
carouselCard.footer = {
|
|
4086
|
+
audioMessage,
|
|
4087
|
+
hasMediaAttachment: true
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
else if ((0, exports.hasOptionalProperty)(card, 'footer')) {
|
|
4091
|
+
carouselCard.footer = { text: card.footer };
|
|
4092
|
+
}
|
|
4093
|
+
return carouselCard;
|
|
4094
|
+
})),
|
|
4095
|
+
carouselCardType: index_js_4.CarouselCardType.UNKNOWN,
|
|
4096
|
+
messageVersion: 1
|
|
4097
|
+
}
|
|
4098
|
+
};
|
|
4099
|
+
if ((0, exports.hasOptionalProperty)(message, 'text')) {
|
|
4100
|
+
interactiveMessage.body = { text: message.text };
|
|
4101
|
+
}
|
|
4102
|
+
if ((0, exports.hasOptionalProperty)(message, 'footer')) {
|
|
4103
|
+
interactiveMessage.footer = { text: message.footer };
|
|
4104
|
+
}
|
|
4105
|
+
m = { interactiveMessage };
|
|
4106
|
+
}
|
|
4107
|
+
// Kage: actualización de compatibilidad.
|
|
4108
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'requestPaymentFrom')) {
|
|
4109
|
+
const requestPaymentMessage = {
|
|
4110
|
+
amount: {
|
|
4111
|
+
currencyCode: 'IDR',
|
|
4112
|
+
offset: 1000,
|
|
4113
|
+
value: 1000
|
|
4114
|
+
},
|
|
4115
|
+
amount1000: 1000,
|
|
4116
|
+
currencyCodeIso4217: 'IDR',
|
|
4117
|
+
expiryTimestamp: Date.now(),
|
|
4118
|
+
noteMessage: m,
|
|
4119
|
+
requestFrom: message.requestPaymentFrom,
|
|
4120
|
+
...message
|
|
4121
|
+
};
|
|
4122
|
+
delete requestPaymentMessage.requestPaymentFrom;
|
|
4123
|
+
if ((0, exports.hasNonNullishProperty)(m, 'extendedTextMessage') || (0, exports.hasNonNullishProperty)(m, 'stickerMessage')) {
|
|
4124
|
+
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
4125
|
+
}
|
|
4126
|
+
else {
|
|
4127
|
+
throw new index_js_2.HttpError('Invalid message type for request payment note message', { statusCode: 400 });
|
|
4128
|
+
}
|
|
4129
|
+
m = { requestPaymentMessage };
|
|
4130
|
+
}
|
|
4131
|
+
// Kage: actualización de compatibilidad.
|
|
4132
|
+
else if ((0, exports.hasNonNullishProperty)(message, 'invoiceNote')) {
|
|
4133
|
+
const attachment = m.imageMessage || m.documentMessage;
|
|
4134
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
4135
|
+
const invoiceMessage = {
|
|
4136
|
+
attachmentType: index_js_1.proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
4137
|
+
note: message.invoiceNote
|
|
4138
|
+
};
|
|
4139
|
+
if (attachment) {
|
|
4140
|
+
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
4141
|
+
Object.assign(invoiceMessage, {
|
|
4142
|
+
attachmentDirectPath: directPath,
|
|
4143
|
+
attachmentFileEncSha256: fileEncSha256,
|
|
4144
|
+
attachmentFileSha256: fileSha256,
|
|
4145
|
+
attachmentJpegThumbnail: jpegThumbnail,
|
|
4146
|
+
attachmentMediaKey: mediaKey,
|
|
4147
|
+
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
4148
|
+
attachmentMimetype: mimetype,
|
|
4149
|
+
token: (0, generics_js_1.generateMessageIDV2)()
|
|
4150
|
+
});
|
|
4151
|
+
}
|
|
4152
|
+
else {
|
|
4153
|
+
throw new index_js_2.HttpError('Invalid media type for invoice message', { statusCode: 400 });
|
|
4154
|
+
}
|
|
4155
|
+
m = { invoiceMessage };
|
|
4156
|
+
}
|
|
4157
|
+
// Kage: actualización de compatibilidad.
|
|
4158
|
+
if ((0, exports.hasOptionalProperty)(message, 'externalAdReply') && !!message.externalAdReply) {
|
|
4159
|
+
const messageType = Object.keys(m)[0];
|
|
4160
|
+
const key = m[messageType];
|
|
4161
|
+
const content = message.externalAdReply;
|
|
4162
|
+
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
4163
|
+
throw new index_js_2.HttpError('Thumbnail must in buffer type', { statusCode: 400 });
|
|
4164
|
+
}
|
|
4165
|
+
if (!content.url || typeof content.url !== 'string') {
|
|
4166
|
+
content.url = index_js_3.DONATE_URL; // Kage: nota de compatibilidad.
|
|
4167
|
+
}
|
|
4168
|
+
const externalAdReply = {
|
|
4169
|
+
...content,
|
|
4170
|
+
body: content.body,
|
|
4171
|
+
mediaType: content.mediaType || 1,
|
|
4172
|
+
mediaUrl: content.url,
|
|
4173
|
+
renderLargerThumbnail: content.largeThumbnail,
|
|
4174
|
+
sourceUrl: content.url,
|
|
4175
|
+
thumbnail: content.thumbnail,
|
|
4176
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
4177
|
+
title: content.title || index_js_3.LIBRARY_NAME
|
|
4178
|
+
};
|
|
4179
|
+
delete externalAdReply.subTitle;
|
|
4180
|
+
delete externalAdReply.largeThumbnail;
|
|
4181
|
+
delete externalAdReply.url;
|
|
4182
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
4183
|
+
key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
|
|
4184
|
+
}
|
|
4185
|
+
else if (key) {
|
|
4186
|
+
key.contextInfo = { externalAdReply };
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
if (((0, exports.hasOptionalProperty)(message, 'mentions') && message.mentions?.length) ||
|
|
4190
|
+
((0, exports.hasOptionalProperty)(message, 'mentionAll') && message.mentionAll)) {
|
|
4191
|
+
const messageType = Object.keys(m)[0];
|
|
4192
|
+
const key = m[messageType];
|
|
4193
|
+
if (key && 'contextInfo' in key) {
|
|
4194
|
+
key.contextInfo = key.contextInfo || {};
|
|
4195
|
+
if (message.mentions?.length) {
|
|
4196
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
4197
|
+
}
|
|
4198
|
+
if (message.mentionAll) {
|
|
4199
|
+
key.contextInfo.nonJidMentions = 1;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
else if (key) {
|
|
4203
|
+
key.contextInfo = {
|
|
4204
|
+
mentionedJid: message.mentions,
|
|
4205
|
+
nonJidMentions: message.mentionAll ? 1 : 0
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
if ((0, exports.hasOptionalProperty)(message, 'contextInfo') && !!message.contextInfo) {
|
|
4210
|
+
const messageType = Object.keys(m)[0];
|
|
4211
|
+
const key = m[messageType];
|
|
4212
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
4213
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
4214
|
+
}
|
|
4215
|
+
else if (key) {
|
|
4216
|
+
key.contextInfo = message.contextInfo;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
// Kage: actualización de compatibilidad.
|
|
4220
|
+
if ((0, exports.hasOptionalProperty)(message, 'groupStatus') && !!message.groupStatus) {
|
|
4221
|
+
const messageType = Object.keys(m)[0];
|
|
4222
|
+
const key = m[messageType];
|
|
4223
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
4224
|
+
key.contextInfo.isGroupStatus = message.groupStatus;
|
|
4225
|
+
}
|
|
4226
|
+
else if (key) {
|
|
4227
|
+
key.contextInfo = {
|
|
4228
|
+
isGroupStatus: message.groupStatus
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
m = { groupStatusMessageV2: { message: m } };
|
|
4232
|
+
delete message.groupStatus;
|
|
4233
|
+
}
|
|
4234
|
+
// Kage: actualización de compatibilidad.
|
|
4235
|
+
if ((0, exports.hasOptionalProperty)(message, 'spoiler') && !!message.spoiler) {
|
|
4236
|
+
const messageType = Object.keys(m)[0];
|
|
4237
|
+
const key = m[messageType];
|
|
4238
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
4239
|
+
key.contextInfo.isSpoiler = message.spoiler;
|
|
4240
|
+
}
|
|
4241
|
+
else if (key) {
|
|
4242
|
+
key.contextInfo = {
|
|
4243
|
+
isSpoiler: message.spoiler
|
|
4244
|
+
};
|
|
4245
|
+
}
|
|
4246
|
+
m = { spoilerMessage: { message: m } };
|
|
4247
|
+
delete message.spoiler;
|
|
4248
|
+
}
|
|
4249
|
+
// Kage: actualización de compatibilidad.
|
|
4250
|
+
else if ((0, exports.hasOptionalProperty)(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
4251
|
+
if (!m.interactiveMessage) {
|
|
4252
|
+
throw new index_js_2.HttpError('Invalid message type for template', { statusCode: 400 }); // Kage: nota de compatibilidad.
|
|
4253
|
+
}
|
|
4254
|
+
m = {
|
|
4255
|
+
templateMessage: {
|
|
4256
|
+
interactiveMessageTemplate: m.interactiveMessage,
|
|
4257
|
+
templateId: message.id || 'template-' + Date.now() // Kage: nota de compatibilidad.
|
|
4258
|
+
}
|
|
4259
|
+
};
|
|
4260
|
+
delete message.interactiveAsTemplate;
|
|
4261
|
+
}
|
|
4262
|
+
// Kage: actualización de compatibilidad.
|
|
4263
|
+
if ((0, exports.hasOptionalProperty)(message, 'ephemeral') && !!message.ephemeral) {
|
|
4264
|
+
m = { ephemeralMessage: { message: m } };
|
|
4265
|
+
delete message.ephemeral;
|
|
4266
|
+
}
|
|
4267
|
+
// Kage: actualización de compatibilidad.
|
|
4268
|
+
if ((0, exports.hasOptionalProperty)(message, 'isLottie') && !!message.isLottie) {
|
|
4269
|
+
m = { lottieStickerMessage: { message: m } };
|
|
4270
|
+
}
|
|
4271
|
+
else if ((0, exports.hasOptionalProperty)(message, 'viewOnce') && !!message.viewOnce) {
|
|
4272
|
+
(0, exports.markViewOnce)(m);
|
|
4273
|
+
m = { viewOnceMessage: { message: m } };
|
|
4274
|
+
}
|
|
4275
|
+
// Kage: actualización de compatibilidad.
|
|
4276
|
+
else if ((0, exports.hasOptionalProperty)(message, 'viewOnceV2') && !!message.viewOnceV2) {
|
|
4277
|
+
(0, exports.markViewOnce)(m);
|
|
4278
|
+
m = { viewOnceMessageV2: { message: m } };
|
|
4279
|
+
delete message.viewOnceV2;
|
|
4280
|
+
}
|
|
4281
|
+
// Kage: actualización de compatibilidad.
|
|
4282
|
+
else if ((0, exports.hasOptionalProperty)(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
|
|
4283
|
+
(0, exports.markViewOnce)(m);
|
|
4284
|
+
m = { viewOnceMessageV2Extension: { message: m } };
|
|
4285
|
+
delete message.viewOnceV2Extension;
|
|
4286
|
+
}
|
|
4287
|
+
if ((0, exports.hasOptionalProperty)(message, 'edit')) {
|
|
4288
|
+
m = {
|
|
4289
|
+
protocolMessage: {
|
|
4290
|
+
key: message.edit,
|
|
4291
|
+
editedMessage: m,
|
|
4292
|
+
timestampMs: Date.now(),
|
|
4293
|
+
type: index_js_4.WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
4294
|
+
}
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
if ((0, reporting_utils_js_1.shouldIncludeReportingToken)(m)) {
|
|
4298
|
+
m.messageContextInfo = m.messageContextInfo || {};
|
|
4299
|
+
if (!m.messageContextInfo.messageSecret) {
|
|
4300
|
+
m.messageContextInfo.messageSecret = (0, node_crypto_1.randomBytes)(32);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
return ensureMessageProto(m);
|
|
4304
|
+
};
|
|
4305
|
+
exports.generateWAMessageContent = generateWAMessageContent;
|
|
4306
|
+
const generateWAMessageFromContent = (jid, message, options) => {
|
|
4307
|
+
// set timestamp to now
|
|
4308
|
+
// if not specified
|
|
4309
|
+
if (!options.timestamp) {
|
|
4310
|
+
options.timestamp = new Date();
|
|
4311
|
+
}
|
|
4312
|
+
const innerMessage = (0, exports.normalizeMessageContent)(message);
|
|
4313
|
+
const messageContextInfo = message.messageContextInfo;
|
|
4314
|
+
const key = (0, exports.getContentType)(innerMessage);
|
|
4315
|
+
const timestamp = (0, generics_js_1.unixTimestampSeconds)(options.timestamp);
|
|
4316
|
+
const isNewsletter = (0, index_js_5.isJidNewsletter)(jid);
|
|
4317
|
+
const { quoted, userJid } = options;
|
|
4318
|
+
if (quoted) {
|
|
4319
|
+
const participant = quoted.key.fromMe
|
|
4320
|
+
? userJid // TODO: Add support for LIDs
|
|
4321
|
+
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
4322
|
+
let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message);
|
|
4323
|
+
const msgType = (0, exports.getContentType)(quotedMsg);
|
|
4324
|
+
// strip any redundant properties
|
|
4325
|
+
quotedMsg = index_js_1.proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
4326
|
+
const quotedContent = quotedMsg[msgType];
|
|
4327
|
+
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
4328
|
+
delete quotedContent.contextInfo;
|
|
4329
|
+
}
|
|
4330
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
4331
|
+
contextInfo.participant = (0, index_js_5.jidNormalizedUser)(participant);
|
|
4332
|
+
contextInfo.stanzaId = quoted.key.id;
|
|
4333
|
+
contextInfo.quotedMessage = quotedMsg;
|
|
4334
|
+
// if a participant is quoted, then it must be a group
|
|
4335
|
+
// hence, remoteJid of group must also be entered
|
|
4336
|
+
if (!isNewsletter && jid !== quoted.key.remoteJid) {
|
|
4337
|
+
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
4338
|
+
}
|
|
4339
|
+
if (contextInfo && innerMessage[key]) {
|
|
4340
|
+
/* @ts-ignore */
|
|
4341
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
if (
|
|
4345
|
+
// if we want to send a disappearing message
|
|
4346
|
+
!!options?.ephemeralExpiration &&
|
|
4347
|
+
// and it's not a protocol message -- delete, toggle disappear message
|
|
4348
|
+
key !== 'protocolMessage' &&
|
|
4349
|
+
// already not converted to disappearing message
|
|
4350
|
+
key !== 'ephemeralMessage' &&
|
|
4351
|
+
// newsletters don't support ephemeral messages
|
|
4352
|
+
!isNewsletter) {
|
|
4353
|
+
/* @ts-ignore */
|
|
4354
|
+
innerMessage[key].contextInfo = {
|
|
4355
|
+
...(innerMessage[key].contextInfo || {}),
|
|
4356
|
+
expiration: options.ephemeralExpiration || index_js_3.WA_DEFAULT_EPHEMERAL
|
|
4357
|
+
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4360
|
+
// Kage: actualización de compatibilidad.
|
|
4361
|
+
if (messageContextInfo?.messageSecret && ((0, index_js_5.isPnUser)(jid) || (0, index_js_5.isLidUser)(jid))) {
|
|
4362
|
+
messageContextInfo.deviceListMetadata = {
|
|
4363
|
+
recipientKeyHash: (0, node_crypto_1.randomBytes)(10),
|
|
4364
|
+
recipientTimestamp: (0, generics_js_1.unixTimestampSeconds)()
|
|
4365
|
+
};
|
|
4366
|
+
messageContextInfo.deviceListMetadataVersion = 2;
|
|
4367
|
+
}
|
|
4368
|
+
return index_js_4.WAProto.WebMessageInfo.create({
|
|
4369
|
+
key: {
|
|
4370
|
+
remoteJid: jid,
|
|
4371
|
+
fromMe: true,
|
|
4372
|
+
id: options?.messageId || (0, generics_js_1.generateMessageIDV2)()
|
|
4373
|
+
},
|
|
4374
|
+
message: ensureMessageProto(message),
|
|
4375
|
+
messageTimestamp: timestamp,
|
|
4376
|
+
messageStubParameters: [],
|
|
4377
|
+
participant: (0, index_js_5.isJidGroup)(jid) || (0, index_js_5.isJidStatusBroadcast)(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
4378
|
+
status: index_js_4.WAMessageStatus.PENDING
|
|
4379
|
+
});
|
|
4380
|
+
};
|
|
4381
|
+
exports.generateWAMessageFromContent = generateWAMessageFromContent;
|
|
4382
|
+
const generateWAMessage = async (jid, content, options) => {
|
|
4383
|
+
// ensure msg ID is with every log
|
|
4384
|
+
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
4385
|
+
// Pass jid in the options to generateWAMessageContent
|
|
4386
|
+
if (jid) {
|
|
4387
|
+
options.jid = jid;
|
|
4388
|
+
}
|
|
4389
|
+
return (0, exports.generateWAMessageFromContent)(jid, await (0, exports.generateWAMessageContent)(content, options), options);
|
|
4390
|
+
};
|
|
4391
|
+
exports.generateWAMessage = generateWAMessage;
|
|
4392
|
+
/** Get the key to access the true type of content */
|
|
4393
|
+
const getContentType = (content) => {
|
|
4394
|
+
if (content) {
|
|
4395
|
+
const keys = Object.keys(content);
|
|
4396
|
+
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
4397
|
+
return key;
|
|
4398
|
+
}
|
|
4399
|
+
};
|
|
4400
|
+
exports.getContentType = getContentType;
|
|
4401
|
+
/**
|
|
4402
|
+
* Normalizes ephemeral, view once messages to regular message content
|
|
4403
|
+
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
4404
|
+
* @param content
|
|
4405
|
+
* @returns
|
|
4406
|
+
*/
|
|
4407
|
+
const normalizeMessageContent = (content) => {
|
|
4408
|
+
if (!content) {
|
|
4409
|
+
return undefined;
|
|
4410
|
+
}
|
|
4411
|
+
// set max iterations to prevent an infinite loop
|
|
4412
|
+
for (let i = 0; i < 5; i++) {
|
|
4413
|
+
const inner = getFutureProofMessage(content);
|
|
4414
|
+
if (!inner) {
|
|
4415
|
+
break;
|
|
4416
|
+
}
|
|
4417
|
+
content = inner.message;
|
|
4418
|
+
}
|
|
4419
|
+
return content;
|
|
4420
|
+
// Kage: actualización de compatibilidad.
|
|
4421
|
+
function getFutureProofMessage(message) {
|
|
4422
|
+
return (message?.associatedChildMessage ||
|
|
4423
|
+
message?.botForwardedMessage ||
|
|
4424
|
+
message?.botInvokeMessage ||
|
|
4425
|
+
message?.botTaskMessage ||
|
|
4426
|
+
message?.documentWithCaptionMessage ||
|
|
4427
|
+
message?.editedMessage ||
|
|
4428
|
+
message?.ephemeralMessage ||
|
|
4429
|
+
message?.eventCoverImage ||
|
|
4430
|
+
message?.groupMentionedMessage ||
|
|
4431
|
+
message?.groupStatusMentionMessage ||
|
|
4432
|
+
message?.groupStatusMessage ||
|
|
4433
|
+
message?.groupStatusMessageV2 ||
|
|
4434
|
+
message?.limitSharingMessage ||
|
|
4435
|
+
message?.lottieStickerMessage ||
|
|
4436
|
+
message?.newsletterAdminProfileMessage ||
|
|
4437
|
+
message?.newsletterAdminProfileMessageV2 ||
|
|
4438
|
+
message?.newsletterAdminProfileStatusMessage ||
|
|
4439
|
+
message?.pollCreationMessageV4 ||
|
|
4440
|
+
message?.pollCreationOptionImageMessage ||
|
|
4441
|
+
message?.questionMessage ||
|
|
4442
|
+
message?.questionReplyMessage ||
|
|
4443
|
+
message?.spoilerMessage ||
|
|
4444
|
+
message?.statusAddYours ||
|
|
4445
|
+
message?.statusMentionMessage ||
|
|
4446
|
+
message?.viewOnceMessage ||
|
|
4447
|
+
message?.viewOnceMessageV2 ||
|
|
4448
|
+
message?.viewOnceMessageV2Extension);
|
|
4449
|
+
}
|
|
4450
|
+
};
|
|
4451
|
+
exports.normalizeMessageContent = normalizeMessageContent;
|
|
4452
|
+
/**
|
|
4453
|
+
* Extract the true message content from a message
|
|
4454
|
+
* Eg. extracts the inner message from a disappearing message/view once message
|
|
4455
|
+
*/
|
|
4456
|
+
const extractMessageContent = (content) => {
|
|
4457
|
+
const extractFromTemplateMessage = (msg) => {
|
|
4458
|
+
if (msg.imageMessage) {
|
|
4459
|
+
return { imageMessage: msg.imageMessage };
|
|
4460
|
+
}
|
|
4461
|
+
else if (msg.documentMessage) {
|
|
4462
|
+
return { documentMessage: msg.documentMessage };
|
|
4463
|
+
}
|
|
4464
|
+
else if (msg.videoMessage) {
|
|
4465
|
+
return { videoMessage: msg.videoMessage };
|
|
4466
|
+
}
|
|
4467
|
+
else if (msg.locationMessage) {
|
|
4468
|
+
return { locationMessage: msg.locationMessage };
|
|
4469
|
+
}
|
|
4470
|
+
else {
|
|
4471
|
+
return {
|
|
4472
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
4475
|
+
};
|
|
4476
|
+
content = (0, exports.normalizeMessageContent)(content);
|
|
4477
|
+
if (content?.buttonsMessage) {
|
|
4478
|
+
return extractFromTemplateMessage(content.buttonsMessage);
|
|
4479
|
+
}
|
|
4480
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
4481
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
4482
|
+
}
|
|
4483
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
4484
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
4485
|
+
}
|
|
4486
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
4487
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
4488
|
+
}
|
|
4489
|
+
return content;
|
|
4490
|
+
};
|
|
4491
|
+
exports.extractMessageContent = extractMessageContent;
|
|
4492
|
+
/**
|
|
4493
|
+
* Returns the device predicted by message ID
|
|
4494
|
+
*/
|
|
4495
|
+
const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
4496
|
+
? 'ios'
|
|
4497
|
+
: /^3E.{20}$/.test(id)
|
|
4498
|
+
? 'web'
|
|
4499
|
+
: /^(.{21}|.{32})$/.test(id)
|
|
4500
|
+
? 'android'
|
|
4501
|
+
: /^(3F|.{18}$)/.test(id)
|
|
4502
|
+
? 'desktop'
|
|
4503
|
+
: 'unknown';
|
|
4504
|
+
exports.getDevice = getDevice;
|
|
4505
|
+
/** Upserts a receipt in the message */
|
|
4506
|
+
const updateMessageWithReceipt = (msg, receipt) => {
|
|
4507
|
+
msg.userReceipt = msg.userReceipt || [];
|
|
4508
|
+
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
4509
|
+
if (recp) {
|
|
4510
|
+
Object.assign(recp, receipt);
|
|
4511
|
+
}
|
|
4512
|
+
else {
|
|
4513
|
+
msg.userReceipt.push(receipt);
|
|
4514
|
+
}
|
|
4515
|
+
};
|
|
4516
|
+
exports.updateMessageWithReceipt = updateMessageWithReceipt;
|
|
4517
|
+
/** Update the message with a new reaction */
|
|
4518
|
+
const updateMessageWithReaction = (msg, reaction) => {
|
|
4519
|
+
const authorID = (0, generics_js_1.getKeyAuthor)(reaction.key);
|
|
4520
|
+
const reactions = (msg.reactions || []).filter(r => (0, generics_js_1.getKeyAuthor)(r.key) !== authorID);
|
|
4521
|
+
reaction.text = reaction.text || '';
|
|
4522
|
+
reactions.push(reaction);
|
|
4523
|
+
msg.reactions = reactions;
|
|
4524
|
+
};
|
|
4525
|
+
exports.updateMessageWithReaction = updateMessageWithReaction;
|
|
4526
|
+
/** Update the message with a new poll update */
|
|
4527
|
+
const updateMessageWithPollUpdate = (msg, update) => {
|
|
4528
|
+
const authorID = (0, generics_js_1.getKeyAuthor)(update.pollUpdateMessageKey);
|
|
4529
|
+
const reactions = (msg.pollUpdates || []).filter(r => (0, generics_js_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
|
|
4530
|
+
if (update.vote?.selectedOptions?.length) {
|
|
4531
|
+
reactions.push(update);
|
|
4532
|
+
}
|
|
4533
|
+
msg.pollUpdates = reactions;
|
|
4534
|
+
};
|
|
4535
|
+
exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
|
|
4536
|
+
/** Update the message with a new event response */
|
|
4537
|
+
const updateMessageWithEventResponse = (msg, update) => {
|
|
4538
|
+
const authorID = (0, generics_js_1.getKeyAuthor)(update.eventResponseMessageKey);
|
|
4539
|
+
const responses = (msg.eventResponses || []).filter(r => (0, generics_js_1.getKeyAuthor)(r.eventResponseMessageKey) !== authorID);
|
|
4540
|
+
responses.push(update);
|
|
4541
|
+
msg.eventResponses = responses;
|
|
4542
|
+
};
|
|
4543
|
+
exports.updateMessageWithEventResponse = updateMessageWithEventResponse;
|
|
4544
|
+
/**
|
|
4545
|
+
* Aggregates all poll updates in a poll.
|
|
4546
|
+
* @param msg the poll creation message
|
|
4547
|
+
* @param meId your jid
|
|
4548
|
+
* @returns A list of options & their voters
|
|
4549
|
+
*/
|
|
4550
|
+
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
4551
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
4552
|
+
message?.pollCreationMessageV2?.options ||
|
|
4553
|
+
message?.pollCreationMessageV3?.options ||
|
|
4554
|
+
[];
|
|
4555
|
+
const voteHashMap = opts.reduce((acc, opt) => {
|
|
4556
|
+
const hash = (0, crypto_js_1.sha256)(Buffer.from(opt.optionName || '')).toString();
|
|
4557
|
+
acc[hash] = {
|
|
4558
|
+
name: opt.optionName || '',
|
|
4559
|
+
voters: []
|
|
4560
|
+
};
|
|
4561
|
+
return acc;
|
|
4562
|
+
}, {});
|
|
4563
|
+
for (const update of pollUpdates || []) {
|
|
4564
|
+
const { vote } = update;
|
|
4565
|
+
if (!vote) {
|
|
4566
|
+
continue;
|
|
4567
|
+
}
|
|
4568
|
+
for (const option of vote.selectedOptions || []) {
|
|
4569
|
+
const hash = option.toString();
|
|
4570
|
+
let data = voteHashMap[hash];
|
|
4571
|
+
if (!data) {
|
|
4572
|
+
voteHashMap[hash] = {
|
|
4573
|
+
name: 'Unknown',
|
|
4574
|
+
voters: []
|
|
4575
|
+
};
|
|
4576
|
+
data = voteHashMap[hash];
|
|
4577
|
+
}
|
|
4578
|
+
voteHashMap[hash].voters.push((0, generics_js_1.getKeyAuthor)(update.pollUpdateMessageKey, meId));
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
return Object.values(voteHashMap);
|
|
4582
|
+
}
|
|
4583
|
+
/**
|
|
4584
|
+
* Aggregates all event responses in an event message.
|
|
4585
|
+
* @param msg the event creation message
|
|
4586
|
+
* @param meId your jid
|
|
4587
|
+
* @returns A list of response types & their responders
|
|
4588
|
+
*/
|
|
4589
|
+
function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
4590
|
+
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
4591
|
+
const responseMap = {};
|
|
4592
|
+
for (const type of responseTypes) {
|
|
4593
|
+
responseMap[type] = {
|
|
4594
|
+
response: type,
|
|
4595
|
+
responders: []
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
for (const update of eventResponses || []) {
|
|
4599
|
+
const responseType = update.eventResponse || 'UNKNOWN';
|
|
4600
|
+
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
4601
|
+
responseMap[responseType].responders.push((0, generics_js_1.getKeyAuthor)(update.eventResponseMessageKey, meId));
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
return Object.values(responseMap);
|
|
4605
|
+
}
|
|
4606
|
+
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
4607
|
+
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
4608
|
+
const keyMap = {};
|
|
4609
|
+
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
4610
|
+
if (!fromMe) {
|
|
4611
|
+
const uqKey = `${remoteJid}:${participant || ''}`;
|
|
4612
|
+
if (!keyMap[uqKey]) {
|
|
4613
|
+
keyMap[uqKey] = {
|
|
4614
|
+
jid: remoteJid,
|
|
4615
|
+
participant: participant,
|
|
4616
|
+
messageIds: []
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
keyMap[uqKey].messageIds.push(id);
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
return Object.values(keyMap);
|
|
4623
|
+
};
|
|
4624
|
+
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
|
|
4625
|
+
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
4626
|
+
/**
|
|
4627
|
+
* Downloads the given message. Throws an error if it's not a media message
|
|
4628
|
+
*/
|
|
4629
|
+
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
4630
|
+
const result = await downloadMsg().catch(async (error) => {
|
|
4631
|
+
if (ctx &&
|
|
4632
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
4633
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
4634
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
4635
|
+
// request reupload
|
|
4636
|
+
message = await ctx.reuploadRequest(message);
|
|
4637
|
+
const result = await downloadMsg();
|
|
4638
|
+
return result;
|
|
4639
|
+
}
|
|
4640
|
+
throw error;
|
|
4641
|
+
});
|
|
4642
|
+
return result;
|
|
4643
|
+
async function downloadMsg() {
|
|
4644
|
+
const mContent = (0, exports.extractMessageContent)(message.message);
|
|
4645
|
+
if (!mContent) {
|
|
4646
|
+
throw new index_js_2.HttpError('No message present', { statusCode: 400, data: message });
|
|
4647
|
+
}
|
|
4648
|
+
const contentType = (0, exports.getContentType)(mContent);
|
|
4649
|
+
let mediaType = contentType?.replace('Message', '');
|
|
4650
|
+
const media = mContent[contentType];
|
|
4651
|
+
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
4652
|
+
throw new index_js_2.HttpError(`"${contentType}" message is not a media message`);
|
|
4653
|
+
}
|
|
4654
|
+
let download;
|
|
4655
|
+
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
4656
|
+
download = {
|
|
4657
|
+
directPath: media.thumbnailDirectPath,
|
|
4658
|
+
mediaKey: media.mediaKey
|
|
4659
|
+
};
|
|
4660
|
+
mediaType = 'thumbnail-link';
|
|
4661
|
+
}
|
|
4662
|
+
else {
|
|
4663
|
+
download = media;
|
|
4664
|
+
}
|
|
4665
|
+
const stream = await (0, messages_media_js_1.downloadContentFromMessage)(download, mediaType, options);
|
|
4666
|
+
if (type === 'buffer') {
|
|
4667
|
+
const bufferArray = [];
|
|
4668
|
+
for await (const chunk of stream) {
|
|
4669
|
+
bufferArray.push(chunk);
|
|
4670
|
+
}
|
|
4671
|
+
return Buffer.concat(bufferArray);
|
|
4672
|
+
}
|
|
4673
|
+
return stream;
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
exports.downloadMediaMessage = downloadMediaMessage;
|
|
4677
|
+
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
4678
|
+
const assertMediaContent = (content) => {
|
|
4679
|
+
content = (0, exports.extractMessageContent)(content);
|
|
4680
|
+
const mediaContent = content?.documentMessage ||
|
|
4681
|
+
content?.imageMessage ||
|
|
4682
|
+
content?.videoMessage ||
|
|
4683
|
+
content?.audioMessage ||
|
|
4684
|
+
content?.stickerMessage;
|
|
4685
|
+
if (!mediaContent) {
|
|
4686
|
+
throw new index_js_2.HttpError('given message is not a media message', { statusCode: 400, data: content });
|
|
4687
|
+
}
|
|
4688
|
+
return mediaContent;
|
|
4689
|
+
};
|
|
4690
|
+
exports.assertMediaContent = assertMediaContent;
|
|
4691
|
+
/**
|
|
4692
|
+
* Checks if a WebP buffer is animated by looking for VP8X chunk with animation flag
|
|
4693
|
+
* or ANIM/ANMF chunks
|
|
4694
|
+
*/
|
|
4695
|
+
const isAnimatedWebP = (buffer) => {
|
|
4696
|
+
// WebP must start with RIFF....WEBP
|
|
4697
|
+
if (buffer.length < 12 ||
|
|
4698
|
+
buffer[0] !== 0x52 ||
|
|
4699
|
+
buffer[1] !== 0x49 ||
|
|
4700
|
+
buffer[2] !== 0x46 ||
|
|
4701
|
+
buffer[3] !== 0x46 ||
|
|
4702
|
+
buffer[8] !== 0x57 ||
|
|
4703
|
+
buffer[9] !== 0x45 ||
|
|
4704
|
+
buffer[10] !== 0x42 ||
|
|
4705
|
+
buffer[11] !== 0x50) {
|
|
4706
|
+
return false;
|
|
4707
|
+
}
|
|
4708
|
+
;
|
|
4709
|
+
// Parse chunks starting after RIFF header (12 bytes)
|
|
4710
|
+
let offset = 12;
|
|
4711
|
+
while (offset < buffer.length - 8) {
|
|
4712
|
+
const chunkFourCC = buffer.toString('ascii', offset, offset + 4);
|
|
4713
|
+
const chunkSize = buffer.readUInt32LE(offset + 4);
|
|
4714
|
+
if (chunkFourCC === 'VP8X') {
|
|
4715
|
+
// VP8X extended header, check animation flag (bit 1 at offset+8)
|
|
4716
|
+
const flagsOffset = offset + 8;
|
|
4717
|
+
if (flagsOffset < buffer.length) {
|
|
4718
|
+
const flags = buffer[flagsOffset];
|
|
4719
|
+
if (flags & 0x02) {
|
|
4720
|
+
return true;
|
|
4721
|
+
}
|
|
4722
|
+
;
|
|
4723
|
+
}
|
|
4724
|
+
;
|
|
4725
|
+
}
|
|
4726
|
+
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
4727
|
+
// ANIM or ANMF chunks indicate animation
|
|
4728
|
+
return true;
|
|
4729
|
+
}
|
|
4730
|
+
;
|
|
4731
|
+
// Move to next chunk (chunk size + 8 bytes header, padded to even)
|
|
4732
|
+
offset += 8 + chunkSize + (chunkSize % 2);
|
|
4733
|
+
}
|
|
4734
|
+
;
|
|
4735
|
+
return false;
|
|
4736
|
+
};
|
|
4737
|
+
/**
|
|
4738
|
+
* Checks if a buffer is a WebP file
|
|
4739
|
+
*/
|
|
4740
|
+
const isWebPBuffer = (buffer) => {
|
|
4741
|
+
return (buffer.length >= 12 &&
|
|
4742
|
+
buffer[0] === 0x52 &&
|
|
4743
|
+
buffer[1] === 0x49 &&
|
|
4744
|
+
buffer[2] === 0x46 &&
|
|
4745
|
+
buffer[3] === 0x46 &&
|
|
4746
|
+
buffer[8] === 0x57 &&
|
|
4747
|
+
buffer[9] === 0x45 &&
|
|
4748
|
+
buffer[10] === 0x42 &&
|
|
4749
|
+
buffer[11] === 0x50);
|
|
4750
|
+
};
|
|
4751
|
+
/**
|
|
4752
|
+
* Kage: actualización de compatibilidad.
|
|
4753
|
+
* ---
|
|
4754
|
+
* Determines whether a message should include a Biz Binary Node.
|
|
4755
|
+
* A Biz Binary Node is added only for interactive messages
|
|
4756
|
+
* such as buttons or other supported interactive types.
|
|
4757
|
+
*/
|
|
4758
|
+
const shouldIncludeBizBinaryNode = (message) => !!(message.buttonsMessage ||
|
|
4759
|
+
message.listMessage ||
|
|
4760
|
+
message.templateMessage ||
|
|
4761
|
+
(message.interactiveMessage &&
|
|
4762
|
+
message.interactiveMessage.nativeFlowMessage));
|
|
4763
|
+
exports.shouldIncludeBizBinaryNode = shouldIncludeBizBinaryNode;
|
|
4764
|
+
|
|
4765
|
+
return module.exports;
|
|
4766
|
+
})();
|
|
4767
|
+
for (const key of Object.keys(__kageModule178)) {
|
|
4768
|
+
if (key !== '__esModule' && !Object.prototype.hasOwnProperty.call(exports,key)) Object.defineProperty(exports,key,{enumerable:true,configurable:true,get:()=>__kageModule178[key]});
|
|
4769
|
+
}
|
|
4770
|
+
Object.defineProperty(exports, Symbol.for('kage.advanced'), {value:__kageModule178, configurable:true});
|
|
4771
|
+
|
|
4772
|
+
{
|
|
4773
|
+
const advanced=exports[Symbol.for('kage.advanced')];
|
|
4774
|
+
const originalContent=exports.generateWAMessageContent;
|
|
4775
|
+
const originalMedia=exports.prepareWAMessageMedia;
|
|
4776
|
+
const advancedKeys=['raw','code','links','table','richResponse','stickers','flowReply','buttonReply','listReply','nativeFlow','cards','paymentInviteServiceType','orderText','requestPaymentFrom','invoiceNote','groupStatus','spoiler','isLottie','viewOnceV2','viewOnceV2Extension','interactiveAsTemplate','secureMetaServiceLabel','mentionAll','pollResult','pollUpdate','limitSharing','favicon'];
|
|
4777
|
+
exports.hasAdvancedMessageOptions=message=>!!message&&advancedKeys.some(k=>message[k]!=null);
|
|
4778
|
+
exports.generateWAMessageContent=async(message,options={})=>{
|
|
4779
|
+
if(exports.hasAdvancedMessageOptions(message)){
|
|
4780
|
+
const result=await advanced.generateWAMessageContent({...message}, {...options});
|
|
4781
|
+
return require('./event-message').prepareEventMessage(result);
|
|
4782
|
+
}
|
|
4783
|
+
return originalContent(message,options);
|
|
4784
|
+
};
|
|
4785
|
+
exports.prepareWAMessageMedia=async(message,options={})=>{
|
|
4786
|
+
if(options.jid&&require('../WABinary').isJidNewsletter(options.jid))return advanced.prepareWAMessageMedia(message,options);
|
|
4787
|
+
return originalMedia(message,options);
|
|
4788
|
+
};
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
{
|
|
4792
|
+
const advanced=exports[Symbol.for('kage.advanced')];const original=exports.generateWAMessage;
|
|
4793
|
+
exports.generateWAMessage=async(jid,content,options={})=>{
|
|
4794
|
+
if(exports.hasAdvancedMessageOptions(content)||require('../WABinary').isJidNewsletter(jid))return advanced.generateWAMessage(jid,{...content},{...options});
|
|
4795
|
+
return original(jid,content,options);
|
|
4796
|
+
};
|
|
4797
|
+
}
|