@pontalabs/baileys 1.0.6 → 1.0.8
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/LICENSE +1 -1
- package/README.md +839 -267
- package/WAProto/GenerateStatics.sh +4 -3
- package/WAProto/WAProto.proto +511 -1215
- package/WAProto/index.d.ts +55 -14017
- package/WAProto/index.js +29 -97689
- package/lib/Defaults/index.d.ts +74 -72
- package/lib/Defaults/index.js +108 -94
- package/lib/Signal/Group/ciphertext-message.d.ts +7 -8
- package/lib/Signal/Group/ciphertext-message.js +16 -9
- package/lib/Signal/Group/group-session-builder.d.ts +13 -11
- package/lib/Signal/Group/group-session-builder.js +61 -19
- package/lib/Signal/Group/group_cipher.d.ts +16 -14
- package/lib/Signal/Group/group_cipher.js +70 -41
- package/lib/Signal/Group/index.d.ts +11 -12
- package/lib/Signal/Group/index.js +61 -12
- package/lib/Signal/Group/keyhelper.d.ts +14 -9
- package/lib/Signal/Group/keyhelper.js +58 -10
- package/lib/Signal/Group/sender-chain-key.d.ts +13 -13
- package/lib/Signal/Group/sender-chain-key.js +34 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -16
- package/lib/Signal/Group/sender-key-distribution-message.js +36 -28
- package/lib/Signal/Group/sender-key-message.d.ts +18 -18
- package/lib/Signal/Group/sender-key-message.js +45 -38
- package/lib/Signal/Group/sender-key-name.d.ts +15 -14
- package/lib/Signal/Group/sender-key-name.js +31 -20
- package/lib/Signal/Group/sender-key-record.d.ts +25 -24
- package/lib/Signal/Group/sender-key-record.js +35 -18
- package/lib/Signal/Group/sender-key-state.d.ts +34 -29
- package/lib/Signal/Group/sender-key-state.js +100 -37
- package/lib/Signal/Group/sender-message-key.d.ts +10 -11
- package/lib/Signal/Group/sender-message-key.js +22 -15
- package/lib/Signal/libsignal.d.ts +8 -5
- package/lib/Signal/libsignal.js +142 -288
- package/lib/Signal/lid-mapping.d.ts +28 -23
- package/lib/Signal/lid-mapping.js +184 -277
- package/lib/Socket/Client/index.d.ts +2 -3
- package/lib/Socket/Client/index.js +22 -3
- package/lib/Socket/Client/types.d.ts +15 -15
- package/lib/Socket/Client/types.js +15 -8
- package/lib/Socket/Client/websocket.d.ts +12 -12
- package/lib/Socket/Client/websocket.js +37 -29
- package/lib/Socket/business.d.ts +178 -208
- package/lib/Socket/business.js +127 -91
- package/lib/Socket/chats.d.ts +93 -120
- package/lib/Socket/chats.js +652 -727
- package/lib/Socket/community.js +10 -2
- package/lib/Socket/groups.d.ts +111 -143
- package/lib/Socket/groups.js +186 -165
- package/lib/Socket/index.d.ts +183 -252
- package/lib/Socket/index.js +18 -38
- package/lib/Socket/messages-recv.d.ts +167 -196
- package/lib/Socket/messages-recv.js +1567 -1443
- package/lib/Socket/messages-send.d.ts +158 -192
- package/lib/Socket/messages-send.js +1208 -1102
- package/lib/Socket/newsletter.d.ts +138 -163
- package/lib/Socket/newsletter.js +249 -153
- package/lib/Socket/socket.d.ts +36 -50
- package/lib/Socket/socket.js +820 -749
- package/lib/Store/index.d.ts +4 -4
- package/lib/Store/index.js +24 -4
- package/lib/Store/make-in-memory-store.d.ts +104 -44
- package/lib/Store/make-in-memory-store.js +213 -199
- package/lib/Store/make-ordered-dictionary.d.ts +12 -13
- package/lib/Store/make-ordered-dictionary.js +45 -38
- package/lib/Store/object-repository.d.ts +10 -11
- package/lib/Store/object-repository.js +16 -9
- package/lib/Types/Auth.d.ts +97 -93
- package/lib/Types/Auth.js +3 -2
- package/lib/Types/Bussines.js +3 -2
- package/lib/Types/Call.d.ts +13 -14
- package/lib/Types/Call.js +3 -2
- package/lib/Types/Chat.d.ts +97 -78
- package/lib/Types/Chat.js +9 -8
- package/lib/Types/Contact.d.ts +9 -12
- package/lib/Types/Contact.js +3 -2
- package/lib/Types/Events.d.ts +176 -206
- package/lib/Types/Events.js +3 -2
- package/lib/Types/GroupMetadata.d.ts +48 -53
- package/lib/Types/GroupMetadata.js +3 -2
- package/lib/Types/Label.d.ts +14 -13
- package/lib/Types/Label.js +30 -24
- package/lib/Types/LabelAssociation.d.ts +20 -15
- package/lib/Types/LabelAssociation.js +12 -6
- package/lib/Types/Message.d.ts +412 -248
- package/lib/Types/Message.js +19 -17
- package/lib/Types/Product.d.ts +71 -58
- package/lib/Types/Product.js +3 -2
- package/lib/Types/Signal.d.ts +82 -71
- package/lib/Types/Signal.js +3 -2
- package/lib/Types/Socket.d.ts +81 -76
- package/lib/Types/Socket.js +3 -3
- package/lib/Types/State.d.ts +19 -75
- package/lib/Types/State.js +14 -56
- package/lib/Types/USync.d.ts +8 -8
- package/lib/Types/USync.js +3 -2
- package/lib/Types/index.d.ts +62 -47
- package/lib/Types/index.js +50 -26
- package/lib/Utils/auth-utils.d.ts +10 -13
- package/lib/Utils/auth-utils.js +472 -213
- package/lib/Utils/business.d.ts +20 -14
- package/lib/Utils/business.js +134 -110
- package/lib/Utils/chat-utils.d.ts +70 -88
- package/lib/Utils/chat-utils.js +403 -466
- package/lib/Utils/crypto.d.ts +46 -27
- package/lib/Utils/crypto.js +188 -117
- package/lib/Utils/decode-wa-message.d.ts +45 -58
- package/lib/Utils/decode-wa-message.js +311 -212
- package/lib/Utils/event-buffer.d.ts +17 -14
- package/lib/Utils/event-buffer.js +296 -321
- package/lib/Utils/generics.d.ts +92 -66
- package/lib/Utils/generics.js +436 -270
- package/lib/Utils/history.d.ts +23 -24
- package/lib/Utils/history.js +76 -106
- package/lib/Utils/index.d.ts +21 -24
- package/lib/Utils/index.js +41 -24
- package/lib/Utils/link-preview.d.ts +14 -12
- package/lib/Utils/link-preview.js +75 -40
- package/lib/Utils/logger.d.ts +11 -10
- package/lib/Utils/logger.js +7 -3
- package/lib/Utils/lt-hash.d.ts +14 -8
- package/lib/Utils/lt-hash.js +53 -3
- package/lib/Utils/make-mutex.d.ts +7 -7
- package/lib/Utils/make-mutex.js +44 -28
- package/lib/Utils/message-retry-manager.d.ts +88 -115
- package/lib/Utils/message-retry-manager.js +160 -265
- package/lib/Utils/messages-media.d.ts +107 -105
- package/lib/Utils/messages-media.js +619 -548
- package/lib/Utils/messages.d.ts +66 -52
- package/lib/Utils/messages.js +1558 -1730
- package/lib/Utils/noise-handler.d.ts +18 -18
- package/lib/Utils/noise-handler.js +130 -176
- package/lib/Utils/process-message.d.ts +32 -43
- package/lib/Utils/process-message.js +282 -484
- package/lib/Utils/rich-message-utils.js +724 -1220
- package/lib/Utils/signal.d.ts +32 -37
- package/lib/Utils/signal.js +105 -140
- package/lib/Utils/use-multi-file-auth-state.d.ts +5 -5
- package/lib/Utils/use-multi-file-auth-state.js +120 -70
- package/lib/Utils/use-single-file-auth-state.d.ts +13 -11
- package/lib/Utils/use-single-file-auth-state.js +66 -92
- package/lib/Utils/validate-connection.d.ts +13 -11
- package/lib/Utils/validate-connection.js +113 -128
- package/lib/WABinary/constants.d.ts +27 -25
- package/lib/WABinary/constants.js +1292 -1451
- package/lib/WABinary/decode.d.ts +9 -7
- package/lib/WABinary/decode.js +189 -163
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.js +155 -110
- package/lib/WABinary/generic-utils.d.ts +27 -14
- package/lib/WABinary/generic-utils.js +105 -177
- package/lib/WABinary/index.d.ts +5 -6
- package/lib/WABinary/index.js +25 -6
- package/lib/WABinary/jid-utils.d.ts +54 -41
- package/lib/WABinary/jid-utils.js +152 -83
- package/lib/WABinary/types.d.ts +13 -10
- package/lib/WABinary/types.js +3 -2
- package/lib/WAM/BinaryInfo.d.ts +15 -8
- package/lib/WAM/BinaryInfo.js +14 -7
- package/lib/WAM/constants.d.ts +37 -30
- package/lib/WAM/constants.js +11983 -19465
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.js +109 -95
- package/lib/WAM/index.d.ts +3 -4
- package/lib/WAM/index.js +23 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -9
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +21 -37
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -19
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +26 -18
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -11
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +20 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -11
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +24 -16
- package/lib/WAUSync/Protocols/index.d.ts +6 -6
- package/lib/WAUSync/Protocols/index.js +26 -6
- package/lib/WAUSync/USyncQuery.d.ts +28 -27
- package/lib/WAUSync/USyncQuery.js +62 -68
- package/lib/WAUSync/USyncUser.d.ts +10 -15
- package/lib/WAUSync/USyncUser.js +19 -20
- package/lib/WAUSync/index.d.ts +3 -4
- package/lib/WAUSync/index.js +23 -4
- package/lib/index.d.ts +12 -11
- package/lib/index.js +39 -12
- package/package.json +43 -87
- package/engine-requirements.js +0 -10
package/lib/Utils/messages.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
const boom_1 = require("@hapi/boom")
|
|
8
|
+
const crypto_1 = require("crypto")
|
|
9
|
+
const fs_1 = require("fs")
|
|
10
|
+
const WAProto_1 = require("../../WAProto")
|
|
11
|
+
const Defaults_1 = require("../Defaults")
|
|
12
|
+
const Types_1 = require("../Types")
|
|
13
|
+
const WABinary_1 = require("../WABinary")
|
|
14
|
+
const crypto_2 = require("./crypto")
|
|
15
|
+
const generics_1 = require("./generics")
|
|
16
|
+
const messages_media_1 = require("./messages-media")
|
|
17
|
+
const rich_message_utils_1 = require("./rich-message-utils")
|
|
18
|
+
|
|
16
19
|
const MIMETYPE_MAP = {
|
|
17
20
|
image: 'image/jpeg',
|
|
18
21
|
video: 'video/mp4',
|
|
@@ -20,111 +23,117 @@ const MIMETYPE_MAP = {
|
|
|
20
23
|
audio: 'audio/ogg; codecs=opus',
|
|
21
24
|
sticker: 'image/webp',
|
|
22
25
|
'product-catalog-image': 'image/jpeg'
|
|
23
|
-
}
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
const MessageTypeProto = {
|
|
25
|
-
image: WAProto.Message.ImageMessage,
|
|
26
|
-
video: WAProto.Message.VideoMessage,
|
|
27
|
-
audio: WAProto.Message.AudioMessage,
|
|
28
|
-
sticker: WAProto.Message.StickerMessage,
|
|
29
|
-
document: WAProto.Message.DocumentMessage
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
|
|
37
|
-
export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
38
|
-
const url = extractUrlFromText(text);
|
|
29
|
+
'image': Types_1.WAProto.Message.ImageMessage,
|
|
30
|
+
'video': Types_1.WAProto.Message.VideoMessage,
|
|
31
|
+
'audio': Types_1.WAProto.Message.AudioMessage,
|
|
32
|
+
'sticker': Types_1.WAProto.Message.StickerMessage,
|
|
33
|
+
'document': Types_1.WAProto.Message.DocumentMessage,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const extractUrlFromText = (text) => text.match(Defaults_1.URL_REGEX)?.[0]
|
|
37
|
+
|
|
38
|
+
const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
39
|
+
const url = extractUrlFromText(text)
|
|
39
40
|
if (!!getUrlInfo && url) {
|
|
40
41
|
try {
|
|
41
|
-
const urlInfo = await getUrlInfo(url)
|
|
42
|
-
return urlInfo
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const urlInfo = await getUrlInfo(url)
|
|
43
|
+
return urlInfo
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger?.warn({
|
|
46
|
+
trace: error.stack
|
|
47
|
+
}, 'url generation failed')
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
|
-
}
|
|
50
|
+
}
|
|
51
|
+
|
|
50
52
|
const assertColor = async (color) => {
|
|
51
|
-
let assertedColor
|
|
53
|
+
let assertedColor
|
|
52
54
|
if (typeof color === 'number') {
|
|
53
|
-
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let hex = color.trim().replace('#', '');
|
|
55
|
+
assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
|
|
56
|
+
} else {
|
|
57
|
+
let hex = color.trim().replace('#', '')
|
|
57
58
|
if (hex.length <= 6) {
|
|
58
|
-
hex = 'FF' + hex.padStart(6, '0')
|
|
59
|
+
hex = 'FF' + hex.padStart(6, '0')
|
|
59
60
|
}
|
|
60
|
-
assertedColor = parseInt(hex, 16)
|
|
61
|
-
return assertedColor
|
|
61
|
+
assertedColor = parseInt(hex, 16)
|
|
62
|
+
return assertedColor
|
|
62
63
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const prepareWAMessageMedia = async (message, options) => {
|
|
67
|
+
const logger = options.logger
|
|
68
|
+
let mediaType
|
|
69
|
+
|
|
70
|
+
for (const key of Defaults_1.MEDIA_KEYS) {
|
|
68
71
|
if (key in message) {
|
|
69
|
-
mediaType = key
|
|
72
|
+
mediaType = key
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
|
|
72
76
|
if (!mediaType) {
|
|
73
|
-
throw new Boom('Invalid media type', {
|
|
77
|
+
throw new boom_1.Boom('Invalid media type', {
|
|
78
|
+
statusCode: 400
|
|
79
|
+
});
|
|
74
80
|
}
|
|
81
|
+
|
|
75
82
|
const uploadData = {
|
|
76
83
|
...message,
|
|
77
84
|
media: message[mediaType]
|
|
78
|
-
}
|
|
79
|
-
delete uploadData[mediaType]
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
'url' in uploadData.media &&
|
|
83
|
-
!!uploadData.media.url &&
|
|
84
|
-
!!options.mediaCache &&
|
|
85
|
-
mediaType + ':' + uploadData.media.url.toString();
|
|
85
|
+
}
|
|
86
|
+
delete uploadData[mediaType]
|
|
87
|
+
const cacheableKey = typeof uploadData.media === 'object' && 'url' in uploadData.media && !!uploadData.media.url && !!options.mediaCache && mediaType + ':' + uploadData.media.url.toString()
|
|
88
|
+
|
|
86
89
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
87
|
-
uploadData.fileName = 'file'
|
|
90
|
+
uploadData.fileName = 'file'
|
|
88
91
|
}
|
|
92
|
+
|
|
89
93
|
if (!uploadData.mimetype) {
|
|
90
|
-
uploadData.mimetype = MIMETYPE_MAP[mediaType]
|
|
94
|
+
uploadData.mimetype = MIMETYPE_MAP[mediaType]
|
|
91
95
|
}
|
|
96
|
+
|
|
92
97
|
if (cacheableKey) {
|
|
93
|
-
const mediaBuff = await options.mediaCache.get(cacheableKey)
|
|
98
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey)
|
|
94
99
|
if (mediaBuff) {
|
|
95
|
-
logger?.debug({
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
logger?.debug({
|
|
101
|
+
cacheableKey
|
|
102
|
+
}, 'got media cache hit')
|
|
103
|
+
const obj = Types_1.WAProto.Message.decode(mediaBuff)
|
|
104
|
+
const key = `${mediaType}Message`
|
|
105
|
+
Object.assign(obj[key], {
|
|
106
|
+
...uploadData,
|
|
107
|
+
media: undefined
|
|
108
|
+
})
|
|
109
|
+
return obj
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
|
-
|
|
112
|
+
|
|
113
|
+
const isNewsletter = !!options.jid && WABinary_1.isJidNewsletter(options.jid)
|
|
114
|
+
|
|
103
115
|
if (isNewsletter) {
|
|
104
|
-
logger?.info({
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
logger?.warn({ err: e }, 'failed to generate thumbnail for newsletter media');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const { mediaUrl, directPath } = await options.upload(filePath, {
|
|
116
|
+
logger?.info({
|
|
117
|
+
key: cacheableKey
|
|
118
|
+
}, 'Preparing raw media for newsletter')
|
|
119
|
+
const {
|
|
120
|
+
filePath,
|
|
121
|
+
fileSha256,
|
|
122
|
+
fileLength
|
|
123
|
+
} = await messages_media_1.getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger)
|
|
124
|
+
const fileSha256B64 = fileSha256.toString('base64')
|
|
125
|
+
const {
|
|
126
|
+
mediaUrl,
|
|
127
|
+
directPath
|
|
128
|
+
} = await options.upload(filePath, {
|
|
121
129
|
fileEncSha256B64: fileSha256B64,
|
|
122
130
|
mediaType: mediaType,
|
|
123
131
|
timeoutMs: options.mediaUploadTimeoutMs
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await fs_1.promises.unlink(filePath)
|
|
135
|
+
|
|
136
|
+
const obj = Types_1.WAProto.Message.fromObject({
|
|
128
137
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
129
138
|
url: mediaUrl,
|
|
130
139
|
directPath,
|
|
@@ -133,83 +142,114 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
133
142
|
...uploadData,
|
|
134
143
|
media: undefined
|
|
135
144
|
})
|
|
136
|
-
})
|
|
145
|
+
})
|
|
146
|
+
|
|
137
147
|
if (uploadData.ptv) {
|
|
138
|
-
obj.ptvMessage = obj.videoMessage
|
|
139
|
-
delete obj.videoMessage
|
|
140
|
-
}
|
|
141
|
-
if (obj.stickerMessage) {
|
|
142
|
-
obj.stickerMessage.stickerSentTs = Date.now();
|
|
148
|
+
obj.ptvMessage = obj.videoMessage
|
|
149
|
+
delete obj.videoMessage
|
|
143
150
|
}
|
|
151
|
+
|
|
144
152
|
if (cacheableKey) {
|
|
145
|
-
logger?.debug({
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const
|
|
153
|
+
logger?.debug({
|
|
154
|
+
cacheableKey
|
|
155
|
+
}, 'set cache');
|
|
156
|
+
await options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish())
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return obj
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
|
|
163
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
|
|
164
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined'
|
|
165
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
|
|
166
|
+
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
|
|
167
|
+
|
|
168
|
+
const {
|
|
169
|
+
mediaKey,
|
|
170
|
+
encFilePath,
|
|
171
|
+
originalFilePath,
|
|
172
|
+
fileEncSha256,
|
|
173
|
+
fileSha256,
|
|
174
|
+
fileLength
|
|
175
|
+
} = await messages_media_1.encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
156
176
|
logger,
|
|
157
177
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
158
178
|
opts: options.options
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
const
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64')
|
|
182
|
+
|
|
183
|
+
const [{
|
|
184
|
+
mediaUrl,
|
|
185
|
+
directPath
|
|
186
|
+
}] = await Promise.all([
|
|
162
187
|
(async () => {
|
|
163
188
|
const result = await options.upload(encFilePath, {
|
|
164
189
|
fileEncSha256B64,
|
|
165
190
|
mediaType,
|
|
166
191
|
timeoutMs: options.mediaUploadTimeoutMs
|
|
167
|
-
})
|
|
168
|
-
logger?.debug({
|
|
169
|
-
|
|
192
|
+
})
|
|
193
|
+
logger?.debug({
|
|
194
|
+
mediaType,
|
|
195
|
+
cacheableKey
|
|
196
|
+
}, 'uploaded media')
|
|
197
|
+
return result
|
|
170
198
|
})(),
|
|
199
|
+
|
|
171
200
|
(async () => {
|
|
172
201
|
try {
|
|
173
202
|
if (requiresThumbnailComputation) {
|
|
174
|
-
const {
|
|
175
|
-
|
|
203
|
+
const {
|
|
204
|
+
thumbnail,
|
|
205
|
+
originalImageDimensions
|
|
206
|
+
} = await messages_media_1.generateThumbnail(originalFilePath, mediaType, options)
|
|
207
|
+
uploadData.jpegThumbnail = thumbnail
|
|
176
208
|
if (!uploadData.width && originalImageDimensions) {
|
|
177
|
-
uploadData.width = originalImageDimensions.width
|
|
178
|
-
uploadData.height = originalImageDimensions.height
|
|
179
|
-
|
|
209
|
+
uploadData.width = originalImageDimensions.width
|
|
210
|
+
uploadData.height = originalImageDimensions.height
|
|
211
|
+
|
|
212
|
+
logger?.debug('set dimensions')
|
|
180
213
|
}
|
|
214
|
+
|
|
181
215
|
logger?.debug('generated thumbnail');
|
|
182
216
|
}
|
|
217
|
+
|
|
183
218
|
if (requiresDurationComputation) {
|
|
184
|
-
uploadData.seconds = await getAudioDuration(originalFilePath)
|
|
185
|
-
logger?.debug('computed audio duration')
|
|
219
|
+
uploadData.seconds = await messages_media_1.getAudioDuration(originalFilePath)
|
|
220
|
+
logger?.debug('computed audio duration')
|
|
186
221
|
}
|
|
222
|
+
|
|
187
223
|
if (requiresWaveformProcessing) {
|
|
188
|
-
uploadData.waveform = await getAudioWaveform(originalFilePath, logger)
|
|
189
|
-
logger?.debug('processed waveform')
|
|
224
|
+
uploadData.waveform = await messages_media_1.getAudioWaveform(originalFilePath, logger)
|
|
225
|
+
logger?.debug('processed waveform')
|
|
190
226
|
}
|
|
227
|
+
|
|
191
228
|
if (requiresAudioBackground) {
|
|
192
|
-
uploadData.backgroundArgb = await assertColor(options.backgroundColor)
|
|
193
|
-
logger?.debug('computed backgroundColor audio status')
|
|
229
|
+
uploadData.backgroundArgb = await assertColor(options.backgroundColor)
|
|
230
|
+
logger?.debug('computed backgroundColor audio status')
|
|
194
231
|
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
} catch (error) {
|
|
233
|
+
logger?.warn({
|
|
234
|
+
trace: error.stack
|
|
235
|
+
}, 'failed to obtain extra info')
|
|
198
236
|
}
|
|
199
237
|
})()
|
|
200
238
|
]).finally(async () => {
|
|
201
239
|
try {
|
|
202
|
-
await
|
|
240
|
+
await fs_1.promises.unlink(encFilePath)
|
|
241
|
+
|
|
203
242
|
if (originalFilePath) {
|
|
204
|
-
await
|
|
243
|
+
await fs_1.promises.unlink(originalFilePath)
|
|
205
244
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
catch (error) {
|
|
209
|
-
logger?.warn('failed to remove tmp file')
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
|
|
245
|
+
|
|
246
|
+
logger?.debug('removed tmp files')
|
|
247
|
+
} catch (error) {
|
|
248
|
+
logger?.warn('failed to remove tmp file')
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
const obj = Types_1.WAProto.Message.fromObject({
|
|
213
253
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
214
254
|
url: mediaUrl,
|
|
215
255
|
directPath,
|
|
@@ -217,827 +257,850 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
217
257
|
fileEncSha256,
|
|
218
258
|
fileSha256,
|
|
219
259
|
fileLength,
|
|
220
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
260
|
+
mediaKeyTimestamp: generics_1.unixTimestampSeconds(),
|
|
221
261
|
...uploadData,
|
|
222
262
|
media: undefined
|
|
223
263
|
})
|
|
224
|
-
})
|
|
264
|
+
})
|
|
265
|
+
|
|
225
266
|
if (uploadData.ptv) {
|
|
226
|
-
obj.ptvMessage = obj.videoMessage
|
|
227
|
-
delete obj.videoMessage
|
|
267
|
+
obj.ptvMessage = obj.videoMessage
|
|
268
|
+
delete obj.videoMessage
|
|
228
269
|
}
|
|
270
|
+
|
|
229
271
|
if (cacheableKey) {
|
|
230
|
-
logger?.debug({
|
|
231
|
-
|
|
272
|
+
logger?.debug({
|
|
273
|
+
cacheableKey
|
|
274
|
+
}, 'set cache');
|
|
275
|
+
await options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish())
|
|
232
276
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
277
|
+
|
|
278
|
+
return obj
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const prepareAlbumMessageContent = async (jid, albums, options) => {
|
|
282
|
+
let mediaHandle
|
|
283
|
+
let mediaMsg
|
|
284
|
+
let message = []
|
|
285
|
+
|
|
286
|
+
const albumMsg = generateWAMessageFromContent(jid, {
|
|
287
|
+
albumMessage: {
|
|
288
|
+
expectedImageCount: albums.filter(item => 'image' in item).length,
|
|
289
|
+
expectedVideoCount: albums.filter(item => 'video' in item).length
|
|
290
|
+
}
|
|
291
|
+
}, options)
|
|
292
|
+
|
|
293
|
+
await options.sock.relayMessage(jid, albumMsg.message, {
|
|
294
|
+
messageId: albumMsg.key.id
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
for (const i in albums) {
|
|
298
|
+
const media = albums[i]
|
|
299
|
+
|
|
300
|
+
if ('image' in media) {
|
|
301
|
+
mediaMsg = await generateWAMessage(jid, {
|
|
302
|
+
image: media.image,
|
|
303
|
+
...media,
|
|
304
|
+
...options
|
|
305
|
+
}, {
|
|
306
|
+
userJid: options.userJid,
|
|
307
|
+
upload: async (encFilePath, opts) => {
|
|
308
|
+
const up = await options.sock.waUploadToServer(encFilePath, {
|
|
309
|
+
...opts,
|
|
310
|
+
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
311
|
+
})
|
|
312
|
+
mediaHandle = up.handle
|
|
313
|
+
return up
|
|
314
|
+
},
|
|
315
|
+
...options
|
|
316
|
+
})
|
|
317
|
+
} else if ('video' in media) {
|
|
318
|
+
mediaMsg = await generateWAMessage(jid, {
|
|
319
|
+
video: media.video,
|
|
320
|
+
...media,
|
|
321
|
+
...options
|
|
322
|
+
}, {
|
|
323
|
+
userJid: options.userJid,
|
|
324
|
+
upload: async (encFilePath, opts) => {
|
|
325
|
+
const up = await options.sock.waUploadToServer(encFilePath, {
|
|
326
|
+
...opts,
|
|
327
|
+
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
328
|
+
})
|
|
329
|
+
mediaHandle = up.handle
|
|
330
|
+
return up
|
|
331
|
+
},
|
|
332
|
+
...options
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (mediaMsg) {
|
|
337
|
+
mediaMsg.message.messageContextInfo = {
|
|
338
|
+
messageSecret: crypto_1.randomBytes(32),
|
|
339
|
+
messageAssociation: {
|
|
340
|
+
associationType: 1,
|
|
341
|
+
parentMessageKey: albumMsg.key
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
message.push(mediaMsg)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return message
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const prepareDisappearingMessageSettingContent = (expiration) => {
|
|
237
353
|
const content = {
|
|
238
354
|
ephemeralMessage: {
|
|
239
355
|
message: {
|
|
240
356
|
protocolMessage: {
|
|
241
|
-
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
242
|
-
ephemeralExpiration
|
|
357
|
+
type: Types_1.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
358
|
+
ephemeralExpiration: expiration ? expiration : 0
|
|
243
359
|
}
|
|
244
360
|
}
|
|
245
361
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*/
|
|
254
|
-
export const generateForwardMessageContent = (message, forceForward) => {
|
|
255
|
-
let content = message.message;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return Types_1.WAProto.Message.fromObject(content)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const generateForwardMessageContent = (message, forceForward) => {
|
|
368
|
+
let content = message.message
|
|
256
369
|
if (!content) {
|
|
257
|
-
throw new Boom('no content in message', {
|
|
370
|
+
throw new boom_1.Boom('no content in message', {
|
|
371
|
+
statusCode: 400
|
|
372
|
+
})
|
|
258
373
|
}
|
|
259
|
-
|
|
260
|
-
content = normalizeMessageContent(content)
|
|
261
|
-
content = proto.Message.decode(proto.Message.encode(content).finish())
|
|
262
|
-
|
|
263
|
-
let
|
|
264
|
-
score
|
|
374
|
+
|
|
375
|
+
content = normalizeMessageContent(content)
|
|
376
|
+
content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish())
|
|
377
|
+
|
|
378
|
+
let key = Object.keys(content)[0]
|
|
379
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0
|
|
380
|
+
|
|
381
|
+
score += message.key.fromMe && !forceForward ? 0 : 1
|
|
382
|
+
|
|
265
383
|
if (key === 'conversation') {
|
|
266
|
-
content.extendedTextMessage = {
|
|
267
|
-
|
|
268
|
-
key = 'extendedTextMessage';
|
|
269
|
-
}
|
|
270
|
-
const key_ = content?.[key];
|
|
271
|
-
if (score > 0) {
|
|
272
|
-
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
key_.contextInfo = {};
|
|
276
|
-
}
|
|
277
|
-
return content;
|
|
278
|
-
};
|
|
279
|
-
export const hasNonNullishProperty = (message, key) => {
|
|
280
|
-
return message != null &&
|
|
281
|
-
typeof message === 'object' &&
|
|
282
|
-
key in message &&
|
|
283
|
-
message[key] != null;
|
|
284
|
-
};
|
|
285
|
-
export const hasOptionalProperty = (obj, key) => {
|
|
286
|
-
return obj != null &&
|
|
287
|
-
typeof obj === 'object' &&
|
|
288
|
-
key in obj &&
|
|
289
|
-
obj[key] != null;
|
|
290
|
-
};
|
|
291
|
-
// Validate album message media to avoid bug
|
|
292
|
-
export const hasValidAlbumMedia = (message) => {
|
|
293
|
-
return message.imageMessage ||
|
|
294
|
-
message.videoMessage;
|
|
295
|
-
};
|
|
296
|
-
export const hasValidInteractiveHeader = (message) => {
|
|
297
|
-
return message.imageMessage ||
|
|
298
|
-
message.videoMessage ||
|
|
299
|
-
message.documentMessage ||
|
|
300
|
-
message.productMessage ||
|
|
301
|
-
message.locationMessage;
|
|
302
|
-
};
|
|
303
|
-
// Validate carousel cards header to avoid bug
|
|
304
|
-
export const hasValidCarouselHeader = (message) => {
|
|
305
|
-
return message.imageMessage ||
|
|
306
|
-
message.videoMessage ||
|
|
307
|
-
message.productMessage;
|
|
308
|
-
};
|
|
309
|
-
// Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
310
|
-
const prepareProductMessage = async (message, options) => {
|
|
311
|
-
if (!message.businessOwnerJid) {
|
|
312
|
-
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
313
|
-
}
|
|
314
|
-
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
315
|
-
const content = {
|
|
316
|
-
...message,
|
|
317
|
-
product: {
|
|
318
|
-
currencyCode: 'IDR',
|
|
319
|
-
priceAmount1000: 1000,
|
|
320
|
-
title: LIBRARY_NAME,
|
|
321
|
-
...message.product,
|
|
322
|
-
productImage: imageMessage
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
delete content.image;
|
|
326
|
-
return content;
|
|
327
|
-
};
|
|
328
|
-
const prepareStickerPackMessage = async (message, options) => {
|
|
329
|
-
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: pontalabs', description = '🏷️ pontalabs/baileys' } = message;
|
|
330
|
-
if (stickers.length > 60) {
|
|
331
|
-
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
332
|
-
}
|
|
333
|
-
if (stickers.length === 0) {
|
|
334
|
-
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
335
|
-
}
|
|
336
|
-
if (!cover) {
|
|
337
|
-
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
338
|
-
}
|
|
339
|
-
const logger = options.logger;
|
|
340
|
-
let cacheableKey = false;
|
|
341
|
-
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
342
|
-
const urls = [];
|
|
343
|
-
for (let i = 0; i < stickers.length; i++) {
|
|
344
|
-
const data = stickers[i].data;
|
|
345
|
-
if (typeof data === 'object' && data?.url) {
|
|
346
|
-
urls.push(data.url);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
if (urls.length > 0) {
|
|
350
|
-
cacheableKey = 'sticker:' + urls.join('@');
|
|
384
|
+
content.extendedTextMessage = {
|
|
385
|
+
text: content[key]
|
|
351
386
|
}
|
|
387
|
+
delete content.conversation
|
|
388
|
+
key = 'extendedTextMessage'
|
|
352
389
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
390
|
+
|
|
391
|
+
if (score > 0) {
|
|
392
|
+
content[key].contextInfo = {
|
|
393
|
+
forwardingScore: score,
|
|
394
|
+
isForwarded: true
|
|
358
395
|
}
|
|
396
|
+
} else {
|
|
397
|
+
content[key].contextInfo = {}
|
|
359
398
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
promises.push((async (index) => {
|
|
375
|
-
const sticker = stickers[index];
|
|
376
|
-
const { stream } = await getStream(sticker.data);
|
|
377
|
-
const buffer = await toBuffer(stream);
|
|
378
|
-
let webpBuffer;
|
|
379
|
-
let isAnimated = false;
|
|
380
|
-
if (isWebPBuffer(buffer)) {
|
|
381
|
-
webpBuffer = buffer;
|
|
382
|
-
isAnimated = isAnimatedWebP(buffer);
|
|
383
|
-
}
|
|
384
|
-
else if (hasSharp) {
|
|
385
|
-
webpBuffer = await lib.sharp.default(buffer)
|
|
386
|
-
.resize(512, 512, { fit: 'inside' })
|
|
387
|
-
.webp({ quality: 80 })
|
|
388
|
-
.toBuffer();
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
webpBuffer = await new lib.image.Transformer(buffer)
|
|
392
|
-
.resize(512, 512)
|
|
393
|
-
.webp(80);
|
|
394
|
-
}
|
|
395
|
-
if (webpBuffer.length > 1024 * 1024) {
|
|
396
|
-
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
397
|
-
}
|
|
398
|
-
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
399
|
-
const fileName = `${hash}.webp`;
|
|
400
|
-
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
401
|
-
stickerMetadata[index] = {
|
|
402
|
-
fileName,
|
|
403
|
-
mimetype: 'image/webp',
|
|
404
|
-
isAnimated,
|
|
405
|
-
emojis: sticker.emojis || ['✨'],
|
|
406
|
-
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
407
|
-
};
|
|
408
|
-
})(j));
|
|
409
|
-
}
|
|
410
|
-
await Promise.all(promises);
|
|
411
|
-
}
|
|
412
|
-
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
413
|
-
const { stream: coverStream } = await getStream(cover);
|
|
414
|
-
const coverBuffer = await toBuffer(coverStream);
|
|
415
|
-
let coverWebpBuffer;
|
|
416
|
-
if (isWebPBuffer(coverBuffer)) {
|
|
417
|
-
coverWebpBuffer = coverBuffer;
|
|
418
|
-
}
|
|
419
|
-
else if (hasSharp) {
|
|
420
|
-
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
421
|
-
.resize(512, 512, { fit: 'inside' })
|
|
422
|
-
.webp({ quality: 80 })
|
|
423
|
-
.toBuffer();
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
427
|
-
.resize(512, 512)
|
|
428
|
-
.webp(80);
|
|
429
|
-
}
|
|
430
|
-
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
431
|
-
const zipBuffer = await new Promise((resolve, reject) => {
|
|
432
|
-
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
433
|
-
});
|
|
434
|
-
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
435
|
-
logger,
|
|
436
|
-
opts: options.options
|
|
437
|
-
});
|
|
438
|
-
let stickerPackUploadResult;
|
|
439
|
-
try {
|
|
440
|
-
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
441
|
-
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
442
|
-
mediaType: 'sticker-pack',
|
|
443
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
finally {
|
|
447
|
-
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
448
|
-
}
|
|
449
|
-
const obj = {
|
|
450
|
-
name,
|
|
451
|
-
publisher,
|
|
452
|
-
stickerPackId: stickerPackIdValue,
|
|
453
|
-
packDescription: description,
|
|
454
|
-
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
455
|
-
stickerPackSize: zipBuffer.length,
|
|
456
|
-
stickers: stickerMetadata,
|
|
457
|
-
fileSha256: stickerPackUpload.fileSha256,
|
|
458
|
-
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
459
|
-
mediaKey: stickerPackUpload.mediaKey,
|
|
460
|
-
directPath: stickerPackUploadResult.directPath,
|
|
461
|
-
fileLength: stickerPackUpload.fileLength,
|
|
462
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
463
|
-
trayIconFileName
|
|
464
|
-
};
|
|
465
|
-
try {
|
|
466
|
-
let thumbnailBuffer;
|
|
467
|
-
if (hasSharp) {
|
|
468
|
-
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
469
|
-
}
|
|
470
|
-
else if (hasImage) {
|
|
471
|
-
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
472
|
-
}
|
|
473
|
-
else if (hasJimp) {
|
|
474
|
-
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
475
|
-
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
throw new Error('No image processing library available for thumbnail generation');
|
|
479
|
-
}
|
|
480
|
-
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
481
|
-
throw new Error('Failed to generate thumbnail buffer');
|
|
482
|
-
}
|
|
483
|
-
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
484
|
-
logger,
|
|
485
|
-
opts: options.options,
|
|
486
|
-
mediaKey: stickerPackUpload.mediaKey
|
|
487
|
-
});
|
|
488
|
-
let thumbUploadResult;
|
|
489
|
-
try {
|
|
490
|
-
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
491
|
-
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
492
|
-
mediaType: 'thumbnail-sticker-pack',
|
|
493
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
finally {
|
|
497
|
-
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
498
|
-
}
|
|
499
|
-
Object.assign(obj, {
|
|
500
|
-
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
501
|
-
thumbnailSha256: thumbUpload.fileSha256,
|
|
502
|
-
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
503
|
-
thumbnailHeight: 252,
|
|
504
|
-
thumbnailWidth: 252,
|
|
505
|
-
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
catch (error) {
|
|
509
|
-
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
510
|
-
}
|
|
511
|
-
if (cacheableKey) {
|
|
512
|
-
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
513
|
-
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
514
|
-
}
|
|
515
|
-
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
516
|
-
};
|
|
517
|
-
const prepareNativeFlowButtons = (message) => {
|
|
518
|
-
const buttons = message.nativeFlow;
|
|
519
|
-
const isButtonsFieldArray = Array.isArray(buttons);
|
|
520
|
-
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
521
|
-
const messageParamsJson = {};
|
|
522
|
-
if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
|
|
523
|
-
Object.assign(messageParamsJson, {
|
|
524
|
-
limited_time_offer: {
|
|
525
|
-
text: message.offerText || LIBRARY_NAME,
|
|
526
|
-
url: (message.offerUrl && /^https?:\/\//i.test(message.offerUrl)) ? message.offerUrl : DONATE_URL,
|
|
527
|
-
copy_code: message.offerCode,
|
|
528
|
-
expiration_time: message.offerExpiration
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
533
|
-
Object.assign(messageParamsJson, {
|
|
534
|
-
bottom_sheet: {
|
|
535
|
-
in_thread_buttons_limit: 1,
|
|
536
|
-
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
537
|
-
list_title: message.optionTitle || '📄 Select Options',
|
|
538
|
-
button_title: message.optionText
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
return {
|
|
543
|
-
buttons: correctedField.map(button => {
|
|
544
|
-
const buttonText = button.text || button.buttonText;
|
|
545
|
-
const buttonIcon = button.icon?.toUpperCase();
|
|
546
|
-
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
547
|
-
return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: buttonText || '👉🏻 Click', id: button.id, icon: buttonIcon }) };
|
|
548
|
-
}
|
|
549
|
-
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
550
|
-
return { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📋 Copy', copy_code: button.copy, icon: buttonIcon }) };
|
|
551
|
-
}
|
|
552
|
-
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
553
|
-
return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🌐 Visit', url: button.url, merchant_url: button.url, webview_interaction: button.useWebview, icon: buttonIcon }) };
|
|
554
|
-
}
|
|
555
|
-
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
556
|
-
return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📞 Call', phone_number: button.call, icon: buttonIcon }) };
|
|
557
|
-
}
|
|
558
|
-
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
559
|
-
return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText || '📋 Select', sections: button.sections, icon: buttonIcon }) };
|
|
560
|
-
}
|
|
561
|
-
return button;
|
|
562
|
-
}),
|
|
563
|
-
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
564
|
-
messageVersion: 3
|
|
565
|
-
};
|
|
566
|
-
};
|
|
567
|
-
export const generateWAMessageContent = async (message, options) => {
|
|
568
|
-
var _a, _b;
|
|
569
|
-
let m = {};
|
|
570
|
-
if (hasNonNullishProperty(message, 'raw')) {
|
|
571
|
-
delete message.raw;
|
|
572
|
-
return message;
|
|
573
|
-
}
|
|
574
|
-
else if (hasNonNullishProperty(message, 'code') ||
|
|
575
|
-
hasNonNullishProperty(message, 'links') ||
|
|
576
|
-
hasNonNullishProperty(message, 'table') ||
|
|
577
|
-
hasNonNullishProperty(message, 'aiBlocks') ||
|
|
578
|
-
hasNonNullishProperty(message, 'latex') ||
|
|
579
|
-
hasNonNullishProperty(message, 'gridImage') ||
|
|
580
|
-
hasNonNullishProperty(message, 'inlineImage') ||
|
|
581
|
-
hasNonNullishProperty(message, 'dynamic') ||
|
|
582
|
-
hasNonNullishProperty(message, 'map') ||
|
|
583
|
-
hasNonNullishProperty(message, 'contentItems') ||
|
|
584
|
-
// tipe baru (nama prefixed 'rich' agar tidak konflik dengan media biasa)
|
|
585
|
-
hasNonNullishProperty(message, 'richImage') ||
|
|
586
|
-
hasNonNullishProperty(message, 'richVideo') ||
|
|
587
|
-
hasNonNullishProperty(message, 'reels') ||
|
|
588
|
-
hasNonNullishProperty(message, 'source') ||
|
|
589
|
-
hasNonNullishProperty(message, 'richProduct') ||
|
|
590
|
-
hasNonNullishProperty(message, 'richPost') ||
|
|
591
|
-
hasNonNullishProperty(message, 'tip') ||
|
|
592
|
-
hasNonNullishProperty(message, 'suggest')) {
|
|
593
|
-
m = prepareRichResponseMessage(message);
|
|
594
|
-
}
|
|
595
|
-
else if (hasNonNullishProperty(message, 'text')) {
|
|
596
|
-
const extContent = { text: message.text };
|
|
597
|
-
let urlInfo = message.linkPreview;
|
|
399
|
+
|
|
400
|
+
return content
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const generateWAMessageContent = async (message, options) => {
|
|
404
|
+
let m = {}
|
|
405
|
+
if ('code' in message || 'codes' in message || 'table' in message || 'items' in message || 'richText' in message || 'richResponse' in message || 'richImages' in message || 'richVideo' in message || 'richSuggestions' in message || 'richQuestion' in message || 'richLatex' in message || 'richProduct' in message || 'richPost' in message || 'richReels' in message || 'richSources' in message || 'richTip' in message || 'richFooter' in message) {
|
|
406
|
+
return rich_message_utils_1.prepareRichResponseMessage(message)
|
|
407
|
+
} else if ('text' in message) {
|
|
408
|
+
const extContent = {
|
|
409
|
+
text: message.text
|
|
410
|
+
}
|
|
411
|
+
let urlInfo = message.linkPreview
|
|
412
|
+
|
|
598
413
|
if (typeof urlInfo === 'undefined') {
|
|
599
|
-
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger)
|
|
414
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger)
|
|
600
415
|
}
|
|
416
|
+
|
|
601
417
|
if (urlInfo) {
|
|
602
|
-
extContent.
|
|
603
|
-
extContent.
|
|
604
|
-
extContent.
|
|
605
|
-
extContent.
|
|
606
|
-
extContent.
|
|
607
|
-
|
|
418
|
+
extContent.canonicalUrl = urlInfo['canonical-url']
|
|
419
|
+
extContent.matchedText = urlInfo['matched-text']
|
|
420
|
+
extContent.jpegThumbnail = urlInfo.jpegThumbnail
|
|
421
|
+
extContent.description = urlInfo.description
|
|
422
|
+
extContent.title = urlInfo.title
|
|
423
|
+
extContent.previewType = 0
|
|
424
|
+
const img = urlInfo.highQualityThumbnail
|
|
425
|
+
|
|
608
426
|
if (img) {
|
|
609
|
-
extContent.thumbnailDirectPath = img.directPath
|
|
610
|
-
extContent.mediaKey = img.mediaKey
|
|
611
|
-
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
|
|
612
|
-
extContent.thumbnailWidth = img.width
|
|
613
|
-
extContent.thumbnailHeight = img.height
|
|
614
|
-
extContent.thumbnailSha256 = img.fileSha256
|
|
615
|
-
extContent.thumbnailEncSha256 = img.fileEncSha256
|
|
427
|
+
extContent.thumbnailDirectPath = img.directPath
|
|
428
|
+
extContent.mediaKey = img.mediaKey
|
|
429
|
+
extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
|
|
430
|
+
extContent.thumbnailWidth = img.width
|
|
431
|
+
extContent.thumbnailHeight = img.height
|
|
432
|
+
extContent.thumbnailSha256 = img.fileSha256
|
|
433
|
+
extContent.thumbnailEncSha256 = img.fileEncSha256
|
|
616
434
|
}
|
|
617
435
|
}
|
|
436
|
+
|
|
618
437
|
if (options.backgroundColor) {
|
|
619
|
-
extContent.backgroundArgb = await assertColor(options.backgroundColor)
|
|
438
|
+
extContent.backgroundArgb = await assertColor(options.backgroundColor)
|
|
620
439
|
}
|
|
621
|
-
|
|
622
|
-
|
|
440
|
+
|
|
441
|
+
if (options.textColor) {
|
|
442
|
+
extContent.textArgb = await assertColor(options.textColor)
|
|
623
443
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
444
|
+
|
|
445
|
+
if (options.font) {
|
|
446
|
+
extContent.font = options.font
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
extContent.contextInfo = {
|
|
450
|
+
...(message.contextInfo || {}),
|
|
451
|
+
...(message.mentions ? {
|
|
452
|
+
mentionedJid: message.mentions
|
|
453
|
+
} : {})
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
m.extendedTextMessage = extContent
|
|
457
|
+
} else if ('contacts' in message) {
|
|
458
|
+
const contactLen = message.contacts.contacts.length
|
|
459
|
+
|
|
460
|
+
let contactMessage
|
|
461
|
+
|
|
628
462
|
if (!contactLen) {
|
|
629
|
-
throw new Boom('require atleast 1 contact', {
|
|
463
|
+
throw new boom_1.Boom('require atleast 1 contact', {
|
|
464
|
+
statusCode: 400
|
|
465
|
+
})
|
|
630
466
|
}
|
|
467
|
+
|
|
631
468
|
if (contactLen === 1) {
|
|
632
|
-
|
|
469
|
+
contactMessage = {
|
|
470
|
+
contactMessage: Types_1.WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0])
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
contactMessage = {
|
|
474
|
+
contactsArrayMessage: Types_1.WAProto.Message.ContactsArrayMessage.fromObject(message.contacts)
|
|
475
|
+
}
|
|
633
476
|
}
|
|
634
|
-
|
|
635
|
-
|
|
477
|
+
|
|
478
|
+
const [type] = Object.keys(contactMessage)
|
|
479
|
+
|
|
480
|
+
contactMessage[type].contextInfo = {
|
|
481
|
+
...(message.contextInfo || {}),
|
|
482
|
+
...(message.mentions ? {
|
|
483
|
+
mentionedJid: message.mentions
|
|
484
|
+
} : {})
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
m = contactMessage
|
|
488
|
+
} else if ('location' in message) {
|
|
489
|
+
let locationMessage
|
|
490
|
+
|
|
491
|
+
if (message.live) {
|
|
492
|
+
locationMessage = {
|
|
493
|
+
liveLocationMessage: Types_1.WAProto.Message.LiveLocationMessage.fromObject(message.location)
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
locationMessage = {
|
|
497
|
+
locationMessage: Types_1.WAProto.Message.LocationMessage.fromObject(message.location)
|
|
498
|
+
}
|
|
636
499
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
500
|
+
|
|
501
|
+
const [type] = Object.keys(locationMessage)
|
|
502
|
+
|
|
503
|
+
locationMessage[type].contextInfo = {
|
|
504
|
+
...(message.contextInfo || {}),
|
|
505
|
+
...(message.mentions ? {
|
|
506
|
+
mentionedJid: message.mentions
|
|
507
|
+
} : {})
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
m = locationMessage
|
|
511
|
+
} else if ('react' in message) {
|
|
642
512
|
if (!message.react.senderTimestampMs) {
|
|
643
|
-
message.react.senderTimestampMs = Date.now()
|
|
513
|
+
message.react.senderTimestampMs = Date.now()
|
|
644
514
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
else if (
|
|
515
|
+
|
|
516
|
+
m.reactionMessage = Types_1.WAProto.Message.ReactionMessage.fromObject(message.react)
|
|
517
|
+
} else if ('delete' in message) {
|
|
648
518
|
m.protocolMessage = {
|
|
649
519
|
key: message.delete,
|
|
650
|
-
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
?
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
else if (
|
|
665
|
-
|
|
666
|
-
m
|
|
667
|
-
|
|
668
|
-
m.groupInviteMessage
|
|
669
|
-
|
|
670
|
-
m.groupInviteMessage.
|
|
671
|
-
|
|
672
|
-
|
|
520
|
+
type: Types_1.WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
521
|
+
}
|
|
522
|
+
} else if ('forward' in message) {
|
|
523
|
+
const mess = generateForwardMessageContent(message.forward, message.force)
|
|
524
|
+
const [type] = Object.keys(mess)
|
|
525
|
+
|
|
526
|
+
mess[type].contextInfo = {
|
|
527
|
+
...(message.contextInfo || {}),
|
|
528
|
+
...(message.mentions ? {
|
|
529
|
+
mentionedJid: message.mentions
|
|
530
|
+
} : {})
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
m = mess
|
|
534
|
+
} else if ('disappearingMessagesInChat' in message) {
|
|
535
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean' ? (message.disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) : message.disappearingMessagesInChat
|
|
536
|
+
m = prepareDisappearingMessageSettingContent(exp)
|
|
537
|
+
} else if ('groupInvite' in message) {
|
|
538
|
+
m.groupInviteMessage = {}
|
|
539
|
+
|
|
540
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.code
|
|
541
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.expiration
|
|
542
|
+
m.groupInviteMessage.caption = message.groupInvite.caption
|
|
543
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid
|
|
544
|
+
m.groupInviteMessage.groupName = message.groupInvite.name
|
|
545
|
+
m.groupInviteMessage.contextInfo = message.contextInfo
|
|
546
|
+
|
|
547
|
+
if (options.getProfilePicUrl) {
|
|
548
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid)
|
|
549
|
+
const { thumbnail } = await messages_media_1.generateThumbnail(pfpUrl, 'image')
|
|
550
|
+
m.groupInviteMessage.jpegThumbnail = thumbnail
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
m.groupInviteMessage.contextInfo = {
|
|
554
|
+
...(message.contextInfo || {}),
|
|
555
|
+
...(message.mentions ? {
|
|
556
|
+
mentionedJid: message.mentions
|
|
557
|
+
} : {})
|
|
558
|
+
}
|
|
559
|
+
} else if ('adminInvite' in message) {
|
|
560
|
+
m.newsletterAdminInviteMessage = {}
|
|
561
|
+
|
|
562
|
+
m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid
|
|
563
|
+
m.newsletterAdminInviteMessage.newsletterName = message.adminInvite.name
|
|
564
|
+
m.newsletterAdminInviteMessage.caption = message.adminInvite.caption
|
|
565
|
+
m.newsletterAdminInviteMessage.inviteExpiration = message.adminInvite.expiration
|
|
566
|
+
m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
|
|
567
|
+
|
|
673
568
|
if (options.getProfilePicUrl) {
|
|
674
|
-
const pfpUrl = await options.getProfilePicUrl(message.
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
569
|
+
const pfpUrl = await options.getProfilePicUrl(message.adminInvite.jid)
|
|
570
|
+
const {
|
|
571
|
+
thumbnail
|
|
572
|
+
} = await messages_media_1.generateThumbnail(pfpUrl, 'image')
|
|
573
|
+
m.newsletterAdminInviteMessage.jpegThumbnail = thumbnail
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
m.newsletterAdminInviteMessage.contextInfo = {
|
|
577
|
+
...(message.contextInfo || {}),
|
|
578
|
+
...(message.mentions ? {
|
|
579
|
+
mentionedJid: message.mentions
|
|
580
|
+
} : {})
|
|
581
|
+
}
|
|
582
|
+
} else if ('pin' in message) {
|
|
583
|
+
m.pinInChatMessage = {}
|
|
584
|
+
m.messageContextInfo = {}
|
|
585
|
+
|
|
586
|
+
m.pinInChatMessage.key = message.pin.key
|
|
587
|
+
m.pinInChatMessage.type = message.pin?.type || 1
|
|
588
|
+
m.pinInChatMessage.senderTimestampMs = message.pin?.time || Date.now()
|
|
589
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.pin.type === 1 ? message.pin.time || 86400 : 0
|
|
590
|
+
m.messageContextInfo.messageAddOnExpiryType = WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.STATIC
|
|
591
|
+
} else if ('keep' in message) {
|
|
592
|
+
m.keepInChatMessage = {}
|
|
593
|
+
|
|
594
|
+
m.keepInChatMessage.key = message.keep.key
|
|
595
|
+
m.keepInChatMessage.keepType = message.keep?.type || 1
|
|
596
|
+
m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
|
|
597
|
+
} else if ('call' in message) {
|
|
598
|
+
m.scheduledCallCreationMessage = {}
|
|
599
|
+
|
|
600
|
+
m.scheduledCallCreationMessage.scheduledTimestampMs = message.call?.time || Date.now()
|
|
601
|
+
m.scheduledCallCreationMessage.callType = message.call?.type || 1
|
|
602
|
+
m.scheduledCallCreationMessage.title = message.call?.name || 'Call Creation'
|
|
603
|
+
|
|
604
|
+
m.scheduledCallCreationMessage.contextInfo = {
|
|
605
|
+
...(message.contextInfo || {}),
|
|
606
|
+
...(message.mentions ? {
|
|
607
|
+
mentionedJid: message.mentions
|
|
608
|
+
} : {})
|
|
609
|
+
}
|
|
610
|
+
} else if ('paymentInvite' in message) {
|
|
611
|
+
m.messageContextInfo = {}
|
|
612
|
+
m.paymentInviteMessage = {}
|
|
613
|
+
|
|
614
|
+
m.paymentInviteMessage.expiryTimestamp = message.paymentInvite?.expiry || 0
|
|
615
|
+
m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
|
|
616
|
+
|
|
617
|
+
m.paymentInviteMessage.contextInfo = {
|
|
618
|
+
...(message.contextInfo || {}),
|
|
619
|
+
...(message.mentions ? {
|
|
620
|
+
mentionedJid: message.mentions
|
|
621
|
+
} : {})
|
|
622
|
+
}
|
|
623
|
+
} else if ('buttonLocation' in message) {
|
|
624
|
+
const bl = message.buttonLocation
|
|
625
|
+
const thumbInput = bl.jpegThumbnail || bl.thumbnail
|
|
626
|
+
if (!thumbInput) throw new Error('buttonLocation requires jpegThumbnail or thumbnail')
|
|
627
|
+
let thumb
|
|
628
|
+
if (Buffer.isBuffer(thumbInput)) {
|
|
629
|
+
thumb = thumbInput
|
|
630
|
+
} else if (typeof thumbInput === 'string' && (thumbInput.startsWith('http://') || thumbInput.startsWith('https://'))) {
|
|
631
|
+
const { stream } = await messages_media_1.getStream(thumbInput)
|
|
632
|
+
thumb = await messages_media_1.toBuffer(stream)
|
|
633
|
+
} else if (typeof thumbInput === 'string') {
|
|
634
|
+
thumb = await fs_1.promises.readFile(thumbInput)
|
|
635
|
+
} else {
|
|
636
|
+
throw new Error('buttonLocation thumbnail must be Buffer, URL, or file path')
|
|
637
|
+
}
|
|
638
|
+
let listButton = []
|
|
639
|
+
if (bl.listMenu && bl.listMenu.length) {
|
|
640
|
+
const nativeParams = JSON.stringify({
|
|
641
|
+
title: bl.listButtonText || 'Menu',
|
|
642
|
+
sections: [{
|
|
643
|
+
title: bl.listSectionTitle || 'Pilihan',
|
|
644
|
+
highlight_label: '',
|
|
645
|
+
rows: bl.listMenu.map(row => ({
|
|
646
|
+
header: row.header || '',
|
|
647
|
+
title: row.title || '',
|
|
648
|
+
description: row.description || '',
|
|
649
|
+
id: row.id || ''
|
|
650
|
+
}))
|
|
651
|
+
}]
|
|
652
|
+
})
|
|
653
|
+
listButton.push({
|
|
654
|
+
buttonId: 'list_menu',
|
|
655
|
+
buttonText: { displayText: bl.listButtonText || 'Pilih Menu' },
|
|
656
|
+
type: 1,
|
|
657
|
+
nativeFlowInfo: {
|
|
658
|
+
name: 'single_select',
|
|
659
|
+
paramsJson: nativeParams,
|
|
660
|
+
buttonParamsJson: nativeParams
|
|
680
661
|
}
|
|
662
|
+
})
|
|
663
|
+
}
|
|
664
|
+
const extraButtons = (bl.extraButtons || []).map(btn => ({
|
|
665
|
+
buttonId: btn.id || btn.buttonId || 'btn',
|
|
666
|
+
buttonText: { displayText: btn.displayText || btn.name || 'Button' },
|
|
667
|
+
type: 1
|
|
668
|
+
}))
|
|
669
|
+
m = {
|
|
670
|
+
buttonsMessage: {
|
|
671
|
+
locationMessage: {
|
|
672
|
+
degreesLatitude: bl.latitude || 0,
|
|
673
|
+
degreesLongitude: bl.longitude || 0,
|
|
674
|
+
name: bl.name || 'Location',
|
|
675
|
+
address: bl.address || '',
|
|
676
|
+
jpegThumbnail: thumb
|
|
677
|
+
},
|
|
678
|
+
contentText: bl.text || '',
|
|
679
|
+
footerText: bl.footer || '',
|
|
680
|
+
buttons: [...listButton, ...extraButtons],
|
|
681
|
+
headerType: 6,
|
|
682
|
+
viewOnce: true
|
|
681
683
|
}
|
|
682
684
|
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
else if (hasNonNullishProperty(message, 'flowReply')) {
|
|
702
|
-
m.interactiveResponseMessage = {
|
|
703
|
-
body: {
|
|
704
|
-
format: message.flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
|
|
705
|
-
text: message.flowReply.text
|
|
706
|
-
},
|
|
707
|
-
nativeFlowResponseMessage: {
|
|
708
|
-
name: message.flowReply.name,
|
|
709
|
-
paramsJson: message.flowReply.paramsJson || '{}',
|
|
710
|
-
version: message.flowReply.version || 1
|
|
685
|
+
} else if ('groupStatus' in message) {
|
|
686
|
+
const gs = message.groupStatus
|
|
687
|
+
if (!gs.message) throw new Error('groupStatus.message is required')
|
|
688
|
+
const audienceType = gs.audienceType || 0
|
|
689
|
+
const backgroundColor = gs.backgroundArgb
|
|
690
|
+
const textColor = gs.textArgb
|
|
691
|
+
const font = gs.font
|
|
692
|
+
let messageContent = gs.message
|
|
693
|
+
if (typeof messageContent === 'string') {
|
|
694
|
+
messageContent = {
|
|
695
|
+
extendedTextMessage: {
|
|
696
|
+
text: messageContent,
|
|
697
|
+
contextInfo: { statusAudienceMetadata: { audienceType } },
|
|
698
|
+
...(backgroundColor && { backgroundArgb: backgroundColor }),
|
|
699
|
+
...(textColor && { textArgb: textColor }),
|
|
700
|
+
...(font && { font })
|
|
701
|
+
}
|
|
711
702
|
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
|
|
703
|
+
} else if (messageContent.extendedTextMessage) {
|
|
704
|
+
if (backgroundColor) messageContent.extendedTextMessage.backgroundArgb = backgroundColor
|
|
705
|
+
if (textColor) messageContent.extendedTextMessage.textArgb = textColor
|
|
706
|
+
if (font) messageContent.extendedTextMessage.font = font
|
|
707
|
+
messageContent.extendedTextMessage.contextInfo = {
|
|
708
|
+
...messageContent.extendedTextMessage.contextInfo,
|
|
709
|
+
statusAudienceMetadata: { audienceType }
|
|
710
|
+
}
|
|
711
|
+
} else if (
|
|
712
|
+
messageContent.imageMessage || messageContent.videoMessage ||
|
|
713
|
+
messageContent.audioMessage || messageContent.documentMessage
|
|
714
|
+
) {
|
|
715
|
+
const key = Object.keys(messageContent)[0]
|
|
716
|
+
if (!messageContent[key].contextInfo) messageContent[key].contextInfo = {}
|
|
717
|
+
messageContent[key].contextInfo.statusAudienceMetadata = { audienceType }
|
|
718
|
+
}
|
|
719
|
+
m = { groupStatusMessageV2: { message: messageContent } }
|
|
720
|
+
} else if ('orderStatus' in message) {
|
|
721
|
+
const os = message.orderStatus
|
|
722
|
+
if (!os.image) throw new Error('orderStatus requires image')
|
|
723
|
+
let imageInput = os.image
|
|
724
|
+
if (typeof imageInput === 'string' && !imageInput.startsWith('http://') && !imageInput.startsWith('https://')) {
|
|
725
|
+
imageInput = await fs_1.promises.readFile(imageInput)
|
|
726
|
+
}
|
|
727
|
+
const media = await prepareWAMessageMedia({ image: imageInput }, { upload: options.upload, ...options })
|
|
728
|
+
m = {
|
|
729
|
+
viewOnceMessage: {
|
|
730
|
+
message: {
|
|
731
|
+
messageContextInfo: {
|
|
732
|
+
deviceListMetadata: {},
|
|
733
|
+
deviceListMetadataVersion: 2
|
|
734
|
+
},
|
|
735
|
+
interactiveMessage: WAProto_1.proto.Message.InteractiveMessage.create({
|
|
736
|
+
header: {
|
|
737
|
+
title: os.title || 'Order Status',
|
|
738
|
+
hasMediaAttachment: true,
|
|
739
|
+
...media
|
|
740
|
+
},
|
|
741
|
+
body: { text: os.text || 'Silakan cek status pesanan Anda.' },
|
|
742
|
+
footer: { text: os.footer || 'Powered by PontaCT' },
|
|
743
|
+
nativeFlowMessage: {
|
|
744
|
+
buttons: [{
|
|
745
|
+
name: 'order_status',
|
|
746
|
+
buttonParamsJson: JSON.stringify({
|
|
747
|
+
reference_id: os.referenceId || 'PT-001',
|
|
748
|
+
order: {
|
|
749
|
+
status: os.status || 'PROCESSING',
|
|
750
|
+
subtotal: {
|
|
751
|
+
value: os.subtotalValue || 0,
|
|
752
|
+
offset: os.subtotalOffset || 100
|
|
753
|
+
},
|
|
754
|
+
tax: {
|
|
755
|
+
value: os.taxValue || 0,
|
|
756
|
+
offset: os.taxOffset || 100
|
|
757
|
+
},
|
|
758
|
+
currency: os.currency || 'IDR'
|
|
759
|
+
}
|
|
760
|
+
})
|
|
761
|
+
}]
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
} else if ('buttonReply' in message) {
|
|
715
768
|
switch (message.type) {
|
|
769
|
+
case 'list':
|
|
770
|
+
m.listResponseMessage = {
|
|
771
|
+
title: message.buttonReply.title,
|
|
772
|
+
description: message.buttonReply.description,
|
|
773
|
+
singleSelectReply: {
|
|
774
|
+
selectedRowId: message.buttonReply.rowId
|
|
775
|
+
},
|
|
776
|
+
lisType: WAProto_1.proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
|
|
777
|
+
}
|
|
778
|
+
break
|
|
716
779
|
case 'template':
|
|
717
780
|
m.templateButtonReplyMessage = {
|
|
718
781
|
selectedDisplayText: message.buttonReply.displayText,
|
|
719
782
|
selectedId: message.buttonReply.id,
|
|
720
783
|
selectedIndex: message.buttonReply.index
|
|
721
|
-
}
|
|
722
|
-
break
|
|
784
|
+
}
|
|
785
|
+
break
|
|
723
786
|
case 'plain':
|
|
724
787
|
m.buttonsResponseMessage = {
|
|
725
788
|
selectedButtonId: message.buttonReply.id,
|
|
726
789
|
selectedDisplayText: message.buttonReply.displayText,
|
|
727
|
-
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
728
|
-
}
|
|
729
|
-
break
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
790
|
+
type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
791
|
+
}
|
|
792
|
+
break
|
|
793
|
+
case 'interactive':
|
|
794
|
+
m.interactiveResponseMessage = {
|
|
795
|
+
body: {
|
|
796
|
+
text: message.buttonReply.displayText,
|
|
797
|
+
format: WAProto_1.proto.Message.InteractiveResponseMessage.Body.Format.EXTENSIONS_1
|
|
798
|
+
},
|
|
799
|
+
nativeFlowResponseMessage: {
|
|
800
|
+
name: message.buttonReply.nativeFlows.name,
|
|
801
|
+
paramsJson: message.buttonReply.nativeFlows.paramsJson,
|
|
802
|
+
version: message.buttonReply.nativeFlows.version
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
break
|
|
806
|
+
}
|
|
807
|
+
} else if ('ptv' in message && message.ptv) {
|
|
808
|
+
const {
|
|
809
|
+
videoMessage
|
|
810
|
+
} = await prepareWAMessageMedia({
|
|
811
|
+
video: message.video
|
|
812
|
+
}, options)
|
|
813
|
+
|
|
814
|
+
m.ptvMessage = videoMessage
|
|
815
|
+
} else if ('album' in message) {
|
|
816
|
+
const imageMessages = message.album.filter(item => 'image' in item)
|
|
817
|
+
const videoMessages = message.album.filter(item => 'video' in item)
|
|
818
|
+
|
|
819
|
+
m.albumMessage = Types_1.WAProto.Message.AlbumMessage.fromObject({
|
|
820
|
+
expectedImageCount: imageMessages.length,
|
|
821
|
+
expectedVideoCount: videoMessages.length
|
|
822
|
+
})
|
|
823
|
+
} else if ('order' in message) {
|
|
824
|
+
m.orderMessage = Types_1.WAProto.Message.OrderMessage.fromObject(message.order)
|
|
825
|
+
|
|
826
|
+
m.orderMessage.contextInfo = {
|
|
827
|
+
...(message.contextInfo || {}),
|
|
828
|
+
...(message.mentions ? {
|
|
829
|
+
mentionedJid: message.mentions
|
|
830
|
+
} : {})
|
|
831
|
+
}
|
|
832
|
+
} else if ('event' in message) {
|
|
833
|
+
m.eventMessage = Types_1.WAProto.Message.EventMessage.fromObject(message.event)
|
|
834
|
+
|
|
835
|
+
if (!message.event.startTime) {
|
|
836
|
+
m.eventMessage.startTime = generics_1.unixTimestampSeconds() + 86400
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (options.getCallLink && message.event.call) {
|
|
840
|
+
const link = await options.getCallLink(message.event.call, m.eventMessage.startTime)
|
|
841
|
+
m.eventMessage.joinLink = link
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
m.eventMessage.contextInfo = {
|
|
845
|
+
...(message.contextInfo || {}),
|
|
846
|
+
...(message.mentions ? {
|
|
847
|
+
mentionedJid: message.mentions
|
|
848
|
+
} : {})
|
|
849
|
+
}
|
|
850
|
+
} else if ('product' in message) {
|
|
851
|
+
const { imageMessage } = await prepareWAMessageMedia({
|
|
852
|
+
image: message.product.productImage
|
|
853
|
+
}, options)
|
|
854
|
+
|
|
855
|
+
m.productMessage = Types_1.WAProto.Message.ProductMessage.fromObject({
|
|
742
856
|
...message,
|
|
743
857
|
product: {
|
|
744
|
-
currencyCode: 'IDR',
|
|
745
|
-
priceAmount1000: 1000,
|
|
746
|
-
title: LIBRARY_NAME,
|
|
747
858
|
...message.product,
|
|
748
|
-
productImage: imageMessage
|
|
859
|
+
productImage: imageMessage,
|
|
749
860
|
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
m.productMessage =
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
title: message.listReply.title
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
else if (hasNonNullishProperty(message, 'event')) {
|
|
765
|
-
m.eventMessage = {};
|
|
766
|
-
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
767
|
-
if (message.event.call && options.getCallLink) {
|
|
768
|
-
const token = await options.getCallLink(message.event.call, { startTime });
|
|
769
|
-
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
770
|
-
}
|
|
771
|
-
m.messageContextInfo = {
|
|
772
|
-
// encKey
|
|
773
|
-
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
774
|
-
};
|
|
775
|
-
m.eventMessage.name = message.event.name;
|
|
776
|
-
m.eventMessage.description = message.event.description;
|
|
777
|
-
m.eventMessage.startTime = startTime;
|
|
778
|
-
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
779
|
-
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
780
|
-
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
781
|
-
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
782
|
-
m.eventMessage.location = message.event.location;
|
|
783
|
-
}
|
|
784
|
-
else if (hasNonNullishProperty(message, 'poll')) {
|
|
785
|
-
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
786
|
-
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
787
|
-
if (!Array.isArray(message.poll.values)) {
|
|
788
|
-
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
789
|
-
}
|
|
790
|
-
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
791
|
-
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
861
|
+
})
|
|
862
|
+
|
|
863
|
+
m.productMessage.contextInfo = {
|
|
864
|
+
...(message.contextInfo || {}),
|
|
865
|
+
...(message.mentions ? {
|
|
866
|
+
mentionedJid: message.mentions
|
|
867
|
+
} : {})
|
|
868
|
+
}
|
|
869
|
+
} else if ('pollResult' in message) {
|
|
870
|
+
if (!Array.isArray(message.pollResult.values)) {
|
|
871
|
+
throw new boom_1.Boom('Invalid pollResult values', {
|
|
792
872
|
statusCode: 400
|
|
793
|
-
})
|
|
794
|
-
}
|
|
795
|
-
const pollCreationMessage = {
|
|
796
|
-
name: message.poll.name,
|
|
797
|
-
selectableOptionsCount: message.poll.selectableCount,
|
|
798
|
-
options: message.poll.values.map(optionName => ({ optionName })),
|
|
799
|
-
endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
|
|
800
|
-
hideParticipantName: message.poll.hideVoter ?? false,
|
|
801
|
-
allowAddOption: message.poll.canAddOption ?? false
|
|
802
|
-
};
|
|
803
|
-
if (message.poll.toAnnouncementGroup) {
|
|
804
|
-
// poll v2 is for community announcement groups (single select and multiple)
|
|
805
|
-
m.pollCreationMessageV2 = pollCreationMessage;
|
|
806
|
-
}
|
|
807
|
-
else {
|
|
808
|
-
// Add quiz message support
|
|
809
|
-
if (message.poll.pollType === 1) {
|
|
810
|
-
if (!message.poll.correctAnswer) {
|
|
811
|
-
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
812
|
-
}
|
|
813
|
-
m.pollCreationMessageV5 = {
|
|
814
|
-
// quiz for newsletter only
|
|
815
|
-
...pollCreationMessage,
|
|
816
|
-
correctAnswer: {
|
|
817
|
-
optionName: message.poll.correctAnswer.toString()
|
|
818
|
-
},
|
|
819
|
-
pollType: 1,
|
|
820
|
-
selectableOptionsCount: 1
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
else if (message.poll.selectableCount === 1) {
|
|
824
|
-
//poll v3 is for single select polls
|
|
825
|
-
m.pollCreationMessageV3 = pollCreationMessage;
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
// poll for multiple choice polls
|
|
829
|
-
m.pollCreationMessage = pollCreationMessage;
|
|
830
|
-
}
|
|
873
|
+
})
|
|
831
874
|
}
|
|
832
|
-
|
|
833
|
-
// encKey
|
|
834
|
-
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
else if (hasNonNullishProperty(message, 'pollResult')) {
|
|
875
|
+
|
|
838
876
|
const pollResultSnapshotMessage = {
|
|
839
877
|
name: message.pollResult.name,
|
|
840
|
-
pollVotes: message.pollResult.
|
|
841
|
-
optionName
|
|
842
|
-
optionVoteCount
|
|
878
|
+
pollVotes: message.pollResult.values.map(([optionName, optionVoteCount]) => ({
|
|
879
|
+
optionName,
|
|
880
|
+
optionVoteCount
|
|
843
881
|
}))
|
|
844
|
-
};
|
|
845
|
-
if (message.pollResult.pollType === 1) {
|
|
846
|
-
pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
|
|
847
|
-
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
848
882
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
883
|
+
|
|
884
|
+
pollResultSnapshotMessage.contextInfo = {
|
|
885
|
+
...(message.contextInfo || {}),
|
|
886
|
+
...(message.mentions ? {
|
|
887
|
+
mentionedJid: message.mentions
|
|
888
|
+
} : {})
|
|
852
889
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
861
|
-
m.pollUpdateMessage = {
|
|
862
|
-
metadata: message.pollUpdate.metadata,
|
|
863
|
-
pollCreationMessageKey: message.pollUpdate.key,
|
|
864
|
-
senderTimestampMs: Date.now(),
|
|
865
|
-
vote: message.pollUpdate.vote
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
|
|
869
|
-
m.paymentInviteMessage = {
|
|
870
|
-
expiryTimestamp: Date.now(),
|
|
871
|
-
serviceType: message.paymentInviteServiceType
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
else if (hasNonNullishProperty(message, 'orderText')) {
|
|
875
|
-
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
876
|
-
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
877
|
-
}
|
|
878
|
-
m.orderMessage = {
|
|
879
|
-
itemCount: 1,
|
|
880
|
-
messageVersion: 1,
|
|
881
|
-
orderTitle: LIBRARY_NAME,
|
|
882
|
-
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
883
|
-
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
884
|
-
token: generateMessageIDV2(),
|
|
885
|
-
totalAmount1000: 1000,
|
|
886
|
-
totalCurrencyCode: 'IDR',
|
|
887
|
-
...message,
|
|
888
|
-
message: message.orderText
|
|
889
|
-
};
|
|
890
|
-
delete m.orderMessage.orderText;
|
|
891
|
-
}
|
|
892
|
-
else if (hasNonNullishProperty(message, 'album')) {
|
|
893
|
-
if (!Array.isArray(message.album)) {
|
|
894
|
-
throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
895
|
-
}
|
|
896
|
-
let videoCount = 0;
|
|
897
|
-
for (let i = 0; i < message.album.length; i++) {
|
|
898
|
-
if (message.album[i].video)
|
|
899
|
-
videoCount++;
|
|
900
|
-
}
|
|
901
|
-
;
|
|
902
|
-
let imageCount = 0;
|
|
903
|
-
for (let i = 0; i < message.album.length; i++) {
|
|
904
|
-
if (message.album[i].image)
|
|
905
|
-
imageCount++;
|
|
906
|
-
}
|
|
907
|
-
;
|
|
908
|
-
if ((videoCount + imageCount) < 2) {
|
|
909
|
-
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
910
|
-
}
|
|
911
|
-
m.albumMessage = {
|
|
912
|
-
expectedImageCount: imageCount,
|
|
913
|
-
expectedVideoCount: videoCount
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
917
|
-
m.protocolMessage = {
|
|
918
|
-
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
922
|
-
m.requestPhoneNumberMessage = {};
|
|
923
|
-
}
|
|
924
|
-
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
925
|
-
m.protocolMessage = {
|
|
926
|
-
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
927
|
-
limitSharing: {
|
|
928
|
-
sharingLimited: message.limitSharing === true,
|
|
929
|
-
trigger: 1,
|
|
930
|
-
limitSharingSettingTimestamp: Date.now(),
|
|
931
|
-
initiatedByMe: true
|
|
932
|
-
}
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
else if (hasNonNullishProperty(message, 'buttonsMessage')) {
|
|
936
|
-
// Direct buttonsMessage passthrough — supports headerType 6 (locationMessage), etc.
|
|
937
|
-
const btnMsg = { ...message.buttonsMessage };
|
|
938
|
-
if (btnMsg.locationMessage?.jpegThumbnail && !Buffer.isBuffer(btnMsg.locationMessage.jpegThumbnail)) {
|
|
939
|
-
const lib = await getImageProcessingLibrary();
|
|
940
|
-
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
941
|
-
if (hasSharp) {
|
|
942
|
-
const rawBuf = typeof btnMsg.locationMessage.jpegThumbnail === 'string'
|
|
943
|
-
? (await fs.readFile(btnMsg.locationMessage.jpegThumbnail))
|
|
944
|
-
: btnMsg.locationMessage.jpegThumbnail;
|
|
945
|
-
btnMsg.locationMessage.jpegThumbnail = await lib.sharp.default(rawBuf)
|
|
946
|
-
.resize(300, 300, { fit: 'inside', withoutEnlargement: true })
|
|
947
|
-
.jpeg({ quality: 80 })
|
|
948
|
-
.toBuffer();
|
|
949
|
-
}
|
|
890
|
+
|
|
891
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage
|
|
892
|
+
} else if ('poll' in message) {
|
|
893
|
+
if (!Array.isArray(message.poll.values)) {
|
|
894
|
+
throw new boom_1.Boom('Invalid poll values', {
|
|
895
|
+
statusCode: 400
|
|
896
|
+
})
|
|
950
897
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (hasNonNullishProperty(message, 'buttons')) {
|
|
957
|
-
const buttonsMessage = {
|
|
958
|
-
buttons: message.buttons.map(button => {
|
|
959
|
-
const buttonText = button.text || button.buttonText;
|
|
960
|
-
if (hasOptionalProperty(button, 'sections')) {
|
|
961
|
-
return {
|
|
962
|
-
nativeFlowInfo: {
|
|
963
|
-
name: 'single_select',
|
|
964
|
-
paramsJson: JSON.stringify({
|
|
965
|
-
title: buttonText,
|
|
966
|
-
sections: button.sections
|
|
967
|
-
})
|
|
968
|
-
},
|
|
969
|
-
type: ButtonType.NATIVE_FLOW
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
else if (hasOptionalProperty(button, 'name')) {
|
|
973
|
-
return {
|
|
974
|
-
nativeFlowInfo: {
|
|
975
|
-
name: button.name,
|
|
976
|
-
paramsJson: button.paramsJson
|
|
977
|
-
},
|
|
978
|
-
type: ButtonType.NATIVE_FLOW
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
return {
|
|
982
|
-
buttonId: button.id || button.buttonId,
|
|
983
|
-
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
984
|
-
type: button.type || ButtonType.RESPONSE
|
|
985
|
-
};
|
|
898
|
+
|
|
899
|
+
if (message.poll.selectableCount < 0 ||
|
|
900
|
+
message.poll.selectableCount > message.poll.values.length) {
|
|
901
|
+
throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
902
|
+
statusCode: 400
|
|
986
903
|
})
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
const pollCreationMessage = {
|
|
907
|
+
name: message.poll.name,
|
|
908
|
+
selectableOptionsCount: message.poll?.selectableCount || 0,
|
|
909
|
+
options: message.poll.values.map(optionName => ({
|
|
910
|
+
optionName
|
|
911
|
+
})),
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
pollCreationMessage.contextInfo = {
|
|
915
|
+
...(message.contextInfo || {}),
|
|
916
|
+
...(message.mentions ? {
|
|
917
|
+
mentionedJid: message.mentions
|
|
918
|
+
} : {})
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (message.poll?.toAnnouncementGroup) {
|
|
922
|
+
m.pollCreationMessageV2 = pollCreationMessage
|
|
923
|
+
} else {
|
|
924
|
+
if (message.poll.selectableCount > 0) {
|
|
925
|
+
m.pollCreationMessageV3 = pollCreationMessage
|
|
926
|
+
} else {
|
|
927
|
+
m.pollCreationMessage = pollCreationMessage
|
|
995
928
|
}
|
|
996
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
997
|
-
buttonsMessage.headerType = ButtonHeaderType[type];
|
|
998
|
-
Object.assign(buttonsMessage, m);
|
|
999
929
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
930
|
+
} else if ('payment' in message) {
|
|
931
|
+
const requestPaymentMessage = {
|
|
932
|
+
amount: {
|
|
933
|
+
currencyCode: message.payment?.currency || 'IDR',
|
|
934
|
+
offset: message.payment?.offset || 0,
|
|
935
|
+
value: message.payment?.amount || 999999999
|
|
936
|
+
},
|
|
937
|
+
expiryTimestamp: message.payment?.expiry || 0,
|
|
938
|
+
amount1000: message.payment?.amount || 999999999 * 1000,
|
|
939
|
+
currencyCodeIso4217: message.payment?.currency || 'IDR',
|
|
940
|
+
requestFrom: message.payment?.from || '0@s.whatsapp.net',
|
|
941
|
+
noteMessage: {
|
|
942
|
+
extendedTextMessage: {
|
|
943
|
+
text: message.payment?.note || 'Notes'
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
background: {
|
|
947
|
+
placeholderArgb: message.payment?.image?.placeholderArgb || 4278190080,
|
|
948
|
+
textArgb: message.payment?.image?.textArgb || 4294967295,
|
|
949
|
+
subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
|
|
950
|
+
type: 1
|
|
951
|
+
}
|
|
1002
952
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
953
|
+
|
|
954
|
+
requestPaymentMessage.noteMessage.extendedTextMessage.contextInfo = {
|
|
955
|
+
...(message.contextInfo || {}),
|
|
956
|
+
...(message.mentions ? {
|
|
957
|
+
mentionedJid: message.mentions
|
|
958
|
+
} : {})
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
m.requestPaymentMessage = requestPaymentMessage
|
|
962
|
+
} else if ('stickerPack' in message) {
|
|
963
|
+
const {
|
|
964
|
+
stickers,
|
|
965
|
+
cover,
|
|
966
|
+
name,
|
|
967
|
+
publisher,
|
|
968
|
+
packId,
|
|
969
|
+
description
|
|
970
|
+
} = message.stickerPack
|
|
971
|
+
|
|
972
|
+
const { zip } = require('fflate')
|
|
973
|
+
const stickerData = {}
|
|
974
|
+
const stickerPromises = stickers.map(async (s, i) => {
|
|
975
|
+
const { stream } = await messages_media_1.getStream(s.sticker)
|
|
976
|
+
const buffer = await messages_media_1.toBuffer(stream)
|
|
977
|
+
const hash = crypto_2.sha256(buffer).toString('base64url')
|
|
978
|
+
const fileName = `${i.toString().padStart(2, '0')}_${hash}.webp`
|
|
979
|
+
stickerData[fileName] = [new Uint8Array(buffer), {
|
|
980
|
+
level: 0
|
|
981
|
+
}]
|
|
982
|
+
|
|
983
|
+
return {
|
|
984
|
+
fileName,
|
|
985
|
+
mimetype: 'image/webp',
|
|
986
|
+
isAnimated: s.isAnimated || false,
|
|
987
|
+
isLottie: s.isLottie || false,
|
|
988
|
+
emojis: s.emojis || [],
|
|
989
|
+
accessibilityLabel: s.accessibilityLabel || ''
|
|
990
|
+
}
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
const stickerMetadata = await Promise.all(stickerPromises)
|
|
994
|
+
|
|
995
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
996
|
+
zip(stickerData, (err, data) => {
|
|
997
|
+
if (err) {
|
|
998
|
+
reject(err)
|
|
999
|
+
} else {
|
|
1000
|
+
resolve(Buffer.from(data))
|
|
1001
|
+
}
|
|
1002
|
+
})
|
|
1003
|
+
})
|
|
1004
|
+
|
|
1005
|
+
const coverBuffer = await messages_media_1.toBuffer((await messages_media_1.getStream(cover)).stream)
|
|
1006
|
+
|
|
1007
|
+
const [stickerPackUpload, coverUpload] = await Promise.all([
|
|
1008
|
+
messages_media_1.encryptedStream(zipBuffer, 'sticker-pack', {
|
|
1009
|
+
logger: options.logger,
|
|
1010
|
+
opts: options.options
|
|
1011
|
+
}),
|
|
1012
|
+
prepareWAMessageMedia({
|
|
1013
|
+
image: coverBuffer
|
|
1014
|
+
}, {
|
|
1015
|
+
...options,
|
|
1016
|
+
mediaTypeOverride: 'image'
|
|
1017
|
+
})
|
|
1018
|
+
])
|
|
1019
|
+
|
|
1020
|
+
const stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
1021
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
1022
|
+
mediaType: 'sticker-pack',
|
|
1023
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
1024
|
+
})
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
const coverImage = coverUpload.imageMessage
|
|
1028
|
+
const imageDataHash = crypto_2.sha256(coverBuffer).toString('base64')
|
|
1029
|
+
const stickerPackId = packId || generics_1.generateMessageID()
|
|
1030
|
+
|
|
1031
|
+
m.stickerPackMessage = {
|
|
1032
|
+
name,
|
|
1033
|
+
publisher,
|
|
1034
|
+
stickerPackId,
|
|
1035
|
+
packDescription: description,
|
|
1036
|
+
stickerPackOrigin: WAProto_1.proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
|
|
1037
|
+
stickerPackSize: stickerPackUpload.fileLength,
|
|
1038
|
+
stickers: stickerMetadata,
|
|
1039
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
1040
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
1041
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
1042
|
+
directPath: stickerPackUploadResult.directPath,
|
|
1043
|
+
fileLength: stickerPackUpload.fileLength,
|
|
1044
|
+
mediaKeyTimestamp: generics_1.unixTimestampSeconds(),
|
|
1045
|
+
trayIconFileName: `${stickerPackId}.webp`,
|
|
1046
|
+
imageDataHash,
|
|
1047
|
+
thumbnailDirectPath: coverImage.directPath,
|
|
1048
|
+
thumbnailFileSha256: coverImage.fileSha256,
|
|
1049
|
+
thumbnailFileEncSha256: coverImage.fileEncSha256,
|
|
1050
|
+
thumbnailHeight: coverImage.height,
|
|
1051
|
+
thumbnailWidth: coverImage.width
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
m.stickerPackMessage.contextInfo = {
|
|
1055
|
+
...(message.contextInfo || {}),
|
|
1056
|
+
...(message.mentions ? {
|
|
1057
|
+
mentionedJid: message.mentions
|
|
1058
|
+
} : {})
|
|
1059
|
+
}
|
|
1060
|
+
} else if ('sharePhoneNumber' in message) {
|
|
1061
|
+
m.protocolMessage = {
|
|
1062
|
+
type: Types_1.WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
1063
|
+
}
|
|
1064
|
+
} else if ('requestPhoneNumber' in message) {
|
|
1065
|
+
m.requestPhoneNumberMessage = {}
|
|
1066
|
+
} else {
|
|
1067
|
+
const mess = await prepareWAMessageMedia(message, options)
|
|
1068
|
+
const [type] = Object.keys(mess)
|
|
1069
|
+
|
|
1070
|
+
mess[type].contextInfo = {
|
|
1071
|
+
...(message.contextInfo || {}),
|
|
1072
|
+
...(message.mentions ? {
|
|
1073
|
+
mentionedJid: message.mentions
|
|
1074
|
+
} : {})
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
m = mess
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if ('sections' in message && !!message.sections) {
|
|
1006
1081
|
const listMessage = {
|
|
1007
|
-
sections: message.sections,
|
|
1008
|
-
buttonText: message.buttonText,
|
|
1009
1082
|
title: message.title,
|
|
1083
|
+
buttonText: message.buttonText,
|
|
1010
1084
|
footerText: message.footer,
|
|
1011
1085
|
description: message.text,
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
m = { listMessage };
|
|
1015
|
-
}
|
|
1016
|
-
else if (hasNonNullishProperty(message, 'productList')) {
|
|
1017
|
-
// --- validasi 100% ---
|
|
1018
|
-
if (!message.businessOwnerJid) {
|
|
1019
|
-
throw new Boom('"businessOwnerJid" wajib diisi untuk mengirim productList', { statusCode: 400 });
|
|
1086
|
+
sections: message.sections,
|
|
1087
|
+
listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
|
|
1020
1088
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1089
|
+
|
|
1090
|
+
listMessage.contextInfo = {
|
|
1091
|
+
...(message.contextInfo || {}),
|
|
1092
|
+
...(message.mentions ? {
|
|
1093
|
+
mentionedJid: message.mentions
|
|
1094
|
+
} : {})
|
|
1023
1095
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
if (!product || !product.productId) {
|
|
1033
|
-
throw new Boom(`productList[${i}].products[${j}].productId wajib diisi`, { statusCode: 400 });
|
|
1034
|
-
}
|
|
1035
|
-
});
|
|
1036
|
-
});
|
|
1037
|
-
const thumbnail = message.thumbnail
|
|
1038
|
-
? await generateThumbnail(message.thumbnail, 'image')
|
|
1039
|
-
: null;
|
|
1040
|
-
m.listMessage = {
|
|
1096
|
+
|
|
1097
|
+
m = {
|
|
1098
|
+
listMessage
|
|
1099
|
+
}
|
|
1100
|
+
} else if ('productList' in message && !!message.productList) {
|
|
1101
|
+
const thumbnail = message.thumbnail ? await messages_media_1.generateThumbnail(message.thumbnail, 'image') : null
|
|
1102
|
+
|
|
1103
|
+
const listMessage = {
|
|
1041
1104
|
title: message.title,
|
|
1042
1105
|
buttonText: message.buttonText,
|
|
1043
1106
|
footerText: message.footer,
|
|
@@ -1050,998 +1113,763 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1050
1113
|
},
|
|
1051
1114
|
businessOwnerJid: message.businessOwnerJid
|
|
1052
1115
|
},
|
|
1053
|
-
listType: proto.Message.ListMessage.ListType.PRODUCT_LIST
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
else if (hasNonNullishProperty(message, 'payment')) {
|
|
1057
|
-
// --- validasi 100% ---
|
|
1058
|
-
const p = message.payment;
|
|
1059
|
-
if (!p || typeof p !== 'object') {
|
|
1060
|
-
throw new Boom('"payment" harus berupa object', { statusCode: 400 });
|
|
1061
|
-
}
|
|
1062
|
-
if (!p.from) {
|
|
1063
|
-
throw new Boom('"payment.from" (JID tujuan pembayaran) wajib diisi', { statusCode: 400 });
|
|
1064
|
-
}
|
|
1065
|
-
if (typeof p.amount !== 'number' || !(p.amount > 0)) {
|
|
1066
|
-
throw new Boom('"payment.amount" wajib berupa angka lebih dari 0', { statusCode: 400 });
|
|
1116
|
+
listType: WAProto_1.proto.Message.ListMessage.ListType.PRODUCT_LIST
|
|
1067
1117
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1118
|
+
|
|
1119
|
+
listMessage.contextInfo = {
|
|
1120
|
+
...(message.contextInfo || {}),
|
|
1121
|
+
...(message.mentions ? {
|
|
1122
|
+
mentionedJid: message.mentions
|
|
1123
|
+
} : {})
|
|
1070
1124
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1125
|
+
|
|
1126
|
+
m = {
|
|
1127
|
+
listMessage
|
|
1073
1128
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1129
|
+
} else if ('buttons' in message && !!message.buttons) {
|
|
1130
|
+
const buttonsMessage = {
|
|
1131
|
+
buttons: message.buttons.map(b => ({
|
|
1132
|
+
...b,
|
|
1133
|
+
type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE
|
|
1134
|
+
}))
|
|
1076
1135
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
expiryTimestamp: p.expiry ?? 0,
|
|
1085
|
-
amount1000: p.amount * 1000,
|
|
1086
|
-
currencyCodeIso4217: currency,
|
|
1087
|
-
requestFrom: p.from,
|
|
1088
|
-
noteMessage: {
|
|
1089
|
-
extendedTextMessage: { text: p.note || '' }
|
|
1090
|
-
},
|
|
1091
|
-
background: {
|
|
1092
|
-
placeholderArgb: p.image?.placeholderArgb ?? 4278190080,
|
|
1093
|
-
textArgb: p.image?.textArgb ?? 4294967295,
|
|
1094
|
-
subtextArgb: p.image?.subtextArgb ?? 4294967295,
|
|
1095
|
-
type: 1
|
|
1136
|
+
|
|
1137
|
+
if ('text' in message) {
|
|
1138
|
+
buttonsMessage.contentText = message.text
|
|
1139
|
+
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType.EMPTY
|
|
1140
|
+
} else {
|
|
1141
|
+
if ('caption' in message) {
|
|
1142
|
+
buttonsMessage.contentText = message.caption
|
|
1096
1143
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
if (
|
|
1106
|
-
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
throw new Boom(`buttonLocation.sections[${i}].rows harus berupa array yang tidak kosong`, { statusCode: 400 });
|
|
1124
|
-
}
|
|
1125
|
-
section.rows.forEach((row, j) => {
|
|
1126
|
-
if (!row || !row.id || !row.title) {
|
|
1127
|
-
throw new Boom(`buttonLocation.sections[${i}].rows[${j}] wajib punya "id" dan "title"`, { statusCode: 400 });
|
|
1128
|
-
}
|
|
1129
|
-
});
|
|
1130
|
-
});
|
|
1131
|
-
const nativeParams = JSON.stringify({
|
|
1132
|
-
sections: bl.sections.map(section => ({
|
|
1133
|
-
title: section.title || '',
|
|
1134
|
-
rows: section.rows.map(row => ({
|
|
1135
|
-
title: row.title || '',
|
|
1136
|
-
description: row.description || '',
|
|
1137
|
-
id: row.id || ''
|
|
1138
|
-
}))
|
|
1139
|
-
}))
|
|
1140
|
-
});
|
|
1141
|
-
listButton.push({
|
|
1142
|
-
buttonId: 'list_menu',
|
|
1143
|
-
buttonText: { displayText: bl.listButtonText || '📄 Pilih Menu' },
|
|
1144
|
-
type: 1,
|
|
1145
|
-
nativeFlowInfo: {
|
|
1146
|
-
name: 'single_select',
|
|
1147
|
-
paramsJson: nativeParams,
|
|
1148
|
-
buttonParamsJson: nativeParams
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1144
|
+
|
|
1145
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase()
|
|
1146
|
+
|
|
1147
|
+
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType[type]
|
|
1148
|
+
|
|
1149
|
+
Object.assign(buttonsMessage, m)
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if ('footer' in message && !!message.footer) {
|
|
1153
|
+
buttonsMessage.footerText = message.footer
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if ('title' in message && !!message.title) {
|
|
1157
|
+
buttonsMessage.text = message.title
|
|
1158
|
+
buttonsMessage.headerType = WAProto_1.proto.Message.ButtonsMessage.HeaderType.TEXT
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
buttonsMessage.contextInfo = {
|
|
1162
|
+
...(message.contextInfo || {}),
|
|
1163
|
+
...(message.mentions ? {
|
|
1164
|
+
mentionedJid: message.mentions
|
|
1165
|
+
} : {})
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
m = {
|
|
1169
|
+
buttonsMessage
|
|
1151
1170
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1171
|
+
} else if ('template' in message && !!message.templateButtons) {
|
|
1172
|
+
const hydratedTemplate = {
|
|
1173
|
+
hydratedButtons: message.templateButtons
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
if ('text' in message) {
|
|
1177
|
+
hydratedTemplate.hydratedContentText = message.text
|
|
1178
|
+
} else {
|
|
1179
|
+
if ('caption' in message) {
|
|
1180
|
+
hydratedTemplate.hydratedContentText = message.caption
|
|
1155
1181
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
buttonText: { displayText: btn.displayText || btn.name || 'Button' },
|
|
1159
|
-
type: 1
|
|
1160
|
-
};
|
|
1161
|
-
});
|
|
1162
|
-
if (listButton.length === 0 && extraButtons.length === 0) {
|
|
1163
|
-
throw new Boom('"buttonLocation" membutuhkan minimal 1 "sections" atau 1 "extraButtons"', { statusCode: 400 });
|
|
1182
|
+
|
|
1183
|
+
Object.assign(msg, m)
|
|
1164
1184
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
degreesLatitude: bl.latitude,
|
|
1169
|
-
degreesLongitude: bl.longitude,
|
|
1170
|
-
name: bl.name || 'Location',
|
|
1171
|
-
address: bl.address || '',
|
|
1172
|
-
jpegThumbnail: thumb
|
|
1173
|
-
},
|
|
1174
|
-
contentText: bl.text || '',
|
|
1175
|
-
footerText: bl.footer || '',
|
|
1176
|
-
buttons: [...listButton, ...extraButtons],
|
|
1177
|
-
headerType: ButtonHeaderType.LOCATION,
|
|
1178
|
-
viewOnce: true
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
else if (hasNonNullishProperty(message, 'orderStatus')) {
|
|
1183
|
-
// --- validasi 100% ---
|
|
1184
|
-
const os = message.orderStatus;
|
|
1185
|
-
if (!os || typeof os !== 'object') {
|
|
1186
|
-
throw new Boom('"orderStatus" harus berupa object', { statusCode: 400 });
|
|
1185
|
+
|
|
1186
|
+
if ('footer' in message && !!message.footer) {
|
|
1187
|
+
hydratedTemplate.hydratedFooterText = message.footer
|
|
1187
1188
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1189
|
+
|
|
1190
|
+
hydratedTemplate.contextInfo = {
|
|
1191
|
+
...(message.contextInfo || {}),
|
|
1192
|
+
...(message.mentions ? {
|
|
1193
|
+
mentionedJid: message.mentions
|
|
1194
|
+
} : {})
|
|
1190
1195
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1196
|
+
|
|
1197
|
+
m = {
|
|
1198
|
+
templateMessage: {
|
|
1199
|
+
hydratedTemplate
|
|
1200
|
+
}
|
|
1193
1201
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1202
|
+
} else if ('interactive' in message && !!message.interactive) {
|
|
1203
|
+
const interactiveMessage = {
|
|
1204
|
+
nativeFlowMessage: {
|
|
1205
|
+
buttons: message.interactive
|
|
1206
|
+
}
|
|
1196
1207
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1208
|
+
|
|
1209
|
+
if ('text' in message) {
|
|
1210
|
+
interactiveMessage.body = {
|
|
1211
|
+
text: message.text
|
|
1212
|
+
},
|
|
1213
|
+
interactiveMessage.header = {
|
|
1214
|
+
title: message.title,
|
|
1215
|
+
subtitle: message.subtitle,
|
|
1216
|
+
hasMediaAttachment: false
|
|
1217
|
+
}
|
|
1218
|
+
} else {
|
|
1219
|
+
if ('caption' in message) {
|
|
1220
|
+
interactiveMessage.body = {
|
|
1221
|
+
text: message.caption
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
interactiveMessage.header = {
|
|
1225
|
+
title: message.title,
|
|
1226
|
+
subtitle: message.subtitle,
|
|
1227
|
+
hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
|
|
1228
|
+
...Object.assign(interactiveMessage, m)
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1199
1231
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1232
|
+
|
|
1233
|
+
if ('footer' in message && !!message.footer) {
|
|
1234
|
+
interactiveMessage.footer = {
|
|
1235
|
+
text: message.footer
|
|
1236
|
+
}
|
|
1202
1237
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1238
|
+
|
|
1239
|
+
interactiveMessage.contextInfo = {
|
|
1240
|
+
...(message.contextInfo || {}),
|
|
1241
|
+
...(message.mentions ? {
|
|
1242
|
+
mentionedJid: message.mentions
|
|
1243
|
+
} : {})
|
|
1206
1244
|
}
|
|
1207
|
-
|
|
1245
|
+
|
|
1208
1246
|
m = {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
header: {
|
|
1217
|
-
title: os.title || 'Order Status',
|
|
1218
|
-
hasMediaAttachment: true,
|
|
1219
|
-
...media
|
|
1220
|
-
},
|
|
1221
|
-
body: {
|
|
1222
|
-
text: os.text || 'Silakan cek status pesanan Anda.'
|
|
1223
|
-
},
|
|
1224
|
-
footer: {
|
|
1225
|
-
text: os.footer || 'Powered by @pontalabs/baileys'
|
|
1226
|
-
},
|
|
1227
|
-
nativeFlowMessage: {
|
|
1228
|
-
buttons: [
|
|
1229
|
-
{
|
|
1230
|
-
name: 'order_status',
|
|
1231
|
-
buttonParamsJson: JSON.stringify({
|
|
1232
|
-
reference_id: os.referenceId,
|
|
1233
|
-
order: {
|
|
1234
|
-
status: os.status || 'PROCESSING',
|
|
1235
|
-
subtotal: {
|
|
1236
|
-
value: os.subtotalValue || 0,
|
|
1237
|
-
offset: os.subtotalOffset || 100
|
|
1238
|
-
},
|
|
1239
|
-
tax: {
|
|
1240
|
-
value: os.taxValue || 0,
|
|
1241
|
-
offset: os.taxOffset || 100
|
|
1242
|
-
},
|
|
1243
|
-
currency: os.currency || 'IDR'
|
|
1244
|
-
}
|
|
1245
|
-
})
|
|
1246
|
-
}
|
|
1247
|
-
]
|
|
1248
|
-
}
|
|
1249
|
-
})
|
|
1250
|
-
}
|
|
1247
|
+
interactiveMessage
|
|
1248
|
+
}
|
|
1249
|
+
} else if ('shop' in message && !!message.shop) {
|
|
1250
|
+
const interactiveMessage = {
|
|
1251
|
+
shopStorefrontMessage: {
|
|
1252
|
+
surface: message.shop.surface,
|
|
1253
|
+
id: message.shop.id
|
|
1251
1254
|
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
displayText: buttonText || '👉🏻 Click',
|
|
1263
|
-
id: button.id
|
|
1264
|
-
}
|
|
1265
|
-
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if ('text' in message) {
|
|
1258
|
+
interactiveMessage.body = {
|
|
1259
|
+
text: message.text
|
|
1260
|
+
},
|
|
1261
|
+
interactiveMessage.header = {
|
|
1262
|
+
title: message.title,
|
|
1263
|
+
subtitle: message.subtitle,
|
|
1264
|
+
hasMediaAttachment: false
|
|
1266
1265
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
displayText: buttonText || '🌐 Visit',
|
|
1272
|
-
url: button.url
|
|
1273
|
-
}
|
|
1274
|
-
};
|
|
1266
|
+
} else {
|
|
1267
|
+
if ('caption' in message) {
|
|
1268
|
+
interactiveMessage.body = {
|
|
1269
|
+
text: message.caption
|
|
1275
1270
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1271
|
+
|
|
1272
|
+
interactiveMessage.header = {
|
|
1273
|
+
title: message.title,
|
|
1274
|
+
subtitle: message.subtitle,
|
|
1275
|
+
hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
|
|
1276
|
+
...Object.assign(interactiveMessage, m)
|
|
1284
1277
|
}
|
|
1285
|
-
button.index = button.index || i;
|
|
1286
|
-
return button;
|
|
1287
|
-
})
|
|
1288
|
-
};
|
|
1289
|
-
if (hasOptionalProperty(message, 'text')) {
|
|
1290
|
-
hydratedTemplate.hydratedContentText = message.text;
|
|
1291
|
-
}
|
|
1292
|
-
else {
|
|
1293
|
-
if (hasOptionalProperty(message, 'caption')) {
|
|
1294
|
-
hydratedTemplate.hydratedTitleText = message.title;
|
|
1295
|
-
hydratedTemplate.hydratedContentText = message.caption;
|
|
1296
1278
|
}
|
|
1297
|
-
;
|
|
1298
|
-
Object.assign(hydratedTemplate, m);
|
|
1299
1279
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1280
|
+
|
|
1281
|
+
if ('footer' in message && !!message.footer) {
|
|
1282
|
+
interactiveMessage.footer = {
|
|
1283
|
+
text: message.footer
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
interactiveMessage.contextInfo = {
|
|
1288
|
+
...(message.contextInfo || {}),
|
|
1289
|
+
...(message.mentions ? {
|
|
1290
|
+
mentionedJid: message.mentions
|
|
1291
|
+
} : {})
|
|
1302
1292
|
}
|
|
1303
|
-
|
|
1293
|
+
|
|
1304
1294
|
m = {
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
}
|
|
1309
|
-
};
|
|
1310
|
-
}
|
|
1311
|
-
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
1295
|
+
interactiveMessage
|
|
1296
|
+
}
|
|
1297
|
+
} else if ('collection' in message && !!message.collection) {
|
|
1312
1298
|
const interactiveMessage = {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
if (hasOptionalProperty(message, 'caption')) {
|
|
1334
|
-
const isValidHeader = hasValidInteractiveHeader(m);
|
|
1335
|
-
if (!isValidHeader) {
|
|
1336
|
-
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
1299
|
+
collectionMessage: {
|
|
1300
|
+
bizJid: message.collection.bizJid,
|
|
1301
|
+
id: message.collection.id,
|
|
1302
|
+
messageVersion: message?.collection?.version
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
if ('text' in message) {
|
|
1307
|
+
interactiveMessage.body = {
|
|
1308
|
+
text: message.text
|
|
1309
|
+
},
|
|
1310
|
+
interactiveMessage.header = {
|
|
1311
|
+
title: message.title,
|
|
1312
|
+
subtitle: message.subtitle,
|
|
1313
|
+
hasMediaAttachment: false
|
|
1314
|
+
}
|
|
1315
|
+
} else {
|
|
1316
|
+
if ('caption' in message) {
|
|
1317
|
+
interactiveMessage.body = {
|
|
1318
|
+
text: message.caption
|
|
1337
1319
|
}
|
|
1338
1320
|
interactiveMessage.header = {
|
|
1339
|
-
title: message.title
|
|
1340
|
-
subtitle: message.subtitle
|
|
1341
|
-
hasMediaAttachment:
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
if (hasOptionalProperty(message, 'thumbnail') && !!message.thumbnail) {
|
|
1346
|
-
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
1321
|
+
title: message.title,
|
|
1322
|
+
subtitle: message.subtitle,
|
|
1323
|
+
hasMediaAttachment: message.hasMediaAttachment ? message.hasMediaAttachment : false,
|
|
1324
|
+
...Object.assign(interactiveMessage, m)
|
|
1325
|
+
}
|
|
1347
1326
|
}
|
|
1348
|
-
Object.assign(interactiveMessage.header, m);
|
|
1349
1327
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
audio: message.audioFooter
|
|
1353
|
-
}, options);
|
|
1328
|
+
|
|
1329
|
+
if ('footer' in message && !!message.footer) {
|
|
1354
1330
|
interactiveMessage.footer = {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
};
|
|
1331
|
+
text: message.footer
|
|
1332
|
+
}
|
|
1358
1333
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1334
|
+
|
|
1335
|
+
interactiveMessage.contextInfo = {
|
|
1336
|
+
...(message.contextInfo || {}),
|
|
1337
|
+
...(message.mentions ? {
|
|
1338
|
+
mentionedJid: message.mentions
|
|
1339
|
+
} : {})
|
|
1361
1340
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1341
|
+
|
|
1342
|
+
m = {
|
|
1343
|
+
interactiveMessage
|
|
1344
|
+
}
|
|
1345
|
+
} else if ('cards' in message && !!message.cards) {
|
|
1346
|
+
const slides = await Promise.all(message.cards.map(async (slide) => {
|
|
1347
|
+
const {
|
|
1348
|
+
image,
|
|
1349
|
+
video,
|
|
1350
|
+
product,
|
|
1351
|
+
title,
|
|
1352
|
+
body,
|
|
1353
|
+
footer,
|
|
1354
|
+
buttons
|
|
1355
|
+
} = slide
|
|
1356
|
+
let header
|
|
1357
|
+
|
|
1358
|
+
if (product) {
|
|
1359
|
+
const { imageMessage } = await prepareWAMessageMedia({
|
|
1360
|
+
image: product.productImage,
|
|
1361
|
+
...options
|
|
1362
|
+
}, options)
|
|
1363
|
+
header = {
|
|
1364
|
+
productMessage: {
|
|
1365
|
+
product: {
|
|
1366
|
+
...product,
|
|
1367
|
+
productImage: imageMessage,
|
|
1368
|
+
},
|
|
1369
|
+
...slide
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
} else if (image) {
|
|
1373
|
+
header = await prepareWAMessageMedia({
|
|
1374
|
+
image: image,
|
|
1375
|
+
...options
|
|
1376
|
+
}, options)
|
|
1377
|
+
} else if (video) {
|
|
1378
|
+
header = await prepareWAMessageMedia({
|
|
1379
|
+
video: video,
|
|
1380
|
+
...options
|
|
1381
|
+
}, options)
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const msg = {
|
|
1385
|
+
header: {
|
|
1386
|
+
title,
|
|
1387
|
+
hasMediaAttachment: true,
|
|
1388
|
+
...header
|
|
1389
|
+
},
|
|
1390
|
+
body: {
|
|
1391
|
+
text: body
|
|
1392
|
+
},
|
|
1393
|
+
footer: {
|
|
1394
|
+
text: footer
|
|
1395
|
+
},
|
|
1396
|
+
nativeFlowMessage: {
|
|
1397
|
+
buttons,
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
return msg
|
|
1402
|
+
}))
|
|
1403
|
+
|
|
1365
1404
|
const interactiveMessage = {
|
|
1366
1405
|
carouselMessage: {
|
|
1367
|
-
cards:
|
|
1368
|
-
let carouselHeader = {};
|
|
1369
|
-
if (hasNonNullishProperty(card, 'product')) {
|
|
1370
|
-
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1371
|
-
}
|
|
1372
|
-
else {
|
|
1373
|
-
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1374
|
-
}
|
|
1375
|
-
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
1376
|
-
if (!isValidHeader) {
|
|
1377
|
-
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1378
|
-
}
|
|
1379
|
-
const carouselCard = {
|
|
1380
|
-
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1381
|
-
};
|
|
1382
|
-
if (hasOptionalProperty(card, 'text')) {
|
|
1383
|
-
carouselCard.body = { text: card.text };
|
|
1384
|
-
}
|
|
1385
|
-
else {
|
|
1386
|
-
if (hasOptionalProperty(card, 'caption')) {
|
|
1387
|
-
carouselCard.header = {
|
|
1388
|
-
title: card.title || '',
|
|
1389
|
-
subtitle: card.subtitle || '',
|
|
1390
|
-
hasMediaAttachment: isValidHeader
|
|
1391
|
-
};
|
|
1392
|
-
carouselCard.body = { text: card.caption };
|
|
1393
|
-
}
|
|
1394
|
-
if (hasOptionalProperty(card, 'thumbnail') && !!card.thumbnail) {
|
|
1395
|
-
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1396
|
-
}
|
|
1397
|
-
Object.assign(carouselCard.header, carouselHeader);
|
|
1398
|
-
}
|
|
1399
|
-
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1400
|
-
const { audioMessage } = await prepareWAMessageMedia({
|
|
1401
|
-
audio: card.audioFooter
|
|
1402
|
-
}, options);
|
|
1403
|
-
carouselCard.footer = {
|
|
1404
|
-
audioMessage,
|
|
1405
|
-
hasMediaAttachment: true
|
|
1406
|
-
};
|
|
1407
|
-
}
|
|
1408
|
-
else if (hasOptionalProperty(card, 'footer')) {
|
|
1409
|
-
carouselCard.footer = { text: card.footer };
|
|
1410
|
-
}
|
|
1411
|
-
return carouselCard;
|
|
1412
|
-
})),
|
|
1413
|
-
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1414
|
-
messageVersion: 1
|
|
1406
|
+
cards: slides
|
|
1415
1407
|
}
|
|
1416
|
-
};
|
|
1417
|
-
if (hasOptionalProperty(message, 'text')) {
|
|
1418
|
-
interactiveMessage.body = { text: message.text };
|
|
1419
1408
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
value: 1000
|
|
1431
|
-
},
|
|
1432
|
-
amount1000: 1000,
|
|
1433
|
-
currencyCodeIso4217: 'IDR',
|
|
1434
|
-
expiryTimestamp: Date.now(),
|
|
1435
|
-
noteMessage: m,
|
|
1436
|
-
requestFrom: message.requestPaymentFrom,
|
|
1437
|
-
...message
|
|
1438
|
-
};
|
|
1439
|
-
delete requestPaymentMessage.requestPaymentFrom;
|
|
1440
|
-
if (hasNonNullishProperty(m, 'extendedTextMessage') || hasNonNullishProperty(m, 'stickerMessage')) {
|
|
1441
|
-
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
1442
|
-
}
|
|
1443
|
-
else {
|
|
1444
|
-
throw new Boom('Invalid message type for request payment note message', { statusCode: 400 });
|
|
1445
|
-
}
|
|
1446
|
-
m = { requestPaymentMessage };
|
|
1447
|
-
}
|
|
1448
|
-
else if (hasNonNullishProperty(message, 'invoiceNote')) {
|
|
1449
|
-
const attachment = m.imageMessage || m.documentMessage;
|
|
1450
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1451
|
-
const invoiceMessage = {
|
|
1452
|
-
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1453
|
-
note: message.invoiceNote
|
|
1454
|
-
};
|
|
1455
|
-
if (attachment) {
|
|
1456
|
-
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1457
|
-
Object.assign(invoiceMessage, {
|
|
1458
|
-
attachmentDirectPath: directPath,
|
|
1459
|
-
attachmentFileEncSha256: fileEncSha256,
|
|
1460
|
-
attachmentFileSha256: fileSha256,
|
|
1461
|
-
attachmentJpegThumbnail: jpegThumbnail,
|
|
1462
|
-
attachmentMediaKey: mediaKey,
|
|
1463
|
-
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1464
|
-
attachmentMimetype: mimetype,
|
|
1465
|
-
token: generateMessageIDV2()
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
else {
|
|
1469
|
-
throw new Boom('Invalid media type for invoice message', { statusCode: 400 });
|
|
1470
|
-
}
|
|
1471
|
-
m = { invoiceMessage };
|
|
1472
|
-
}
|
|
1473
|
-
if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
|
|
1474
|
-
const messageType = Object.keys(m)[0];
|
|
1475
|
-
const key = m[messageType];
|
|
1476
|
-
const content = message.externalAdReply;
|
|
1477
|
-
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
1478
|
-
throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
|
|
1479
|
-
}
|
|
1480
|
-
if (!content.url || typeof content.url !== 'string' || !/^https?:\/\//i.test(content.url)) {
|
|
1481
|
-
options.logger?.warn({ givenUrl: content.url }, 'externalAdReply: field "url" kosong/tidak valid (harus diawali http:// atau https://) — fallback ke DONATE_URL, link produk asli TIDAK terkirim. Cek kode yang mengisi `url` di externalAdReply.');
|
|
1482
|
-
content.url = DONATE_URL;
|
|
1483
|
-
}
|
|
1484
|
-
const externalAdReply = {
|
|
1485
|
-
...content,
|
|
1486
|
-
body: content.body,
|
|
1487
|
-
mediaType: content.mediaType || 1,
|
|
1488
|
-
mediaUrl: content.url,
|
|
1489
|
-
renderLargerThumbnail: content.largeThumbnail,
|
|
1490
|
-
sourceUrl: content.url,
|
|
1491
|
-
thumbnail: content.thumbnail,
|
|
1492
|
-
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1493
|
-
title: content.title || LIBRARY_NAME
|
|
1494
|
-
};
|
|
1495
|
-
delete externalAdReply.subTitle;
|
|
1496
|
-
delete externalAdReply.largeThumbnail;
|
|
1497
|
-
delete externalAdReply.url;
|
|
1498
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1499
|
-
key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
|
|
1500
|
-
}
|
|
1501
|
-
else if (key) {
|
|
1502
|
-
key.contextInfo = { externalAdReply };
|
|
1409
|
+
|
|
1410
|
+
if ('text' in message) {
|
|
1411
|
+
interactiveMessage.body = {
|
|
1412
|
+
text: message.text
|
|
1413
|
+
},
|
|
1414
|
+
interactiveMessage.header = {
|
|
1415
|
+
title: message.title,
|
|
1416
|
+
subtitle: message.subtitle,
|
|
1417
|
+
hasMediaAttachment: false
|
|
1418
|
+
}
|
|
1503
1419
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
const key = m[messageType];
|
|
1509
|
-
if (key && 'contextInfo' in key) {
|
|
1510
|
-
key.contextInfo = key.contextInfo || {};
|
|
1511
|
-
if (message.mentions?.length) {
|
|
1512
|
-
key.contextInfo.mentionedJid = message.mentions;
|
|
1513
|
-
}
|
|
1514
|
-
if (message.mentionAll) {
|
|
1515
|
-
key.contextInfo.nonJidMentions = 1;
|
|
1420
|
+
|
|
1421
|
+
if ('footer' in message && !!message.footer) {
|
|
1422
|
+
interactiveMessage.footer = {
|
|
1423
|
+
text: message.footer
|
|
1516
1424
|
}
|
|
1517
1425
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1426
|
+
|
|
1427
|
+
interactiveMessage.contextInfo = {
|
|
1428
|
+
...(message.contextInfo || {}),
|
|
1429
|
+
...(message.mentions ? {
|
|
1430
|
+
mentionedJid: message.mentions
|
|
1431
|
+
} : {})
|
|
1523
1432
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const key = m[messageType];
|
|
1528
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1529
|
-
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
1530
|
-
}
|
|
1531
|
-
else if (key) {
|
|
1532
|
-
key.contextInfo = message.contextInfo;
|
|
1433
|
+
|
|
1434
|
+
m = {
|
|
1435
|
+
interactiveMessage
|
|
1533
1436
|
}
|
|
1534
1437
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
const key = m[messageType];
|
|
1538
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1539
|
-
key.contextInfo.isGroupStatus = message.groupStatus;
|
|
1540
|
-
}
|
|
1541
|
-
else if (key) {
|
|
1542
|
-
key.contextInfo = {
|
|
1543
|
-
isGroupStatus: message.groupStatus
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
m = { groupStatusMessageV2: { message: m } };
|
|
1547
|
-
delete message.groupStatus;
|
|
1548
|
-
}
|
|
1549
|
-
if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
|
|
1550
|
-
const messageType = Object.keys(m)[0];
|
|
1551
|
-
const key = m[messageType];
|
|
1552
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1553
|
-
key.contextInfo.isSpoiler = message.spoiler;
|
|
1554
|
-
}
|
|
1555
|
-
else if (key) {
|
|
1556
|
-
key.contextInfo = {
|
|
1557
|
-
isSpoiler: message.spoiler
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
m = { spoilerMessage: { message: m } };
|
|
1561
|
-
delete message.spoiler;
|
|
1562
|
-
}
|
|
1563
|
-
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1564
|
-
if (!m.interactiveMessage) {
|
|
1565
|
-
throw new Boom('Invalid message type for template', { statusCode: 400 });
|
|
1566
|
-
}
|
|
1438
|
+
|
|
1439
|
+
if ('ephemeral' in message && !!message.ephemeral) {
|
|
1567
1440
|
m = {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
templateId: message.id || 'template-' + Date.now()
|
|
1441
|
+
ephemeralMessage: {
|
|
1442
|
+
message: m
|
|
1571
1443
|
}
|
|
1572
|
-
}
|
|
1573
|
-
delete message.interactiveAsTemplate;
|
|
1574
|
-
}
|
|
1575
|
-
if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
|
|
1576
|
-
m = { ephemeralMessage: { message: m } };
|
|
1577
|
-
delete message.ephemeral;
|
|
1578
|
-
}
|
|
1579
|
-
if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
|
|
1580
|
-
m = { lottieStickerMessage: { message: m } };
|
|
1581
|
-
}
|
|
1582
|
-
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1583
|
-
m = { viewOnceMessage: { message: m } };
|
|
1444
|
+
}
|
|
1584
1445
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1446
|
+
|
|
1447
|
+
if ('viewOnce' in message && !!message.viewOnce) {
|
|
1448
|
+
m = {
|
|
1449
|
+
viewOnceMessageV2: {
|
|
1450
|
+
message: m
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1588
1453
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1454
|
+
|
|
1455
|
+
if ('viewOnceExt' in message && !!message.viewOnceExt) {
|
|
1456
|
+
m = {
|
|
1457
|
+
viewOnceMessageV2Extension: {
|
|
1458
|
+
message: m
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1592
1461
|
}
|
|
1593
|
-
|
|
1462
|
+
|
|
1463
|
+
if ('edit' in message) {
|
|
1594
1464
|
m = {
|
|
1595
1465
|
protocolMessage: {
|
|
1596
1466
|
key: message.edit,
|
|
1597
1467
|
editedMessage: m,
|
|
1598
1468
|
timestampMs: Date.now(),
|
|
1599
|
-
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1469
|
+
type: Types_1.WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1600
1470
|
}
|
|
1601
|
-
};
|
|
1602
|
-
}
|
|
1603
|
-
if (shouldIncludeReportingToken(m)) {
|
|
1604
|
-
m.messageContextInfo = m.messageContextInfo || {};
|
|
1605
|
-
if (!m.messageContextInfo.messageSecret) {
|
|
1606
|
-
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1607
1471
|
}
|
|
1608
1472
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1473
|
+
|
|
1474
|
+
return Types_1.WAProto.Message.fromObject(m)
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
const generateWAMessageFromContent = (jid, message, options) => {
|
|
1614
1478
|
if (!options.timestamp) {
|
|
1615
|
-
options.timestamp = new Date()
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
const
|
|
1619
|
-
const key = getContentType(innerMessage)
|
|
1620
|
-
const timestamp = unixTimestampSeconds(options.timestamp)
|
|
1621
|
-
const
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1479
|
+
options.timestamp = new Date()
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const innerMessage = normalizeMessageContent(message)
|
|
1483
|
+
const key = getContentType(innerMessage)
|
|
1484
|
+
const timestamp = generics_1.unixTimestampSeconds(options.timestamp)
|
|
1485
|
+
const {
|
|
1486
|
+
quoted,
|
|
1487
|
+
userJid
|
|
1488
|
+
} = options
|
|
1489
|
+
|
|
1490
|
+
if (quoted && !WABinary_1.isJidNewsletter(jid)) {
|
|
1491
|
+
const participant = quoted.key.fromMe ? userJid : quoted.participant || quoted.key.participant || quoted.key.remoteJid
|
|
1492
|
+
let quotedMsg = normalizeMessageContent(quoted.message)
|
|
1493
|
+
const msgType = getContentType(quotedMsg)
|
|
1494
|
+
|
|
1495
|
+
quotedMsg = WAProto_1.proto.Message.fromObject({
|
|
1496
|
+
[msgType]: quotedMsg[msgType]
|
|
1497
|
+
})
|
|
1498
|
+
|
|
1499
|
+
const quotedContent = quotedMsg[msgType]
|
|
1632
1500
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
1633
|
-
delete quotedContent.contextInfo
|
|
1501
|
+
delete quotedContent.contextInfo
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
let requestPayment
|
|
1505
|
+
if (key === 'requestPaymentMessage') {
|
|
1506
|
+
if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage) {
|
|
1507
|
+
requestPayment = innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage
|
|
1508
|
+
} else if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.stickerMessage) {
|
|
1509
|
+
requestPayment = innerMessage.requestPaymentMessage?.noteMessage?.stickerMessage
|
|
1510
|
+
}
|
|
1634
1511
|
}
|
|
1635
|
-
|
|
1636
|
-
contextInfo
|
|
1637
|
-
contextInfo.
|
|
1638
|
-
contextInfo.
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
if (
|
|
1642
|
-
contextInfo.remoteJid = quoted.key.remoteJid
|
|
1512
|
+
|
|
1513
|
+
const contextInfo = (key === 'requestPaymentMessage' ? requestPayment?.contextInfo : innerMessage[key].contextInfo) || {}
|
|
1514
|
+
contextInfo.participant = WABinary_1.jidNormalizedUser(participant)
|
|
1515
|
+
contextInfo.stanzaId = quoted.key.id
|
|
1516
|
+
contextInfo.quotedMessage = quotedMsg
|
|
1517
|
+
|
|
1518
|
+
if (jid !== quoted.key.remoteJid) {
|
|
1519
|
+
contextInfo.remoteJid = quoted.key.remoteJid
|
|
1643
1520
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1521
|
+
|
|
1522
|
+
if (contextInfo.quotedMessage) {
|
|
1523
|
+
contextInfo.quotedType = WAProto_1.proto.ContextInfo.QuotedType.EXPLICIT
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
if (key === 'requestPaymentMessage' && requestPayment) {
|
|
1527
|
+
requestPayment.contextInfo = contextInfo
|
|
1528
|
+
} else {
|
|
1529
|
+
innerMessage[key].contextInfo = contextInfo
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (key !== 'protocolMessage' && key !== 'ephemeralMessage' && !WABinary_1.isJidNewsletter(jid)) {
|
|
1534
|
+
message.messageContextInfo = {
|
|
1535
|
+
messageSecret: crypto_1.randomBytes(32),
|
|
1536
|
+
...message.messageContextInfo
|
|
1647
1537
|
}
|
|
1648
1538
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
!!options?.ephemeralExpiration &&
|
|
1652
|
-
// and it's not a protocol message -- delete, toggle disappear message
|
|
1653
|
-
key !== 'protocolMessage' &&
|
|
1654
|
-
// already not converted to disappearing message
|
|
1655
|
-
key !== 'ephemeralMessage' &&
|
|
1656
|
-
// newsletters don't support ephemeral messages
|
|
1657
|
-
!isNewsletter) {
|
|
1658
|
-
/* @ts-ignore */
|
|
1539
|
+
|
|
1540
|
+
if (!!options?.ephemeralExpiration && key !== 'protocolMessage' && key !== 'ephemeralMessage' && !WABinary_1.isJidNewsletter(jid)) {
|
|
1659
1541
|
innerMessage[key].contextInfo = {
|
|
1660
1542
|
...(innerMessage[key].contextInfo || {}),
|
|
1661
|
-
expiration: options.ephemeralExpiration
|
|
1662
|
-
|
|
1663
|
-
};
|
|
1664
|
-
}
|
|
1665
|
-
if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1666
|
-
messageContextInfo.deviceListMetadata = {
|
|
1667
|
-
recipientKeyHash: randomBytes(10),
|
|
1668
|
-
recipientTimestamp: unixTimestampSeconds()
|
|
1669
|
-
};
|
|
1670
|
-
messageContextInfo.deviceListMetadataVersion = 2;
|
|
1543
|
+
expiration: options.ephemeralExpiration
|
|
1544
|
+
}
|
|
1671
1545
|
}
|
|
1672
|
-
|
|
1546
|
+
|
|
1547
|
+
message = Types_1.WAProto.Message.fromObject(message)
|
|
1548
|
+
|
|
1673
1549
|
const messageJSON = {
|
|
1674
1550
|
key: {
|
|
1675
1551
|
remoteJid: jid,
|
|
1676
1552
|
fromMe: true,
|
|
1677
|
-
id: options?.messageId ||
|
|
1553
|
+
id: options?.messageId || generics_1.generateMessageID()
|
|
1678
1554
|
},
|
|
1679
1555
|
message: message,
|
|
1680
1556
|
messageTimestamp: timestamp,
|
|
1681
1557
|
messageStubParameters: [],
|
|
1682
|
-
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
1683
|
-
status: WAMessageStatus.PENDING
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1558
|
+
participant: WABinary_1.isJidGroup(jid) || WABinary_1.isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
1559
|
+
status: Types_1.WAMessageStatus.PENDING
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON)
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
const generateWAMessage = async (jid, content, options) => {
|
|
1566
|
+
options.logger = options?.logger?.child({
|
|
1567
|
+
msgId: options.messageId
|
|
1568
|
+
})
|
|
1569
|
+
|
|
1691
1570
|
if (jid) {
|
|
1692
|
-
options.jid = jid
|
|
1571
|
+
options.jid = jid
|
|
1693
1572
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1573
|
+
|
|
1574
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, {
|
|
1575
|
+
newsletter: WABinary_1.isJidNewsletter(jid),
|
|
1576
|
+
...options
|
|
1577
|
+
}), options)
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const getContentType = (content) => {
|
|
1698
1581
|
if (content) {
|
|
1699
|
-
const keys = Object.keys(content)
|
|
1700
|
-
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
|
|
1701
|
-
|
|
1582
|
+
const keys = Object.keys(content)
|
|
1583
|
+
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
|
|
1584
|
+
|
|
1585
|
+
return key
|
|
1702
1586
|
}
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
const normalizeMessageContent = (content) => {
|
|
1705
1590
|
if (!content) {
|
|
1706
|
-
return undefined
|
|
1591
|
+
return undefined
|
|
1707
1592
|
}
|
|
1708
|
-
|
|
1593
|
+
|
|
1709
1594
|
for (let i = 0; i < 5; i++) {
|
|
1710
|
-
const inner = getFutureProofMessage(content)
|
|
1595
|
+
const inner = getFutureProofMessage(content)
|
|
1711
1596
|
if (!inner) {
|
|
1712
|
-
break
|
|
1597
|
+
break
|
|
1713
1598
|
}
|
|
1714
|
-
|
|
1599
|
+
|
|
1600
|
+
content = inner.message
|
|
1715
1601
|
}
|
|
1716
|
-
|
|
1602
|
+
|
|
1603
|
+
return content
|
|
1604
|
+
|
|
1717
1605
|
function getFutureProofMessage(message) {
|
|
1718
|
-
return (message
|
|
1719
|
-
message?.botForwardedMessage ||
|
|
1720
|
-
message?.botInvokeMessage ||
|
|
1721
|
-
message?.botTaskMessage ||
|
|
1722
|
-
message?.documentWithCaptionMessage ||
|
|
1723
|
-
message?.editedMessage ||
|
|
1724
|
-
message?.ephemeralMessage ||
|
|
1725
|
-
message?.eventCoverImage ||
|
|
1726
|
-
message?.groupMentionedMessage ||
|
|
1727
|
-
message?.groupStatusMentionMessage ||
|
|
1728
|
-
message?.groupStatusMessage ||
|
|
1729
|
-
message?.groupStatusMessageV2 ||
|
|
1730
|
-
message?.limitSharingMessage ||
|
|
1731
|
-
message?.lottieStickerMessage ||
|
|
1732
|
-
message?.newsletterAdminProfileMessage ||
|
|
1733
|
-
message?.newsletterAdminProfileMessageV2 ||
|
|
1734
|
-
message?.newsletterAdminProfileStatusMessage ||
|
|
1735
|
-
message?.pollCreationMessageV4 ||
|
|
1736
|
-
message?.pollCreationOptionImageMessage ||
|
|
1737
|
-
message?.questionMessage ||
|
|
1738
|
-
message?.questionReplyMessage ||
|
|
1739
|
-
message?.spoilerMessage ||
|
|
1740
|
-
message?.statusAddYours ||
|
|
1741
|
-
message?.statusMentionMessage ||
|
|
1742
|
-
message?.viewOnceMessage ||
|
|
1743
|
-
message?.viewOnceMessageV2 ||
|
|
1744
|
-
message?.viewOnceMessageV2Extension);
|
|
1606
|
+
return ((message === null || message === void 0 ? void 0 : message.editedMessage) || (message === null || message === void 0 ? void 0 : message.statusAddYours) || (message === null || message === void 0 ? void 0 : message.botTaskMessage) || (message === null || message === void 0 ? void 0 : message.eventCoverImage) || (message === null || message === void 0 ? void 0 : message.questionMessage) || (message === null || message === void 0 ? void 0 : message.questionReplyMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessage) || (message === null || message === void 0 ? void 0 : message.botInvokeMessage) || (message === null || message === void 0 ? void 0 : message.ephemeralMessage) || (message === null || message === void 0 ? void 0 : message.lottieStickerMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessage) || (message === null || message === void 0 ? void 0 : message.limitSharingMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2) || (message === null || message === void 0 ? void 0 : message.botForwardedMessage) || (message === null || message === void 0 ? void 0 : message.statusMentionMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessageV2) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV4) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV5) || (message === null || message === void 0 ? void 0 : message.associatedChildMessage) || (message === null || message === void 0 ? void 0 : message.groupMentionedMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMentionMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension) || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage) || (message === null || message === void 0 ? void 0 : message.pollCreationOptionImageMessage) || (message === null || message === void 0 ? void 0 : message.spoilerMessage) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileMessage) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileMessageV2) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileStatusMessage))
|
|
1745
1607
|
}
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
else if (msg.documentMessage) {
|
|
1757
|
-
return {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1762
|
-
else if (msg.locationMessage) {
|
|
1763
|
-
return { locationMessage: msg.locationMessage };
|
|
1764
|
-
}
|
|
1765
|
-
else {
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
const extractMessageContent = (content) => {
|
|
1611
|
+
const extractFromButtonsMessage = (msg) => {
|
|
1612
|
+
const header = typeof msg.header === 'object' && msg.header !== null
|
|
1613
|
+
|
|
1614
|
+
if (header ? msg.header?.imageMessage : msg.imageMessage) {
|
|
1615
|
+
return {
|
|
1616
|
+
imageMessage: header ? msg.header.imageMessage : msg.imageMessage
|
|
1617
|
+
}
|
|
1618
|
+
} else if (header ? msg.header?.documentMessage : msg.documentMessage) {
|
|
1619
|
+
return {
|
|
1620
|
+
documentMessage: header ? msg.header.documentMessage : msg.documentMessage
|
|
1621
|
+
}
|
|
1622
|
+
} else if (header ? msg.header?.videoMessage : msg.videoMessage) {
|
|
1766
1623
|
return {
|
|
1767
|
-
|
|
1768
|
-
}
|
|
1624
|
+
videoMessage: header ? msg.header.videoMessage : msg.videoMessage
|
|
1625
|
+
}
|
|
1626
|
+
} else if (header ? msg.header?.locationMessage : msg.locationMessage) {
|
|
1627
|
+
return {
|
|
1628
|
+
locationMessage: header ? msg.header.locationMessage : msg.locationMessage
|
|
1629
|
+
}
|
|
1630
|
+
} else if (header ? msg.header?.productMessage : msg.productMessage) {
|
|
1631
|
+
return {
|
|
1632
|
+
productMessage: header ? msg.header.productMessage : msg.productMessage
|
|
1633
|
+
}
|
|
1634
|
+
} else {
|
|
1635
|
+
return {
|
|
1636
|
+
conversation: 'contentText' in msg ? msg.contentText : ('hydratedContentText' in msg ? msg.hydratedContentText : 'body' in msg ? msg.body.text : '')
|
|
1637
|
+
}
|
|
1769
1638
|
}
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
content = normalizeMessageContent(content)
|
|
1772
1642
|
if (content?.buttonsMessage) {
|
|
1773
|
-
return
|
|
1643
|
+
return extractFromButtonsMessage(content.buttonsMessage)
|
|
1644
|
+
}
|
|
1645
|
+
if (content?.interactiveMessage) {
|
|
1646
|
+
return extractFromButtonsMessage(content.interactiveMessage)
|
|
1647
|
+
}
|
|
1648
|
+
if (content?.templateMessage?.interactiveMessageTemplate) {
|
|
1649
|
+
return extractFromButtonsMessage(content?.templateMessage?.interactiveMessageTemplate)
|
|
1774
1650
|
}
|
|
1775
1651
|
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
1776
|
-
return
|
|
1652
|
+
return extractFromButtonsMessage(content?.templateMessage?.hydratedFourRowTemplate)
|
|
1777
1653
|
}
|
|
1778
1654
|
if (content?.templateMessage?.hydratedTemplate) {
|
|
1779
|
-
return
|
|
1655
|
+
return extractFromButtonsMessage(content?.templateMessage?.hydratedTemplate)
|
|
1780
1656
|
}
|
|
1781
1657
|
if (content?.templateMessage?.fourRowTemplate) {
|
|
1782
|
-
return
|
|
1658
|
+
return extractFromButtonsMessage(content?.templateMessage?.fourRowTemplate)
|
|
1783
1659
|
}
|
|
1784
|
-
return content
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
? 'web'
|
|
1793
|
-
: /^(.{21}|.{32})$/.test(id)
|
|
1794
|
-
? 'android'
|
|
1795
|
-
: /^(3F|.{18}$)/.test(id)
|
|
1796
|
-
? 'desktop'
|
|
1797
|
-
: 'unknown';
|
|
1798
|
-
/** Upserts a receipt in the message */
|
|
1799
|
-
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
1800
|
-
msg.userReceipt = msg.userReceipt || [];
|
|
1801
|
-
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
1660
|
+
return content
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' : /^3E.{20}$/.test(id) ? 'web' : /^(.{21}|.{32})$/.test(id) ? 'android' : /^(3F|.{18}$)/.test(id) ? 'desktop' : 'bots'
|
|
1664
|
+
|
|
1665
|
+
const updateMessageWithReceipt = (msg, receipt) => {
|
|
1666
|
+
msg.userReceipt = msg.userReceipt || []
|
|
1667
|
+
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
|
|
1802
1668
|
if (recp) {
|
|
1803
|
-
Object.assign(recp, receipt)
|
|
1669
|
+
Object.assign(recp, receipt)
|
|
1670
|
+
} else {
|
|
1671
|
+
msg.userReceipt.push(receipt)
|
|
1804
1672
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
1820
|
-
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
const updateMessageWithReaction = (msg, reaction) => {
|
|
1676
|
+
const authorID = generics_1.getKeyAuthor(reaction.key)
|
|
1677
|
+
const reactions = (msg.reactions || []).filter(r => generics_1.getKeyAuthor(r.key) !== authorID)
|
|
1678
|
+
reaction.text = reaction.text || ''
|
|
1679
|
+
reactions.push(reaction)
|
|
1680
|
+
msg.reactions = reactions
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
const updateMessageWithPollUpdate = (msg, update) => {
|
|
1684
|
+
const authorID = generics_1.getKeyAuthor(update.pollUpdateMessageKey)
|
|
1685
|
+
const votes = (msg.pollUpdates || []).filter(r => generics_1.getKeyAuthor(r.pollUpdateMessageKey) !== authorID)
|
|
1686
|
+
|
|
1821
1687
|
if (update.vote?.selectedOptions?.length) {
|
|
1822
|
-
|
|
1688
|
+
votes.push(update)
|
|
1823
1689
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
* @param meId your jid
|
|
1837
|
-
* @returns A list of options & their voters
|
|
1838
|
-
*/
|
|
1839
|
-
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
1840
|
-
const opts = message?.pollCreationMessage?.options ||
|
|
1841
|
-
message?.pollCreationMessageV2?.options ||
|
|
1842
|
-
message?.pollCreationMessageV3?.options ||
|
|
1843
|
-
[];
|
|
1690
|
+
|
|
1691
|
+
msg.pollUpdates = votes
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
function getAggregateVotesInPollMessage({
|
|
1695
|
+
message,
|
|
1696
|
+
pollUpdates
|
|
1697
|
+
}, meId) {
|
|
1698
|
+
message = normalizeMessageContent(message)
|
|
1699
|
+
|
|
1700
|
+
const opts = message?.pollCreationMessage?.options || message?.pollCreationMessageV2?.options || message?.pollCreationMessageV3?.options || []
|
|
1701
|
+
|
|
1844
1702
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
1845
|
-
const hash = sha256(Buffer.from(opt.optionName || '')).toString()
|
|
1703
|
+
const hash = crypto_2.sha256(Buffer.from(opt.optionName || '')).toString()
|
|
1846
1704
|
acc[hash] = {
|
|
1847
1705
|
name: opt.optionName || '',
|
|
1848
1706
|
voters: []
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
return acc
|
|
1710
|
+
}, {})
|
|
1711
|
+
|
|
1852
1712
|
for (const update of pollUpdates || []) {
|
|
1853
|
-
const { vote } = update
|
|
1713
|
+
const { vote } = update
|
|
1854
1714
|
if (!vote) {
|
|
1855
|
-
continue
|
|
1715
|
+
continue
|
|
1856
1716
|
}
|
|
1717
|
+
|
|
1857
1718
|
for (const option of vote.selectedOptions || []) {
|
|
1858
|
-
const hash = option.toString()
|
|
1859
|
-
let data = voteHashMap[hash]
|
|
1719
|
+
const hash = option.toString()
|
|
1720
|
+
let data = voteHashMap[hash]
|
|
1721
|
+
|
|
1860
1722
|
if (!data) {
|
|
1861
1723
|
voteHashMap[hash] = {
|
|
1862
1724
|
name: 'Unknown',
|
|
1863
1725
|
voters: []
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
data = voteHashMap[hash]
|
|
1866
1729
|
}
|
|
1867
|
-
|
|
1730
|
+
|
|
1731
|
+
voteHashMap[hash].voters.push(generics_1.getKeyAuthor(update.pollUpdateMessageKey, meId))
|
|
1868
1732
|
}
|
|
1869
1733
|
}
|
|
1870
|
-
|
|
1734
|
+
|
|
1735
|
+
return Object.values(voteHashMap)
|
|
1871
1736
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
* @returns A list of response types & their responders
|
|
1877
|
-
*/
|
|
1878
|
-
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
1879
|
-
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
1880
|
-
const responseMap = {};
|
|
1881
|
-
for (const type of responseTypes) {
|
|
1882
|
-
responseMap[type] = {
|
|
1883
|
-
response: type,
|
|
1884
|
-
responders: []
|
|
1885
|
-
};
|
|
1886
|
-
}
|
|
1887
|
-
for (const update of eventResponses || []) {
|
|
1888
|
-
const responseType = update.eventResponse || 'UNKNOWN';
|
|
1889
|
-
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
1890
|
-
responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
return Object.values(responseMap);
|
|
1894
|
-
}
|
|
1895
|
-
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
1896
|
-
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
1897
|
-
const keyMap = {};
|
|
1737
|
+
|
|
1738
|
+
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
1739
|
+
const keyMap = {}
|
|
1740
|
+
|
|
1898
1741
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
1899
1742
|
if (!fromMe) {
|
|
1900
|
-
const uqKey = `${remoteJid}:${participant || ''}
|
|
1743
|
+
const uqKey = `${remoteJid}:${participant || ''}`
|
|
1744
|
+
|
|
1901
1745
|
if (!keyMap[uqKey]) {
|
|
1902
1746
|
keyMap[uqKey] = {
|
|
1903
1747
|
jid: remoteJid,
|
|
1904
1748
|
participant: participant,
|
|
1905
1749
|
messageIds: []
|
|
1906
|
-
}
|
|
1750
|
+
}
|
|
1907
1751
|
}
|
|
1908
|
-
|
|
1752
|
+
|
|
1753
|
+
keyMap[uqKey].messageIds.push(id)
|
|
1909
1754
|
}
|
|
1910
1755
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1756
|
+
|
|
1757
|
+
return Object.values(keyMap)
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
const REUPLOAD_REQUIRED_STATUS = [410, 404]
|
|
1761
|
+
|
|
1762
|
+
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
1918
1763
|
const result = await downloadMsg().catch(async (error) => {
|
|
1919
|
-
if (ctx &&
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
message = await ctx.reuploadRequest(message)
|
|
1925
|
-
const result = await downloadMsg()
|
|
1926
|
-
return result
|
|
1927
|
-
}
|
|
1928
|
-
throw error
|
|
1929
|
-
})
|
|
1930
|
-
|
|
1764
|
+
if (ctx && typeof error?.status === 'number' && REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
1765
|
+
ctx.logger.info({
|
|
1766
|
+
key: message.key
|
|
1767
|
+
}, 'sending reupload media request...')
|
|
1768
|
+
|
|
1769
|
+
message = await ctx.reuploadRequest(message)
|
|
1770
|
+
const result = await downloadMsg()
|
|
1771
|
+
return result
|
|
1772
|
+
}
|
|
1773
|
+
throw error
|
|
1774
|
+
})
|
|
1775
|
+
|
|
1776
|
+
return result
|
|
1777
|
+
|
|
1931
1778
|
async function downloadMsg() {
|
|
1932
|
-
const mContent = extractMessageContent(message.message)
|
|
1779
|
+
const mContent = extractMessageContent(message.message)
|
|
1933
1780
|
if (!mContent) {
|
|
1934
|
-
throw new Boom('No message present', {
|
|
1781
|
+
throw new boom_1.Boom('No message present', {
|
|
1782
|
+
statusCode: 400,
|
|
1783
|
+
data: message
|
|
1784
|
+
})
|
|
1935
1785
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1786
|
+
|
|
1787
|
+
const contentType = getContentType(mContent)
|
|
1788
|
+
let mediaType = contentType?.replace('Message', '')
|
|
1789
|
+
const media = contentType === 'productMessage' ? mContent[contentType]?.product?.productImage : mContent[contentType]
|
|
1790
|
+
|
|
1939
1791
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
1940
|
-
throw new Boom(`"${contentType}" message is not a media message`)
|
|
1792
|
+
throw new boom_1.Boom(`"${contentType}" message is not a media message`)
|
|
1941
1793
|
}
|
|
1942
|
-
|
|
1794
|
+
|
|
1795
|
+
let download
|
|
1796
|
+
|
|
1943
1797
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
1944
1798
|
download = {
|
|
1945
1799
|
directPath: media.thumbnailDirectPath,
|
|
1946
1800
|
mediaKey: media.mediaKey
|
|
1947
|
-
}
|
|
1948
|
-
mediaType = 'thumbnail-link'
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
download = media;
|
|
1801
|
+
}
|
|
1802
|
+
mediaType = 'thumbnail-link'
|
|
1803
|
+
} else {
|
|
1804
|
+
download = media
|
|
1952
1805
|
}
|
|
1953
|
-
|
|
1806
|
+
|
|
1807
|
+
const stream = await messages_media_1.downloadContentFromMessage(download, mediaType, options)
|
|
1808
|
+
|
|
1954
1809
|
if (type === 'buffer') {
|
|
1955
|
-
const bufferArray = []
|
|
1810
|
+
const bufferArray = []
|
|
1811
|
+
|
|
1956
1812
|
for await (const chunk of stream) {
|
|
1957
|
-
bufferArray.push(chunk)
|
|
1813
|
+
bufferArray.push(chunk)
|
|
1958
1814
|
}
|
|
1959
|
-
|
|
1815
|
+
|
|
1816
|
+
return Buffer.concat(bufferArray)
|
|
1960
1817
|
}
|
|
1961
|
-
|
|
1818
|
+
|
|
1819
|
+
return stream
|
|
1962
1820
|
}
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
content = extractMessageContent(content)
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
content?.videoMessage ||
|
|
1970
|
-
content?.audioMessage ||
|
|
1971
|
-
content?.stickerMessage;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
const assertMediaContent = (content) => {
|
|
1824
|
+
content = extractMessageContent(content)
|
|
1825
|
+
|
|
1826
|
+
const mediaContent = content?.documentMessage || content?.imageMessage || content?.videoMessage || content?.audioMessage || content?.stickerMessage
|
|
1972
1827
|
if (!mediaContent) {
|
|
1973
|
-
throw new Boom('given message is not a media message', {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
}
|
|
1977
|
-
/**
|
|
1978
|
-
* Checks if a WebP buffer is animated by looking for VP8X chunk with animation flag
|
|
1979
|
-
* or ANIM/ANMF chunks
|
|
1980
|
-
*/
|
|
1981
|
-
const isAnimatedWebP = (buffer) => {
|
|
1982
|
-
// WebP must start with RIFF....WEBP
|
|
1983
|
-
if (buffer.length < 12 ||
|
|
1984
|
-
buffer[0] !== 0x52 ||
|
|
1985
|
-
buffer[1] !== 0x49 ||
|
|
1986
|
-
buffer[2] !== 0x46 ||
|
|
1987
|
-
buffer[3] !== 0x46 ||
|
|
1988
|
-
buffer[8] !== 0x57 ||
|
|
1989
|
-
buffer[9] !== 0x45 ||
|
|
1990
|
-
buffer[10] !== 0x42 ||
|
|
1991
|
-
buffer[11] !== 0x50) {
|
|
1992
|
-
return false;
|
|
1828
|
+
throw new boom_1.Boom('given message is not a media message', {
|
|
1829
|
+
statusCode: 400,
|
|
1830
|
+
data: content
|
|
1831
|
+
})
|
|
1993
1832
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
1833
|
+
return mediaContent
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
const patchMessageForMdIfRequired = (message) => {
|
|
1837
|
+
if (message?.buttonsMessage || message?.templateMessage || message?.listMessage || message?.interactiveMessage?.nativeFlowMesaage) {
|
|
1838
|
+
message = {
|
|
1839
|
+
viewOnceMessageV2Extension: {
|
|
1840
|
+
message: {
|
|
1841
|
+
messageContextInfo: {
|
|
1842
|
+
deviceListMetadataVersion: 2,
|
|
1843
|
+
deviceListMetadata: {}
|
|
1844
|
+
},
|
|
1845
|
+
...message
|
|
2007
1846
|
}
|
|
2008
|
-
;
|
|
2009
1847
|
}
|
|
2010
|
-
;
|
|
2011
1848
|
}
|
|
2012
|
-
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
2013
|
-
// ANIM or ANMF chunks indicate animation
|
|
2014
|
-
return true;
|
|
2015
|
-
}
|
|
2016
|
-
;
|
|
2017
|
-
// Move to next chunk (chunk size + 8 bytes header, padded to even)
|
|
2018
|
-
offset += 8 + chunkSize + (chunkSize % 2);
|
|
2019
1849
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
};
|
|
2047
|
-
//# sourceMappingURL=messages.js.map
|
|
1850
|
+
return message
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
module.exports = {
|
|
1854
|
+
extractUrlFromText,
|
|
1855
|
+
generateLinkPreviewIfRequired,
|
|
1856
|
+
prepareWAMessageMedia,
|
|
1857
|
+
prepareAlbumMessageContent,
|
|
1858
|
+
prepareDisappearingMessageSettingContent,
|
|
1859
|
+
generateForwardMessageContent,
|
|
1860
|
+
generateWAMessageContent,
|
|
1861
|
+
generateWAMessageFromContent,
|
|
1862
|
+
generateWAMessage,
|
|
1863
|
+
getContentType,
|
|
1864
|
+
normalizeMessageContent,
|
|
1865
|
+
extractMessageContent,
|
|
1866
|
+
getDevice,
|
|
1867
|
+
updateMessageWithReceipt,
|
|
1868
|
+
updateMessageWithReaction,
|
|
1869
|
+
updateMessageWithPollUpdate,
|
|
1870
|
+
getAggregateVotesInPollMessage,
|
|
1871
|
+
aggregateMessageKeysNotFromMe,
|
|
1872
|
+
downloadMediaMessage,
|
|
1873
|
+
assertMediaContent,
|
|
1874
|
+
patchMessageForMdIfRequired
|
|
1875
|
+
}
|