@alannxd/baileys 6.0.6 → 6.0.9
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 +341 -286
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +205861 -60565
- package/engine-requirements.js +1 -1
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.js +627 -427
- package/lib/Socket/communities.js +90 -80
- package/lib/Socket/groups.js +154 -161
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +315 -469
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -799
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +152 -204
- package/lib/Socket/socket.js +544 -313
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.js +236 -84
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.js +189 -209
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +23 -33
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.js +795 -369
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.js +120 -72
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.js +55 -49
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.js +76 -40
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.js +13 -36
- package/package.json +85 -51
- package/WAProto/fix-import.js +0 -29
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -14
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -10
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
- package/lib/Signal/Group/sender-key-message.d.ts +0 -18
- package/lib/Signal/Group/sender-key-name.d.ts +0 -17
- package/lib/Signal/Group/sender-key-record.d.ts +0 -30
- package/lib/Signal/Group/sender-key-state.d.ts +0 -38
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -3
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/communities.d.ts +0 -180
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/luxu.d.ts +0 -266
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -183
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/socket.d.ts +0 -44
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- package/lib/Store/index.d.ts +0 -3
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-in-memory-store.d.ts +0 -118
- package/lib/Store/make-ordered-dictionary.d.ts +0 -13
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -110
- package/lib/Types/Call.d.ts +0 -13
- package/lib/Types/Chat.d.ts +0 -102
- package/lib/Types/Contact.d.ts +0 -19
- package/lib/Types/Events.d.ts +0 -157
- package/lib/Types/GroupMetadata.d.ts +0 -55
- package/lib/Types/Label.d.ts +0 -35
- package/lib/Types/LabelAssociation.d.ts +0 -29
- package/lib/Types/Message.d.ts +0 -273
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Newsletter.js +0 -38
- package/lib/Types/Product.d.ts +0 -78
- package/lib/Types/Signal.d.ts +0 -57
- package/lib/Types/Socket.d.ts +0 -111
- package/lib/Types/State.d.ts +0 -27
- package/lib/Types/USync.d.ts +0 -25
- package/lib/Types/index.d.ts +0 -57
- package/lib/Utils/auth-utils.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/baileys-event-stream.js +0 -63
- package/lib/Utils/business.d.ts +0 -22
- package/lib/Utils/chat-utils.d.ts +0 -71
- package/lib/Utils/crypto.d.ts +0 -41
- package/lib/Utils/decode-wa-message.d.ts +0 -19
- package/lib/Utils/event-buffer.d.ts +0 -35
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -15
- package/lib/Utils/index.d.ts +0 -17
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -4
- package/lib/Utils/lt-hash.d.ts +0 -12
- package/lib/Utils/make-mutex.d.ts +0 -7
- package/lib/Utils/messages-media.d.ts +0 -116
- package/lib/Utils/messages.d.ts +0 -77
- package/lib/Utils/noise-handler.d.ts +0 -21
- package/lib/Utils/process-message.d.ts +0 -41
- package/lib/Utils/signal.d.ts +0 -32
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -17
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -31
- package/lib/WABinary/types.d.ts +0 -18
- package/lib/WAM/BinaryInfo.d.ts +0 -17
- package/lib/WAM/constants.d.ts +0 -38
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
- package/lib/WAUSync/Protocols/index.d.ts +0 -4
- package/lib/WAUSync/USyncQuery.d.ts +0 -28
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/index.d.ts +0 -12
package/lib/Utils/messages.js
CHANGED
|
@@ -1,56 +1,177 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const Types_1 = require("../Types");
|
|
14
|
-
const WABinary_1 = require("../WABinary");
|
|
15
|
-
const crypto_2 = require("./crypto");
|
|
16
|
-
const generics_1 = require("./generics");
|
|
17
|
-
const messages_media_1 = require("./messages-media");
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { randomUUID, randomBytes } from 'crypto';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import {} from 'stream';
|
|
5
|
+
import { proto } from '../../WAProto/index.js';
|
|
6
|
+
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
7
|
+
import { WAMessageStatus, WAProto } from '../Types/index.js';
|
|
8
|
+
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
|
|
9
|
+
import { sha256 } from './crypto.js';
|
|
10
|
+
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
11
|
+
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
|
|
12
|
+
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
18
13
|
const MIMETYPE_MAP = {
|
|
19
14
|
image: 'image/jpeg',
|
|
20
15
|
video: 'video/mp4',
|
|
21
16
|
document: 'application/pdf',
|
|
22
17
|
audio: 'audio/ogg; codecs=opus',
|
|
23
18
|
sticker: 'image/webp',
|
|
24
|
-
'product-catalog-image': 'image/jpeg'
|
|
19
|
+
'product-catalog-image': 'image/jpeg'
|
|
25
20
|
};
|
|
26
21
|
const MessageTypeProto = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
image: WAProto.Message.ImageMessage,
|
|
23
|
+
video: WAProto.Message.VideoMessage,
|
|
24
|
+
audio: WAProto.Message.AudioMessage,
|
|
25
|
+
sticker: WAProto.Message.StickerMessage,
|
|
26
|
+
document: WAProto.Message.DocumentMessage
|
|
32
27
|
};
|
|
33
|
-
const ButtonType =
|
|
28
|
+
const ButtonType = proto.Message.ButtonsMessage.HeaderType;
|
|
29
|
+
const RICH_RESPONSE_CODE_KEYWORDS = new Set([
|
|
30
|
+
'break',
|
|
31
|
+
'case',
|
|
32
|
+
'catch',
|
|
33
|
+
'continue',
|
|
34
|
+
'debugger',
|
|
35
|
+
'default',
|
|
36
|
+
'delete',
|
|
37
|
+
'do',
|
|
38
|
+
'else',
|
|
39
|
+
'finally',
|
|
40
|
+
'for',
|
|
41
|
+
'function',
|
|
42
|
+
'if',
|
|
43
|
+
'in',
|
|
44
|
+
'instanceof',
|
|
45
|
+
'new',
|
|
46
|
+
'return',
|
|
47
|
+
'switch',
|
|
48
|
+
'this',
|
|
49
|
+
'throw',
|
|
50
|
+
'try',
|
|
51
|
+
'typeof',
|
|
52
|
+
'var',
|
|
53
|
+
'void',
|
|
54
|
+
'while',
|
|
55
|
+
'with',
|
|
56
|
+
'true',
|
|
57
|
+
'false',
|
|
58
|
+
'null',
|
|
59
|
+
'undefined',
|
|
60
|
+
'NaN',
|
|
61
|
+
'Infinity',
|
|
62
|
+
'class',
|
|
63
|
+
'const',
|
|
64
|
+
'let',
|
|
65
|
+
'super',
|
|
66
|
+
'extends',
|
|
67
|
+
'export',
|
|
68
|
+
'import',
|
|
69
|
+
'yield',
|
|
70
|
+
'static',
|
|
71
|
+
'constructor',
|
|
72
|
+
'of',
|
|
73
|
+
'async',
|
|
74
|
+
'await',
|
|
75
|
+
'get',
|
|
76
|
+
'set',
|
|
77
|
+
'implements',
|
|
78
|
+
'interface',
|
|
79
|
+
'package',
|
|
80
|
+
'private',
|
|
81
|
+
'protected',
|
|
82
|
+
'public',
|
|
83
|
+
'enum',
|
|
84
|
+
'throws',
|
|
85
|
+
'transient'
|
|
86
|
+
])
|
|
87
|
+
const tokenizeCode = code => {
|
|
88
|
+
const tokens = []
|
|
89
|
+
let i = 0
|
|
90
|
+
const len = code.length
|
|
91
|
+
while (i < len) {
|
|
92
|
+
if (/\s/.test(code[i])) {
|
|
93
|
+
const start = i
|
|
94
|
+
while (i < len && /\s/.test(code[i])) i++
|
|
95
|
+
tokens.push({ content: code.slice(start, i), type: 'DEFAULT' })
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
if (code[i] === '"' || code[i] === "'" || code[i] === '`') {
|
|
99
|
+
const start = i
|
|
100
|
+
const quote = code[i]
|
|
101
|
+
i++
|
|
102
|
+
while (i < len && code[i] !== quote) {
|
|
103
|
+
if (code[i] === '\\') i++
|
|
104
|
+
i++
|
|
105
|
+
}
|
|
106
|
+
i++
|
|
107
|
+
tokens.push({ content: code.slice(start, i), type: 'STR' })
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
if (code[i] === '/' && i + 1 < len && code[i + 1] === '/') {
|
|
111
|
+
const start = i
|
|
112
|
+
while (i < len && code[i] !== '\n') i++
|
|
113
|
+
tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if (code[i] === '/' && i + 1 < len && code[i + 1] === '*') {
|
|
117
|
+
const start = i
|
|
118
|
+
i += 2
|
|
119
|
+
while (i + 1 < len && !(code[i] === '*' && code[i + 1] === '/')) i++
|
|
120
|
+
i += 2
|
|
121
|
+
tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
if (/[0-9]/.test(code[i])) {
|
|
125
|
+
const start = i
|
|
126
|
+
while (i < len && /[0-9.]/.test(code[i])) i++
|
|
127
|
+
tokens.push({ content: code.slice(start, i), type: 'NUMBER' })
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
if (/[a-zA-Z_$]/.test(code[i])) {
|
|
131
|
+
const start = i
|
|
132
|
+
while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) i++
|
|
133
|
+
const word = code.slice(start, i)
|
|
134
|
+
if (RICH_RESPONSE_CODE_KEYWORDS.has(word)) {
|
|
135
|
+
tokens.push({ content: word, type: 'KEYWORD' })
|
|
136
|
+
} else {
|
|
137
|
+
let j = i
|
|
138
|
+
while (j < len && /\s/.test(code[j])) j++
|
|
139
|
+
tokens.push({ content: word, type: j < len && code[j] === '(' ? 'METHOD' : 'DEFAULT' })
|
|
140
|
+
}
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
tokens.push({ content: code[i], type: 'DEFAULT' })
|
|
144
|
+
i++
|
|
145
|
+
}
|
|
146
|
+
const merged = []
|
|
147
|
+
for (const t of tokens) {
|
|
148
|
+
if (merged.length && merged[merged.length - 1].type === 'DEFAULT' && t.type === 'DEFAULT') {
|
|
149
|
+
merged[merged.length - 1].content += t.content
|
|
150
|
+
} else {
|
|
151
|
+
merged.push(t)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return merged
|
|
155
|
+
}
|
|
34
156
|
/**
|
|
35
157
|
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
36
158
|
* @param text eg. hello https://google.com
|
|
37
159
|
* @returns the URL, eg. https://google.com
|
|
38
160
|
*/
|
|
39
|
-
const extractUrlFromText = (text) =>
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const url = (0, exports.extractUrlFromText)(text);
|
|
161
|
+
export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
|
|
162
|
+
export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
163
|
+
const url = extractUrlFromText(text);
|
|
43
164
|
if (!!getUrlInfo && url) {
|
|
44
165
|
try {
|
|
45
166
|
const urlInfo = await getUrlInfo(url);
|
|
46
167
|
return urlInfo;
|
|
47
168
|
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
|
|
169
|
+
catch (error) {
|
|
170
|
+
// ignore if fails
|
|
171
|
+
logger?.warn({ trace: error.stack }, 'url generation failed');
|
|
50
172
|
}
|
|
51
173
|
}
|
|
52
174
|
};
|
|
53
|
-
exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
|
|
54
175
|
const assertColor = async (color) => {
|
|
55
176
|
let assertedColor;
|
|
56
177
|
if (typeof color === 'number') {
|
|
@@ -65,159 +186,150 @@ const assertColor = async (color) => {
|
|
|
65
186
|
return assertedColor;
|
|
66
187
|
}
|
|
67
188
|
};
|
|
68
|
-
const prepareWAMessageMedia = async (message, options) => {
|
|
189
|
+
export const prepareWAMessageMedia = async (message, options) => {
|
|
69
190
|
const logger = options.logger;
|
|
70
191
|
let mediaType;
|
|
71
|
-
for (const key of
|
|
192
|
+
for (const key of MEDIA_KEYS) {
|
|
72
193
|
if (key in message) {
|
|
73
194
|
mediaType = key;
|
|
74
195
|
}
|
|
75
196
|
}
|
|
76
197
|
if (!mediaType) {
|
|
77
|
-
throw new
|
|
78
|
-
statusCode: 400
|
|
79
|
-
});
|
|
198
|
+
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
80
199
|
}
|
|
81
|
-
|
|
82
200
|
const uploadData = {
|
|
83
201
|
...message,
|
|
84
|
-
...(message.annotations ? {
|
|
85
|
-
annotations: message.annotations
|
|
86
|
-
} : {
|
|
87
|
-
annotations: [
|
|
88
|
-
{
|
|
89
|
-
polygonVertices: [
|
|
90
|
-
{
|
|
91
|
-
x: 60.71664810180664,
|
|
92
|
-
y: -36.39784622192383
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
x: -16.710189819335938,
|
|
96
|
-
y: 49.263675689697266
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
x: -56.585853576660156,
|
|
100
|
-
y: 37.85963439941406
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
x: 20.840980529785156,
|
|
104
|
-
y: -47.80188751220703
|
|
105
|
-
}
|
|
106
|
-
],
|
|
107
|
-
newsletter: {
|
|
108
|
-
newsletterJid: "120363420757607688@newsletter",
|
|
109
|
-
serverMessageId: 0,
|
|
110
|
-
newsletterName: "7eppeli - Information",
|
|
111
|
-
contentType: "UPDATE",
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
]
|
|
115
|
-
}),
|
|
116
202
|
media: message[mediaType]
|
|
117
203
|
};
|
|
118
204
|
delete uploadData[mediaType];
|
|
205
|
+
// check if cacheable + generate cache key
|
|
119
206
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
120
|
-
|
|
207
|
+
'url' in uploadData.media &&
|
|
121
208
|
!!uploadData.media.url &&
|
|
122
|
-
!!options.mediaCache &&
|
|
123
|
-
|
|
124
|
-
|
|
209
|
+
!!options.mediaCache &&
|
|
210
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
125
211
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
126
212
|
uploadData.fileName = 'file';
|
|
127
213
|
}
|
|
128
|
-
|
|
129
214
|
if (!uploadData.mimetype) {
|
|
130
215
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
131
216
|
}
|
|
132
|
-
|
|
133
217
|
if (cacheableKey) {
|
|
134
|
-
const mediaBuff = options.mediaCache.get(cacheableKey);
|
|
218
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
135
219
|
if (mediaBuff) {
|
|
136
|
-
logger
|
|
137
|
-
const obj =
|
|
220
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
221
|
+
const obj = proto.Message.decode(mediaBuff);
|
|
138
222
|
const key = `${mediaType}Message`;
|
|
139
223
|
Object.assign(obj[key], { ...uploadData, media: undefined });
|
|
140
224
|
return obj;
|
|
141
225
|
}
|
|
142
226
|
}
|
|
143
|
-
|
|
227
|
+
const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
|
|
228
|
+
if (isNewsletter) {
|
|
229
|
+
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
230
|
+
const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
|
|
231
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
232
|
+
const { mediaUrl, directPath } = await options.upload(filePath, {
|
|
233
|
+
fileEncSha256B64: fileSha256B64,
|
|
234
|
+
mediaType: mediaType,
|
|
235
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
236
|
+
});
|
|
237
|
+
await fs.unlink(filePath);
|
|
238
|
+
const obj = WAProto.Message.fromObject({
|
|
239
|
+
// todo: add more support here
|
|
240
|
+
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
241
|
+
url: mediaUrl,
|
|
242
|
+
directPath,
|
|
243
|
+
fileSha256,
|
|
244
|
+
fileLength,
|
|
245
|
+
...uploadData,
|
|
246
|
+
media: undefined
|
|
247
|
+
})
|
|
248
|
+
});
|
|
249
|
+
if (uploadData.ptv) {
|
|
250
|
+
obj.ptvMessage = obj.videoMessage;
|
|
251
|
+
delete obj.videoMessage;
|
|
252
|
+
}
|
|
253
|
+
if (obj.stickerMessage) {
|
|
254
|
+
obj.stickerMessage.stickerSentTs = Date.now();
|
|
255
|
+
}
|
|
256
|
+
if (cacheableKey) {
|
|
257
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
258
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
259
|
+
}
|
|
260
|
+
return obj;
|
|
261
|
+
}
|
|
144
262
|
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
145
|
-
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
|
146
|
-
|
|
147
|
-
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
|
|
263
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
264
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
148
265
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
149
266
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
150
|
-
|
|
151
|
-
const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, opusConverted } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
267
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
152
268
|
logger,
|
|
153
269
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
154
|
-
opts: options.options
|
|
155
|
-
isPtt: uploadData.ptt,
|
|
156
|
-
forceOpus: (mediaType === "audio" && uploadData.mimetype && uploadData.mimetype.includes('opus'))
|
|
270
|
+
opts: options.options
|
|
157
271
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
uploadData.mimetype = 'audio/ogg; codecs=opus';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const fileEncSha256B64 = (options.newsletter ? fileSha256 : fileEncSha256 !== null && fileEncSha256 !== void 0 ? fileEncSha256 : fileSha256).toString('base64');
|
|
164
|
-
|
|
165
|
-
const [{ mediaUrl, directPath, handle }] = await Promise.all([
|
|
272
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
273
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
166
274
|
(async () => {
|
|
167
|
-
const result = await options.upload(
|
|
168
|
-
|
|
275
|
+
const result = await options.upload(encFilePath, {
|
|
276
|
+
fileEncSha256B64,
|
|
277
|
+
mediaType,
|
|
278
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
279
|
+
});
|
|
280
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
169
281
|
return result;
|
|
170
282
|
})(),
|
|
171
283
|
(async () => {
|
|
172
284
|
try {
|
|
173
285
|
if (requiresThumbnailComputation) {
|
|
174
|
-
const { thumbnail, originalImageDimensions } = await
|
|
286
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
|
|
175
287
|
uploadData.jpegThumbnail = thumbnail;
|
|
176
288
|
if (!uploadData.width && originalImageDimensions) {
|
|
177
289
|
uploadData.width = originalImageDimensions.width;
|
|
178
290
|
uploadData.height = originalImageDimensions.height;
|
|
179
|
-
logger
|
|
291
|
+
logger?.debug('set dimensions');
|
|
180
292
|
}
|
|
181
|
-
logger
|
|
293
|
+
logger?.debug('generated thumbnail');
|
|
182
294
|
}
|
|
183
295
|
if (requiresDurationComputation) {
|
|
184
|
-
uploadData.seconds = await
|
|
185
|
-
logger
|
|
296
|
+
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
297
|
+
logger?.debug('computed audio duration');
|
|
186
298
|
}
|
|
187
299
|
if (requiresWaveformProcessing) {
|
|
188
|
-
uploadData.waveform = await
|
|
189
|
-
logger
|
|
300
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
301
|
+
logger?.debug('processed waveform');
|
|
190
302
|
}
|
|
191
303
|
if (requiresAudioBackground) {
|
|
192
304
|
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
193
|
-
logger
|
|
305
|
+
logger?.debug('computed backgroundColor audio status');
|
|
194
306
|
}
|
|
195
307
|
}
|
|
196
308
|
catch (error) {
|
|
197
|
-
logger
|
|
309
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
198
310
|
}
|
|
199
|
-
})()
|
|
200
|
-
])
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
311
|
+
})()
|
|
312
|
+
]).finally(async () => {
|
|
313
|
+
try {
|
|
314
|
+
await fs.unlink(encFilePath);
|
|
315
|
+
if (originalFilePath) {
|
|
316
|
+
await fs.unlink(originalFilePath);
|
|
317
|
+
}
|
|
318
|
+
logger?.debug('removed tmp files');
|
|
204
319
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
await fs_1.promises.unlink(bodyPath);
|
|
208
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
|
|
320
|
+
catch (error) {
|
|
321
|
+
logger?.warn('failed to remove tmp file');
|
|
209
322
|
}
|
|
210
323
|
});
|
|
211
|
-
|
|
212
|
-
const obj = Types_1.WAProto.Message.fromObject({
|
|
324
|
+
const obj = WAProto.Message.fromObject({
|
|
213
325
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
214
|
-
url:
|
|
326
|
+
url: mediaUrl,
|
|
215
327
|
directPath,
|
|
216
|
-
mediaKey
|
|
217
|
-
fileEncSha256
|
|
328
|
+
mediaKey,
|
|
329
|
+
fileEncSha256,
|
|
218
330
|
fileSha256,
|
|
219
331
|
fileLength,
|
|
220
|
-
mediaKeyTimestamp:
|
|
332
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
221
333
|
...uploadData,
|
|
222
334
|
media: undefined
|
|
223
335
|
})
|
|
@@ -226,73 +338,308 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
226
338
|
obj.ptvMessage = obj.videoMessage;
|
|
227
339
|
delete obj.videoMessage;
|
|
228
340
|
}
|
|
229
|
-
|
|
230
341
|
if (cacheableKey) {
|
|
231
|
-
logger
|
|
232
|
-
options.mediaCache.set(cacheableKey,
|
|
342
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
343
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
233
344
|
}
|
|
234
|
-
|
|
235
345
|
return obj;
|
|
236
346
|
};
|
|
237
|
-
|
|
238
|
-
const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
347
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
239
348
|
ephemeralExpiration = ephemeralExpiration || 0;
|
|
240
349
|
const content = {
|
|
241
350
|
ephemeralMessage: {
|
|
242
351
|
message: {
|
|
243
352
|
protocolMessage: {
|
|
244
|
-
type:
|
|
353
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
245
354
|
ephemeralExpiration
|
|
246
355
|
}
|
|
247
356
|
}
|
|
248
357
|
}
|
|
249
358
|
};
|
|
250
|
-
return
|
|
359
|
+
return WAProto.Message.fromObject(content);
|
|
251
360
|
};
|
|
252
|
-
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
|
|
253
361
|
/**
|
|
254
362
|
* Generate forwarded message content like WA does
|
|
255
363
|
* @param message the message to forward
|
|
256
364
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
257
365
|
*/
|
|
258
|
-
const generateForwardMessageContent = (message, forceForward) => {
|
|
259
|
-
var _a;
|
|
366
|
+
export const generateForwardMessageContent = (message, forceForward) => {
|
|
260
367
|
let content = message.message;
|
|
261
368
|
if (!content) {
|
|
262
|
-
throw new
|
|
369
|
+
throw new Boom('no content in message', { statusCode: 400 });
|
|
263
370
|
}
|
|
264
371
|
// hacky copy
|
|
265
|
-
content =
|
|
266
|
-
content =
|
|
372
|
+
content = normalizeMessageContent(content);
|
|
373
|
+
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
267
374
|
let key = Object.keys(content)[0];
|
|
268
|
-
let score =
|
|
375
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
269
376
|
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
270
377
|
if (key === 'conversation') {
|
|
271
378
|
content.extendedTextMessage = { text: content[key] };
|
|
272
379
|
delete content.conversation;
|
|
273
380
|
key = 'extendedTextMessage';
|
|
274
381
|
}
|
|
382
|
+
const key_ = content?.[key];
|
|
275
383
|
if (score > 0) {
|
|
276
|
-
|
|
384
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
277
385
|
}
|
|
278
386
|
else {
|
|
279
|
-
|
|
387
|
+
key_.contextInfo = {};
|
|
280
388
|
}
|
|
281
389
|
return content;
|
|
282
390
|
};
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
391
|
+
export const hasNonNullishProperty = (message, key) => {
|
|
392
|
+
return (typeof message === 'object' &&
|
|
393
|
+
message !== null &&
|
|
394
|
+
key in message &&
|
|
395
|
+
message[key] !== null &&
|
|
396
|
+
message[key] !== undefined);
|
|
397
|
+
};
|
|
398
|
+
function hasOptionalProperty(obj, key) {
|
|
399
|
+
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
400
|
+
}
|
|
401
|
+
const applyContextInfoAndMentions = (interactiveMessage, message) => {
|
|
402
|
+
if ('contextInfo' in message && !!message.contextInfo) {
|
|
403
|
+
interactiveMessage.contextInfo = message.contextInfo
|
|
404
|
+
}
|
|
405
|
+
if ('mentions' in message && !!message.mentions) {
|
|
406
|
+
interactiveMessage.contextInfo = {
|
|
407
|
+
...(interactiveMessage.contextInfo || {}),
|
|
408
|
+
mentionedJid: message.mentions
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
export const generateWAMessageContent = async (message, options) => {
|
|
413
|
+
var _a, _b;
|
|
287
414
|
let m = {};
|
|
288
|
-
if ('
|
|
415
|
+
if ('interactiveButtons' in message && !!message.interactiveButtons) {
|
|
416
|
+
const nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
|
|
417
|
+
buttons: message.interactiveButtons,
|
|
418
|
+
messageParamsJson: message.messageParams ?? "{}"
|
|
419
|
+
});
|
|
420
|
+
let interactiveMessage = { nativeFlowMessage: nativeFlow };
|
|
421
|
+
if ('text' in message) {
|
|
422
|
+
interactiveMessage.body = { text: message.text };
|
|
423
|
+
} else if ('caption' in message) {
|
|
424
|
+
interactiveMessage.body = { text: message.caption };
|
|
425
|
+
}
|
|
426
|
+
if ('title' in message && !!message.title) {
|
|
427
|
+
interactiveMessage.header = {
|
|
428
|
+
title: message.title,
|
|
429
|
+
subtitle: message.subtitle || "",
|
|
430
|
+
hasMediaAttachment: false
|
|
431
|
+
};
|
|
432
|
+
let media = null;
|
|
433
|
+
if ('image' in message && !!message.image) {
|
|
434
|
+
media = await prepareWAMessageMedia({ image: message.image }, options);
|
|
435
|
+
interactiveMessage.header.imageMessage = media.imageMessage;
|
|
436
|
+
if (message.image.caption) {
|
|
437
|
+
interactiveMessage.header.imageMessage.caption = message.image.caption;
|
|
438
|
+
}
|
|
439
|
+
} else if ('video' in message && !!message.video) {
|
|
440
|
+
media = await prepareWAMessageMedia({ video: message.video }, options);
|
|
441
|
+
interactiveMessage.header.videoMessage = media.videoMessage;
|
|
442
|
+
if (message.video.caption) {
|
|
443
|
+
interactiveMessage.header.videoMessage.caption = message.video.caption;
|
|
444
|
+
}
|
|
445
|
+
} else if ('gif' in message && !!message.gif) {
|
|
446
|
+
media = await prepareWAMessageMedia({ video: message.gif }, options);
|
|
447
|
+
interactiveMessage.header.videoMessage = media.videoMessage;
|
|
448
|
+
interactiveMessage.header.videoMessage.gifPlayback = true;
|
|
449
|
+
if (message.gif.caption) {
|
|
450
|
+
interactiveMessage.header.videoMessage.caption = message.gif.caption;
|
|
451
|
+
}
|
|
452
|
+
} else if ('document' in message && !!message.document) {
|
|
453
|
+
media = await prepareWAMessageMedia({ document: message.document }, options);
|
|
454
|
+
interactiveMessage.header.documentMessage = media.documentMessage;
|
|
455
|
+
let docuR = interactiveMessage.header.documentMessage;
|
|
456
|
+
docuR.fileName = message.document.fileName || "Document";
|
|
457
|
+
docuR.mimetype = message.document.mimetype || docuR.mimetype;
|
|
458
|
+
if (message.document.caption) docuR.caption = message.document.caption;
|
|
459
|
+
if (message.document.jpegThumbnail) docuR.jpegThumbnail = message.document.jpegThumbnail;
|
|
460
|
+
} else if ('location' in message && !!message.location) {
|
|
461
|
+
let mLoc = message.location;
|
|
462
|
+
interactiveMessage.header.locationMessage = {
|
|
463
|
+
degreesLongitude: mLoc.longitude || 0,
|
|
464
|
+
degreesLatitude: mLoc.latitude || 0,
|
|
465
|
+
name: mLoc.name || null,
|
|
466
|
+
address: mLoc.address || null,
|
|
467
|
+
url: mLoc.url || null
|
|
468
|
+
};
|
|
469
|
+
media = true;
|
|
470
|
+
} else if ('thumbnail' in message && !!message.thumbnail) {
|
|
471
|
+
interactiveMessage.header.jpegThumbnail = message.thumbnail;
|
|
472
|
+
}
|
|
473
|
+
if (media || ('thumbnail' in message && !!message.thumbnail)) {
|
|
474
|
+
interactiveMessage.header.hasMediaAttachment = true;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if ('footer' in message && !!message.footer) {
|
|
478
|
+
interactiveMessage.footer = { text: message.footer };
|
|
479
|
+
}
|
|
480
|
+
applyContextInfoAndMentions(interactiveMessage, message);
|
|
481
|
+
m = { interactiveMessage };
|
|
482
|
+
}
|
|
483
|
+
else if ('buttons' in message && !!message.buttons) {
|
|
484
|
+
const buttonsMessage = {
|
|
485
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
486
|
+
};
|
|
487
|
+
if ('text' in message) {
|
|
488
|
+
buttonsMessage.contentText = message.text;
|
|
489
|
+
buttonsMessage.headerType = ButtonType.EMPTY;
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
if ('caption' in message) {
|
|
493
|
+
buttonsMessage.contentText = message.caption;
|
|
494
|
+
}
|
|
495
|
+
const contentType = getContentType(m);
|
|
496
|
+
const type = contentType.replace('Message', '').toUpperCase();
|
|
497
|
+
buttonsMessage.headerType = ButtonType[type];
|
|
498
|
+
Object.assign(buttonsMessage, m);
|
|
499
|
+
}
|
|
500
|
+
if ('footer' in message && !!message.footer) {
|
|
501
|
+
buttonsMessage.footerText = message.footer;
|
|
502
|
+
}
|
|
503
|
+
m = { buttonsMessage };
|
|
504
|
+
}
|
|
505
|
+
else if ('richResponse' in message) {
|
|
506
|
+
const {
|
|
507
|
+
text,
|
|
508
|
+
code,
|
|
509
|
+
language = 'javascript',
|
|
510
|
+
botJid = '867051314767696@bot',
|
|
511
|
+
table,
|
|
512
|
+
latex,
|
|
513
|
+
map,
|
|
514
|
+
imageUrl,
|
|
515
|
+
imageUrls,
|
|
516
|
+
responseId,
|
|
517
|
+
messageSecret: richSecret
|
|
518
|
+
} = message.richResponse
|
|
519
|
+
const sections = []
|
|
520
|
+
if (text) {
|
|
521
|
+
sections.push({
|
|
522
|
+
view_model: {
|
|
523
|
+
primitive: { text, __typename: 'GenAIMarkdownTextUXPrimitive' },
|
|
524
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
525
|
+
}
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
if (code) {
|
|
529
|
+
sections.push({
|
|
530
|
+
view_model: {
|
|
531
|
+
primitive: {
|
|
532
|
+
language,
|
|
533
|
+
code_blocks: tokenizeCode(String(code)),
|
|
534
|
+
__typename: 'GenAICodeUXPrimitive'
|
|
535
|
+
},
|
|
536
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
if (table && Array.isArray(table.rows)) {
|
|
541
|
+
sections.push({
|
|
542
|
+
view_model: {
|
|
543
|
+
primitive: {
|
|
544
|
+
rows: table.rows.map(row => ({
|
|
545
|
+
cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells
|
|
546
|
+
})),
|
|
547
|
+
__typename: 'GenAITableUXPrimitive'
|
|
548
|
+
},
|
|
549
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
}
|
|
553
|
+
if (latex) {
|
|
554
|
+
const expressions = Array.isArray(latex)
|
|
555
|
+
? latex.map(e => (typeof e === 'string' ? { expression: e } : e))
|
|
556
|
+
: [{ expression: String(latex) }]
|
|
557
|
+
sections.push({
|
|
558
|
+
view_model: {
|
|
559
|
+
primitive: { expressions, __typename: 'GenAILatexUXPrimitive' },
|
|
560
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
}
|
|
564
|
+
if (map) {
|
|
565
|
+
sections.push({
|
|
566
|
+
view_model: {
|
|
567
|
+
primitive: {
|
|
568
|
+
latitude: map.latitude,
|
|
569
|
+
longitude: map.longitude,
|
|
570
|
+
zoom: map.zoom,
|
|
571
|
+
title: map.title,
|
|
572
|
+
annotations: map.annotations || [],
|
|
573
|
+
__typename: 'GenAIMapUXPrimitive'
|
|
574
|
+
},
|
|
575
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
if (imageUrl) {
|
|
580
|
+
sections.push({
|
|
581
|
+
view_model: {
|
|
582
|
+
primitive: { url: imageUrl, __typename: 'GenAIInlineImageUXPrimitive' },
|
|
583
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
}
|
|
587
|
+
if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
|
|
588
|
+
sections.push({
|
|
589
|
+
view_model: {
|
|
590
|
+
primitive: {
|
|
591
|
+
urls: imageUrls.map(u => (typeof u === 'string' ? { url: u } : u)),
|
|
592
|
+
__typename: 'GenAIGridImageUXPrimitive'
|
|
593
|
+
},
|
|
594
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
}
|
|
598
|
+
if (!sections.length && !text) {
|
|
599
|
+
sections.push({
|
|
600
|
+
view_model: {
|
|
601
|
+
primitive: { text: '', __typename: 'GenAIMarkdownTextUXPrimitive' },
|
|
602
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
603
|
+
}
|
|
604
|
+
})
|
|
605
|
+
}
|
|
606
|
+
const unifiedData = {
|
|
607
|
+
response_id: responseId || randomUUID(),
|
|
608
|
+
sections
|
|
609
|
+
}
|
|
610
|
+
return proto.Message.fromObject({
|
|
611
|
+
messageContextInfo: {
|
|
612
|
+
deviceListMetadata: {},
|
|
613
|
+
deviceListMetadataVersion: 2,
|
|
614
|
+
messageSecret: richSecret || randomBytes(32)
|
|
615
|
+
},
|
|
616
|
+
botForwardedMessage: {
|
|
617
|
+
message: {
|
|
618
|
+
richResponseMessage: {
|
|
619
|
+
submessages: [],
|
|
620
|
+
messageType: 1,
|
|
621
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify(unifiedData)) },
|
|
622
|
+
contextInfo: {
|
|
623
|
+
forwardingScore: 2,
|
|
624
|
+
isForwarded: true,
|
|
625
|
+
forwardedAiBotMessageInfo: { botJid },
|
|
626
|
+
botMessageSharingInfo: {
|
|
627
|
+
botEntryPointOrigin: 1,
|
|
628
|
+
forwardScore: 2
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
})
|
|
635
|
+
}
|
|
636
|
+
else if (hasNonNullishProperty(message, 'text')) {
|
|
289
637
|
const extContent = { text: message.text };
|
|
290
638
|
let urlInfo = message.linkPreview;
|
|
291
639
|
if (typeof urlInfo === 'undefined') {
|
|
292
|
-
urlInfo = await
|
|
640
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
293
641
|
}
|
|
294
642
|
if (urlInfo) {
|
|
295
|
-
extContent.canonicalUrl = urlInfo['canonical-url'];
|
|
296
643
|
extContent.matchedText = urlInfo['matched-text'];
|
|
297
644
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
298
645
|
extContent.description = urlInfo.description;
|
|
@@ -317,218 +664,274 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
317
664
|
}
|
|
318
665
|
m.extendedTextMessage = extContent;
|
|
319
666
|
}
|
|
320
|
-
else if ('contacts'
|
|
667
|
+
else if (hasNonNullishProperty(message, 'contacts')) {
|
|
321
668
|
const contactLen = message.contacts.contacts.length;
|
|
322
669
|
if (!contactLen) {
|
|
323
|
-
throw new
|
|
670
|
+
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
324
671
|
}
|
|
325
672
|
if (contactLen === 1) {
|
|
326
|
-
m.contactMessage =
|
|
673
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
327
674
|
}
|
|
328
675
|
else {
|
|
329
|
-
m.contactsArrayMessage =
|
|
676
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
330
677
|
}
|
|
331
678
|
}
|
|
332
|
-
else if ('
|
|
333
|
-
m.
|
|
679
|
+
else if (hasNonNullishProperty(message, 'location')) {
|
|
680
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
334
681
|
}
|
|
335
|
-
else if ('
|
|
336
|
-
m.locationMessage = Types_1.WAProto.Message.LocationMessage.fromObject(message.location);
|
|
337
|
-
}
|
|
338
|
-
else if ('react' in message) {
|
|
682
|
+
else if (hasNonNullishProperty(message, 'react')) {
|
|
339
683
|
if (!message.react.senderTimestampMs) {
|
|
340
684
|
message.react.senderTimestampMs = Date.now();
|
|
341
685
|
}
|
|
342
|
-
m.reactionMessage =
|
|
686
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
343
687
|
}
|
|
344
|
-
else if ('delete'
|
|
688
|
+
else if (hasNonNullishProperty(message, 'delete')) {
|
|
345
689
|
m.protocolMessage = {
|
|
346
690
|
key: message.delete,
|
|
347
|
-
type:
|
|
691
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
348
692
|
};
|
|
349
693
|
}
|
|
350
|
-
else if ('forward'
|
|
351
|
-
m =
|
|
694
|
+
else if (hasNonNullishProperty(message, 'forward')) {
|
|
695
|
+
m = generateForwardMessageContent(message.forward, message.force);
|
|
696
|
+
}
|
|
697
|
+
else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
|
|
698
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
699
|
+
? message.disappearingMessagesInChat
|
|
700
|
+
? WA_DEFAULT_EPHEMERAL
|
|
701
|
+
: 0
|
|
702
|
+
: message.disappearingMessagesInChat;
|
|
703
|
+
m = prepareDisappearingMessageSettingContent(exp);
|
|
704
|
+
}
|
|
705
|
+
else if (hasNonNullishProperty(message, 'groupInvite')) {
|
|
706
|
+
m.groupInviteMessage = {};
|
|
707
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
|
|
708
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
|
|
709
|
+
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
710
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
711
|
+
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
712
|
+
//TODO: use built-in interface and get disappearing mode info etc.
|
|
713
|
+
//TODO: cache / use store!?
|
|
714
|
+
if (options.getProfilePicUrl) {
|
|
715
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
716
|
+
if (pfpUrl) {
|
|
717
|
+
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
718
|
+
if (resp.ok) {
|
|
719
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
720
|
+
m.groupInviteMessage.jpegThumbnail = buf;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
352
724
|
}
|
|
353
|
-
else if ('
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
m =
|
|
725
|
+
else if (hasNonNullishProperty(message, 'pin')) {
|
|
726
|
+
m.pinInChatMessage = {};
|
|
727
|
+
m.messageContextInfo = {};
|
|
728
|
+
m.pinInChatMessage.key = message.pin;
|
|
729
|
+
m.pinInChatMessage.type = message.type;
|
|
730
|
+
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
731
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
358
732
|
}
|
|
359
|
-
else if ('buttonReply'
|
|
733
|
+
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
360
734
|
switch (message.type) {
|
|
361
735
|
case 'template':
|
|
362
736
|
m.templateButtonReplyMessage = {
|
|
363
737
|
selectedDisplayText: message.buttonReply.displayText,
|
|
364
738
|
selectedId: message.buttonReply.id,
|
|
365
|
-
selectedIndex: message.buttonReply.index
|
|
739
|
+
selectedIndex: message.buttonReply.index
|
|
366
740
|
};
|
|
367
741
|
break;
|
|
368
742
|
case 'plain':
|
|
369
743
|
m.buttonsResponseMessage = {
|
|
370
744
|
selectedButtonId: message.buttonReply.id,
|
|
371
745
|
selectedDisplayText: message.buttonReply.displayText,
|
|
372
|
-
type:
|
|
746
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
373
747
|
};
|
|
374
748
|
break;
|
|
375
749
|
}
|
|
376
750
|
}
|
|
377
|
-
else if ('
|
|
378
|
-
const {
|
|
379
|
-
m.
|
|
751
|
+
else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
|
|
752
|
+
const { videoMessage } = await prepareWAMessageMedia({ video: message.ptv }, options);
|
|
753
|
+
m.ptvMessage = videoMessage;
|
|
754
|
+
}
|
|
755
|
+
else if (hasNonNullishProperty(message, 'product')) {
|
|
756
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
|
|
757
|
+
m.productMessage = WAProto.Message.ProductMessage.create({
|
|
380
758
|
...message,
|
|
381
759
|
product: {
|
|
382
760
|
...message.product,
|
|
383
|
-
productImage: imageMessage
|
|
761
|
+
productImage: imageMessage
|
|
384
762
|
}
|
|
385
763
|
});
|
|
386
764
|
}
|
|
387
|
-
else if ('listReply'
|
|
765
|
+
else if (hasNonNullishProperty(message, 'listReply')) {
|
|
388
766
|
m.listResponseMessage = { ...message.listReply };
|
|
389
767
|
}
|
|
390
|
-
else if ('
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
768
|
+
else if (hasNonNullishProperty(message, 'event')) {
|
|
769
|
+
m.eventMessage = {};
|
|
770
|
+
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
771
|
+
if (message.event.call && options.getCallLink) {
|
|
772
|
+
const token = await options.getCallLink(message.event.call, { startTime });
|
|
773
|
+
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
774
|
+
}
|
|
775
|
+
m.messageContextInfo = {
|
|
776
|
+
// encKey
|
|
777
|
+
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
778
|
+
};
|
|
779
|
+
m.eventMessage.name = message.event.name;
|
|
780
|
+
m.eventMessage.description = message.event.description;
|
|
781
|
+
m.eventMessage.startTime = startTime;
|
|
782
|
+
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
783
|
+
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
784
|
+
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
785
|
+
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
786
|
+
m.eventMessage.location = message.event.location;
|
|
396
787
|
}
|
|
397
|
-
else if ('poll'
|
|
398
|
-
(
|
|
788
|
+
else if (hasNonNullishProperty(message, 'poll')) {
|
|
789
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
790
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
399
791
|
if (!Array.isArray(message.poll.values)) {
|
|
400
|
-
throw new
|
|
792
|
+
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
401
793
|
}
|
|
402
|
-
if (message.poll.selectableCount < 0
|
|
403
|
-
|
|
404
|
-
|
|
794
|
+
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
795
|
+
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
796
|
+
statusCode: 400
|
|
797
|
+
});
|
|
405
798
|
}
|
|
406
799
|
m.messageContextInfo = {
|
|
407
800
|
// encKey
|
|
408
|
-
messageSecret: message.poll.messageSecret ||
|
|
801
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
409
802
|
};
|
|
410
|
-
|
|
803
|
+
const pollCreationMessage = {
|
|
411
804
|
name: message.poll.name,
|
|
412
805
|
selectableOptionsCount: message.poll.selectableCount,
|
|
413
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
414
|
-
pollType: message.poll.type,
|
|
415
|
-
correctAnswer: message.poll.answer
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
else if ('sharePhoneNumber' in message) {
|
|
419
|
-
m.protocolMessage = {
|
|
420
|
-
type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
806
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
421
807
|
};
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
m = await (0, exports.prepareWAMessageMedia)(message, options);
|
|
428
|
-
}
|
|
429
|
-
if ('buttons' in message && !!message.buttons) {
|
|
430
|
-
const buttonsMessage = {
|
|
431
|
-
buttons: message.buttons.map(b => ({ ...b, type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
432
|
-
};
|
|
433
|
-
if ('text' in message) {
|
|
434
|
-
buttonsMessage.contentText = message.text;
|
|
435
|
-
buttonsMessage.headerType = ButtonType.EMPTY;
|
|
808
|
+
if (message.poll.toAnnouncementGroup) {
|
|
809
|
+
// poll v2 is for community announcement groups (single select and multiple)
|
|
810
|
+
m.pollCreationMessageV2 = pollCreationMessage;
|
|
436
811
|
}
|
|
437
812
|
else {
|
|
438
|
-
if (
|
|
439
|
-
|
|
813
|
+
if (message.poll.selectableCount === 1) {
|
|
814
|
+
//poll v3 is for single select polls
|
|
815
|
+
m.pollCreationMessageV3 = pollCreationMessage;
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// poll for multiple choice polls
|
|
819
|
+
m.pollCreationMessage = pollCreationMessage;
|
|
440
820
|
}
|
|
441
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
442
|
-
buttonsMessage.headerType = ButtonType[type];
|
|
443
|
-
Object.assign(buttonsMessage, m);
|
|
444
|
-
}
|
|
445
|
-
if ('footer' in message && !!message.footer) {
|
|
446
|
-
buttonsMessage.footerText = message.footer;
|
|
447
821
|
}
|
|
448
|
-
m = { buttonsMessage };
|
|
449
822
|
}
|
|
450
|
-
else if ('
|
|
451
|
-
|
|
452
|
-
|
|
823
|
+
else if (hasNonNullishProperty(message, 'album')) {
|
|
824
|
+
m.albumMessage = {
|
|
825
|
+
expectedImageCount: message.album.expectedImageCount,
|
|
826
|
+
expectedVideoCount: message.album.expectedVideoCount
|
|
453
827
|
};
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if ('caption' in message) {
|
|
459
|
-
msg.hydratedContentText = message.caption;
|
|
460
|
-
}
|
|
461
|
-
Object.assign(msg, m);
|
|
462
|
-
}
|
|
463
|
-
if ('footer' in message && !!message.footer) {
|
|
464
|
-
msg.hydratedFooterText = message.footer;
|
|
465
|
-
}
|
|
466
|
-
m = {
|
|
467
|
-
templateMessage: {
|
|
468
|
-
fourRowTemplate: msg,
|
|
469
|
-
hydratedTemplate: msg
|
|
470
|
-
}
|
|
828
|
+
}
|
|
829
|
+
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
830
|
+
m.protocolMessage = {
|
|
831
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
471
832
|
};
|
|
472
833
|
}
|
|
473
|
-
if ('
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
834
|
+
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
835
|
+
m.requestPhoneNumberMessage = {};
|
|
836
|
+
}
|
|
837
|
+
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
838
|
+
m.protocolMessage = {
|
|
839
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
840
|
+
limitSharing: {
|
|
841
|
+
sharingLimited: message.limitSharing === true,
|
|
842
|
+
trigger: 1,
|
|
843
|
+
limitSharingSettingTimestamp: Date.now(),
|
|
844
|
+
initiatedByMe: true
|
|
845
|
+
}
|
|
481
846
|
};
|
|
482
|
-
m = { listMessage };
|
|
483
847
|
}
|
|
484
|
-
|
|
848
|
+
else {
|
|
849
|
+
m = await prepareWAMessageMedia(message, options);
|
|
850
|
+
}
|
|
851
|
+
if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
485
852
|
m = { viewOnceMessage: { message: m } };
|
|
486
853
|
}
|
|
487
|
-
if ('mentions'
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
m[messageType]
|
|
854
|
+
if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
|
|
855
|
+
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
|
|
856
|
+
const messageType = Object.keys(m)[0];
|
|
857
|
+
const key = m[messageType];
|
|
858
|
+
if (key && 'contextInfo' in key) {
|
|
859
|
+
key.contextInfo = key.contextInfo || {};
|
|
860
|
+
if (message.mentions?.length) {
|
|
861
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
862
|
+
}
|
|
863
|
+
if (message.mentionAll) {
|
|
864
|
+
key.contextInfo.nonJidMentions = 1;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
else if (key) {
|
|
868
|
+
key.contextInfo = {
|
|
869
|
+
mentionedJid: message.mentions,
|
|
870
|
+
nonJidMentions: message.mentionAll ? 1 : 0
|
|
871
|
+
};
|
|
872
|
+
}
|
|
491
873
|
}
|
|
492
|
-
if ('edit'
|
|
874
|
+
if (hasOptionalProperty(message, 'edit')) {
|
|
493
875
|
m = {
|
|
494
876
|
protocolMessage: {
|
|
495
877
|
key: message.edit,
|
|
496
878
|
editedMessage: m,
|
|
497
879
|
timestampMs: Date.now(),
|
|
498
|
-
type:
|
|
880
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
|
|
885
|
+
const messageType = Object.keys(m)[0];
|
|
886
|
+
const key = m[messageType];
|
|
887
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
888
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
889
|
+
}
|
|
890
|
+
else if (key) {
|
|
891
|
+
key.contextInfo = message.contextInfo;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (hasOptionalProperty(message, 'albumParentKey') && !!message.albumParentKey) {
|
|
895
|
+
m.messageContextInfo = {
|
|
896
|
+
...m.messageContextInfo,
|
|
897
|
+
messageAssociation: {
|
|
898
|
+
associationType: WAProto.MessageAssociation.AssociationType.MEDIA_ALBUM,
|
|
899
|
+
parentMessageKey: message.albumParentKey
|
|
499
900
|
}
|
|
500
901
|
};
|
|
501
902
|
}
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
903
|
+
if (shouldIncludeReportingToken(m)) {
|
|
904
|
+
m.messageContextInfo = m.messageContextInfo || {};
|
|
905
|
+
if (!m.messageContextInfo.messageSecret) {
|
|
906
|
+
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
907
|
+
}
|
|
506
908
|
}
|
|
507
|
-
return
|
|
909
|
+
return WAProto.Message.create(m);
|
|
508
910
|
};
|
|
509
|
-
|
|
510
|
-
const generateWAMessageFromContent = (jid, message, options) => {
|
|
911
|
+
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
511
912
|
// set timestamp to now
|
|
512
913
|
// if not specified
|
|
513
914
|
if (!options.timestamp) {
|
|
514
915
|
options.timestamp = new Date();
|
|
515
916
|
}
|
|
516
|
-
const innerMessage =
|
|
517
|
-
const key =
|
|
518
|
-
const timestamp =
|
|
917
|
+
const innerMessage = normalizeMessageContent(message);
|
|
918
|
+
const key = getContentType(innerMessage);
|
|
919
|
+
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
519
920
|
const { quoted, userJid } = options;
|
|
520
|
-
if (quoted && !(
|
|
521
|
-
const participant = quoted.key.fromMe
|
|
522
|
-
|
|
523
|
-
|
|
921
|
+
if (quoted && !isJidNewsletter(jid)) {
|
|
922
|
+
const participant = quoted.key.fromMe
|
|
923
|
+
? userJid // TODO: Add support for LIDs
|
|
924
|
+
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
925
|
+
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
926
|
+
const msgType = getContentType(quotedMsg);
|
|
524
927
|
// strip any redundant properties
|
|
525
|
-
quotedMsg =
|
|
928
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
526
929
|
const quotedContent = quotedMsg[msgType];
|
|
527
930
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
528
931
|
delete quotedContent.contextInfo;
|
|
529
932
|
}
|
|
530
|
-
const contextInfo = innerMessage[key]
|
|
531
|
-
contextInfo.participant =
|
|
933
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
934
|
+
contextInfo.participant = jidNormalizedUser(participant);
|
|
532
935
|
contextInfo.stanzaId = quoted.key.id;
|
|
533
936
|
contextInfo.quotedMessage = quotedMsg;
|
|
534
937
|
// if a participant is quoted, then it must be a group
|
|
@@ -536,62 +939,63 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
536
939
|
if (jid !== quoted.key.remoteJid) {
|
|
537
940
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
538
941
|
}
|
|
539
|
-
innerMessage[key]
|
|
942
|
+
if (contextInfo && innerMessage[key]) {
|
|
943
|
+
/* @ts-ignore */
|
|
944
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
945
|
+
}
|
|
540
946
|
}
|
|
541
947
|
if (
|
|
542
948
|
// if we want to send a disappearing message
|
|
543
|
-
!!
|
|
949
|
+
!!options?.ephemeralExpiration &&
|
|
544
950
|
// and it's not a protocol message -- delete, toggle disappear message
|
|
545
951
|
key !== 'protocolMessage' &&
|
|
546
952
|
// already not converted to disappearing message
|
|
547
953
|
key !== 'ephemeralMessage' &&
|
|
548
|
-
//
|
|
549
|
-
!(
|
|
954
|
+
// newsletters don't support ephemeral messages
|
|
955
|
+
!isJidNewsletter(jid)) {
|
|
956
|
+
/* @ts-ignore */
|
|
550
957
|
innerMessage[key].contextInfo = {
|
|
551
958
|
...(innerMessage[key].contextInfo || {}),
|
|
552
|
-
expiration: options.ephemeralExpiration ||
|
|
959
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
553
960
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
554
961
|
};
|
|
555
962
|
}
|
|
556
|
-
message =
|
|
963
|
+
message = WAProto.Message.create(message);
|
|
557
964
|
const messageJSON = {
|
|
558
965
|
key: {
|
|
559
966
|
remoteJid: jid,
|
|
560
967
|
fromMe: true,
|
|
561
|
-
id:
|
|
968
|
+
id: options?.messageId || generateMessageIDV2()
|
|
562
969
|
},
|
|
563
970
|
message: message,
|
|
564
971
|
messageTimestamp: timestamp,
|
|
565
972
|
messageStubParameters: [],
|
|
566
|
-
participant:
|
|
567
|
-
status:
|
|
973
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
974
|
+
status: WAMessageStatus.PENDING
|
|
568
975
|
};
|
|
569
|
-
return
|
|
976
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
570
977
|
};
|
|
571
|
-
|
|
572
|
-
const generateWAMessage = async (jid, content, options) => {
|
|
573
|
-
var _a;
|
|
978
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
574
979
|
// ensure msg ID is with every log
|
|
575
|
-
options.logger =
|
|
576
|
-
|
|
980
|
+
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
981
|
+
// Pass jid in the options to generateWAMessageContent
|
|
982
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
|
|
577
983
|
};
|
|
578
|
-
exports.generateWAMessage = generateWAMessage;
|
|
579
984
|
/** Get the key to access the true type of content */
|
|
580
|
-
const getContentType = (content) => {
|
|
985
|
+
export const getContentType = (content) => {
|
|
581
986
|
if (content) {
|
|
582
987
|
const keys = Object.keys(content);
|
|
583
988
|
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
584
989
|
return key;
|
|
585
990
|
}
|
|
586
991
|
};
|
|
587
|
-
exports.getContentType = getContentType;
|
|
588
992
|
/**
|
|
589
993
|
* Normalizes ephemeral, view once messages to regular message content
|
|
590
994
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
591
995
|
* @param content
|
|
592
996
|
* @returns
|
|
593
997
|
*/
|
|
594
|
-
const normalizeMessageContent = (content) => {
|
|
998
|
+
export const normalizeMessageContent = (content) => {
|
|
595
999
|
if (!content) {
|
|
596
1000
|
return undefined;
|
|
597
1001
|
}
|
|
@@ -605,21 +1009,22 @@ const normalizeMessageContent = (content) => {
|
|
|
605
1009
|
}
|
|
606
1010
|
return content;
|
|
607
1011
|
function getFutureProofMessage(message) {
|
|
608
|
-
return (
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1012
|
+
return (message?.ephemeralMessage ||
|
|
1013
|
+
message?.viewOnceMessage ||
|
|
1014
|
+
message?.documentWithCaptionMessage ||
|
|
1015
|
+
message?.viewOnceMessageV2 ||
|
|
1016
|
+
message?.viewOnceMessageV2Extension ||
|
|
1017
|
+
message?.editedMessage ||
|
|
1018
|
+
message?.associatedChildMessage ||
|
|
1019
|
+
message?.groupStatusMessage ||
|
|
1020
|
+
message?.groupStatusMessageV2);
|
|
614
1021
|
}
|
|
615
1022
|
};
|
|
616
|
-
exports.normalizeMessageContent = normalizeMessageContent;
|
|
617
1023
|
/**
|
|
618
1024
|
* Extract the true message content from a message
|
|
619
1025
|
* Eg. extracts the inner message from a disappearing message/view once message
|
|
620
1026
|
*/
|
|
621
|
-
const extractMessageContent = (content) => {
|
|
622
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1027
|
+
export const extractMessageContent = (content) => {
|
|
623
1028
|
const extractFromTemplateMessage = (msg) => {
|
|
624
1029
|
if (msg.imageMessage) {
|
|
625
1030
|
return { imageMessage: msg.imageMessage };
|
|
@@ -635,35 +1040,39 @@ const extractMessageContent = (content) => {
|
|
|
635
1040
|
}
|
|
636
1041
|
else {
|
|
637
1042
|
return {
|
|
638
|
-
conversation: 'contentText' in msg
|
|
639
|
-
? msg.contentText
|
|
640
|
-
: ('hydratedContentText' in msg ? msg.hydratedContentText : '')
|
|
1043
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
641
1044
|
};
|
|
642
1045
|
}
|
|
643
1046
|
};
|
|
644
|
-
content =
|
|
645
|
-
if (content
|
|
1047
|
+
content = normalizeMessageContent(content);
|
|
1048
|
+
if (content?.buttonsMessage) {
|
|
646
1049
|
return extractFromTemplateMessage(content.buttonsMessage);
|
|
647
1050
|
}
|
|
648
|
-
if (
|
|
649
|
-
return extractFromTemplateMessage(
|
|
1051
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
1052
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
650
1053
|
}
|
|
651
|
-
if (
|
|
652
|
-
return extractFromTemplateMessage(
|
|
1054
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
1055
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
653
1056
|
}
|
|
654
|
-
if (
|
|
655
|
-
return extractFromTemplateMessage(
|
|
1057
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
1058
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
656
1059
|
}
|
|
657
1060
|
return content;
|
|
658
1061
|
};
|
|
659
|
-
exports.extractMessageContent = extractMessageContent;
|
|
660
1062
|
/**
|
|
661
1063
|
* Returns the device predicted by message ID
|
|
662
1064
|
*/
|
|
663
|
-
const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
664
|
-
|
|
1065
|
+
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
1066
|
+
? 'ios'
|
|
1067
|
+
: /^3E.{20}$/.test(id)
|
|
1068
|
+
? 'web'
|
|
1069
|
+
: /^(.{21}|.{32})$/.test(id)
|
|
1070
|
+
? 'android'
|
|
1071
|
+
: /^(3F|.{18}$)/.test(id)
|
|
1072
|
+
? 'desktop'
|
|
1073
|
+
: 'wa bot';
|
|
665
1074
|
/** Upserts a receipt in the message */
|
|
666
|
-
const updateMessageWithReceipt = (msg, receipt) => {
|
|
1075
|
+
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
667
1076
|
msg.userReceipt = msg.userReceipt || [];
|
|
668
1077
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
669
1078
|
if (recp) {
|
|
@@ -673,41 +1082,43 @@ const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
673
1082
|
msg.userReceipt.push(receipt);
|
|
674
1083
|
}
|
|
675
1084
|
};
|
|
676
|
-
exports.updateMessageWithReceipt = updateMessageWithReceipt;
|
|
677
1085
|
/** Update the message with a new reaction */
|
|
678
|
-
const updateMessageWithReaction = (msg, reaction) => {
|
|
679
|
-
const authorID =
|
|
680
|
-
const reactions = (msg.reactions || [])
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
reactions.push(reaction);
|
|
684
|
-
}
|
|
1086
|
+
export const updateMessageWithReaction = (msg, reaction) => {
|
|
1087
|
+
const authorID = getKeyAuthor(reaction.key);
|
|
1088
|
+
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
1089
|
+
reaction.text = reaction.text || '';
|
|
1090
|
+
reactions.push(reaction);
|
|
685
1091
|
msg.reactions = reactions;
|
|
686
1092
|
};
|
|
687
|
-
exports.updateMessageWithReaction = updateMessageWithReaction;
|
|
688
1093
|
/** Update the message with a new poll update */
|
|
689
|
-
const updateMessageWithPollUpdate = (msg, update) => {
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
.filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
|
|
694
|
-
if ((_b = (_a = update.vote) === null || _a === void 0 ? void 0 : _a.selectedOptions) === null || _b === void 0 ? void 0 : _b.length) {
|
|
1094
|
+
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
1095
|
+
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
1096
|
+
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
1097
|
+
if (update.vote?.selectedOptions?.length) {
|
|
695
1098
|
reactions.push(update);
|
|
696
1099
|
}
|
|
697
1100
|
msg.pollUpdates = reactions;
|
|
698
1101
|
};
|
|
699
|
-
|
|
1102
|
+
/** Update the message with a new event response */
|
|
1103
|
+
export const updateMessageWithEventResponse = (msg, update) => {
|
|
1104
|
+
const authorID = getKeyAuthor(update.eventResponseMessageKey);
|
|
1105
|
+
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
|
|
1106
|
+
responses.push(update);
|
|
1107
|
+
msg.eventResponses = responses;
|
|
1108
|
+
};
|
|
700
1109
|
/**
|
|
701
1110
|
* Aggregates all poll updates in a poll.
|
|
702
1111
|
* @param msg the poll creation message
|
|
703
1112
|
* @param meId your jid
|
|
704
1113
|
* @returns A list of options & their voters
|
|
705
1114
|
*/
|
|
706
|
-
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
707
|
-
|
|
708
|
-
|
|
1115
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
1116
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
1117
|
+
message?.pollCreationMessageV2?.options ||
|
|
1118
|
+
message?.pollCreationMessageV3?.options ||
|
|
1119
|
+
[];
|
|
709
1120
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
710
|
-
const hash =
|
|
1121
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString();
|
|
711
1122
|
acc[hash] = {
|
|
712
1123
|
name: opt.optionName || '',
|
|
713
1124
|
voters: []
|
|
@@ -729,14 +1140,36 @@ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
729
1140
|
};
|
|
730
1141
|
data = voteHashMap[hash];
|
|
731
1142
|
}
|
|
732
|
-
voteHashMap[hash].voters.push(
|
|
1143
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
|
|
733
1144
|
}
|
|
734
1145
|
}
|
|
735
1146
|
return Object.values(voteHashMap);
|
|
736
1147
|
}
|
|
737
|
-
|
|
1148
|
+
/**
|
|
1149
|
+
* Aggregates all event responses in an event message.
|
|
1150
|
+
* @param msg the event creation message
|
|
1151
|
+
* @param meId your jid
|
|
1152
|
+
* @returns A list of response types & their responders
|
|
1153
|
+
*/
|
|
1154
|
+
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
1155
|
+
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
1156
|
+
const responseMap = {};
|
|
1157
|
+
for (const type of responseTypes) {
|
|
1158
|
+
responseMap[type] = {
|
|
1159
|
+
response: type,
|
|
1160
|
+
responders: []
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
for (const update of eventResponses || []) {
|
|
1164
|
+
const responseType = update.eventResponse || 'UNKNOWN';
|
|
1165
|
+
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
1166
|
+
responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return Object.values(responseMap);
|
|
1170
|
+
}
|
|
738
1171
|
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
739
|
-
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
1172
|
+
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
740
1173
|
const keyMap = {};
|
|
741
1174
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
742
1175
|
if (!fromMe) {
|
|
@@ -753,40 +1186,34 @@ const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
753
1186
|
}
|
|
754
1187
|
return Object.values(keyMap);
|
|
755
1188
|
};
|
|
756
|
-
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
|
|
757
1189
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
758
1190
|
/**
|
|
759
1191
|
* Downloads the given message. Throws an error if it's not a media message
|
|
760
1192
|
*/
|
|
761
|
-
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
762
|
-
const result = await downloadMsg()
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
message = await ctx.reuploadRequest(message);
|
|
772
|
-
const result = await downloadMsg();
|
|
773
|
-
return result;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
1193
|
+
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
1194
|
+
const result = await downloadMsg().catch(async (error) => {
|
|
1195
|
+
if (ctx &&
|
|
1196
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
1197
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
1198
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
1199
|
+
// request reupload
|
|
1200
|
+
message = await ctx.reuploadRequest(message);
|
|
1201
|
+
const result = await downloadMsg();
|
|
1202
|
+
return result;
|
|
776
1203
|
}
|
|
777
1204
|
throw error;
|
|
778
1205
|
});
|
|
779
1206
|
return result;
|
|
780
1207
|
async function downloadMsg() {
|
|
781
|
-
const mContent =
|
|
1208
|
+
const mContent = extractMessageContent(message.message);
|
|
782
1209
|
if (!mContent) {
|
|
783
|
-
throw new
|
|
1210
|
+
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
784
1211
|
}
|
|
785
|
-
const contentType =
|
|
786
|
-
let mediaType = contentType
|
|
1212
|
+
const contentType = getContentType(mContent);
|
|
1213
|
+
let mediaType = contentType?.replace('Message', '');
|
|
787
1214
|
const media = mContent[contentType];
|
|
788
1215
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
789
|
-
throw new
|
|
1216
|
+
throw new Boom(`"${contentType}" message is not a media message`);
|
|
790
1217
|
}
|
|
791
1218
|
let download;
|
|
792
1219
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
@@ -799,7 +1226,7 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
799
1226
|
else {
|
|
800
1227
|
download = media;
|
|
801
1228
|
}
|
|
802
|
-
const stream = await
|
|
1229
|
+
const stream = await downloadContentFromMessage(download, mediaType, options);
|
|
803
1230
|
if (type === 'buffer') {
|
|
804
1231
|
const bufferArray = [];
|
|
805
1232
|
for await (const chunk of stream) {
|
|
@@ -810,18 +1237,17 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
810
1237
|
return stream;
|
|
811
1238
|
}
|
|
812
1239
|
};
|
|
813
|
-
exports.downloadMediaMessage = downloadMediaMessage;
|
|
814
1240
|
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
815
|
-
const assertMediaContent = (content) => {
|
|
816
|
-
content =
|
|
817
|
-
const mediaContent =
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1241
|
+
export const assertMediaContent = (content) => {
|
|
1242
|
+
content = extractMessageContent(content);
|
|
1243
|
+
const mediaContent = content?.documentMessage ||
|
|
1244
|
+
content?.imageMessage ||
|
|
1245
|
+
content?.videoMessage ||
|
|
1246
|
+
content?.audioMessage ||
|
|
1247
|
+
content?.stickerMessage;
|
|
822
1248
|
if (!mediaContent) {
|
|
823
|
-
throw new
|
|
1249
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
824
1250
|
}
|
|
825
1251
|
return mediaContent;
|
|
826
1252
|
};
|
|
827
|
-
|
|
1253
|
+
//# sourceMappingURL=messages.js.map
|