@badzz88/baileys 8.3.0 → 8.4.1
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/README.md +27 -27
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-import.js +29 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +204305 -131020
- package/engine-requirements.js +3 -4
- package/lib/Defaults/baileys-version.json +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 -404
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/community.js +392 -0
- package/lib/Socket/dugong.js +161 -8
- package/lib/Socket/groups.js +154 -160
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -624
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +139 -314
- package/lib/Socket/registration.js +2 -1
- package/lib/Socket/socket.js +544 -242
- 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 +188 -283
- 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 +796 -359
- 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 +39 -53
- package/package.json +134 -101
- package/lib/Defaults/index.d.ts +0 -53
- 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/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/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/dugong.d.ts +0 -254
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -149
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/socket.d.ts +0 -43
- package/lib/Socket/usync.d.ts +0 -36
- 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/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/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/WAUSync/index.d.ts +0 -3
- 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,235 +186,460 @@ 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: "120363420249672073@newsletter",
|
|
109
|
-
serverMessageId: 0,
|
|
110
|
-
newsletterName: "kyuu ilysm",
|
|
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
|
})
|
|
224
336
|
});
|
|
225
|
-
|
|
226
337
|
if (uploadData.ptv) {
|
|
227
338
|
obj.ptvMessage = obj.videoMessage;
|
|
228
339
|
delete obj.videoMessage;
|
|
229
340
|
}
|
|
230
|
-
|
|
231
341
|
if (cacheableKey) {
|
|
232
|
-
logger
|
|
233
|
-
options.mediaCache.set(cacheableKey,
|
|
342
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
343
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
234
344
|
}
|
|
235
|
-
|
|
236
345
|
return obj;
|
|
237
346
|
};
|
|
238
|
-
|
|
239
|
-
const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
347
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
240
348
|
ephemeralExpiration = ephemeralExpiration || 0;
|
|
241
349
|
const content = {
|
|
242
350
|
ephemeralMessage: {
|
|
243
351
|
message: {
|
|
244
352
|
protocolMessage: {
|
|
245
|
-
type:
|
|
353
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
246
354
|
ephemeralExpiration
|
|
247
355
|
}
|
|
248
356
|
}
|
|
249
357
|
}
|
|
250
358
|
};
|
|
251
|
-
return
|
|
359
|
+
return WAProto.Message.fromObject(content);
|
|
252
360
|
};
|
|
253
|
-
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
|
|
254
361
|
/**
|
|
255
362
|
* Generate forwarded message content like WA does
|
|
256
363
|
* @param message the message to forward
|
|
257
364
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
258
365
|
*/
|
|
259
|
-
const generateForwardMessageContent = (message, forceForward) => {
|
|
260
|
-
var _a;
|
|
366
|
+
export const generateForwardMessageContent = (message, forceForward) => {
|
|
261
367
|
let content = message.message;
|
|
262
368
|
if (!content) {
|
|
263
|
-
throw new
|
|
369
|
+
throw new Boom('no content in message', { statusCode: 400 });
|
|
264
370
|
}
|
|
265
371
|
// hacky copy
|
|
266
|
-
content =
|
|
267
|
-
content =
|
|
372
|
+
content = normalizeMessageContent(content);
|
|
373
|
+
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
268
374
|
let key = Object.keys(content)[0];
|
|
269
|
-
let score =
|
|
375
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
270
376
|
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
271
377
|
if (key === 'conversation') {
|
|
272
378
|
content.extendedTextMessage = { text: content[key] };
|
|
273
379
|
delete content.conversation;
|
|
274
380
|
key = 'extendedTextMessage';
|
|
275
381
|
}
|
|
382
|
+
const key_ = content?.[key];
|
|
276
383
|
if (score > 0) {
|
|
277
|
-
|
|
384
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
278
385
|
}
|
|
279
386
|
else {
|
|
280
|
-
|
|
387
|
+
key_.contextInfo = {};
|
|
281
388
|
}
|
|
282
389
|
return content;
|
|
283
390
|
};
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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;
|
|
288
414
|
let m = {};
|
|
289
|
-
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')) {
|
|
290
637
|
const extContent = { text: message.text };
|
|
291
638
|
let urlInfo = message.linkPreview;
|
|
292
639
|
if (typeof urlInfo === 'undefined') {
|
|
293
|
-
urlInfo = await
|
|
640
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
294
641
|
}
|
|
295
642
|
if (urlInfo) {
|
|
296
|
-
extContent.canonicalUrl = urlInfo['canonical-url'];
|
|
297
643
|
extContent.matchedText = urlInfo['matched-text'];
|
|
298
644
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
299
645
|
extContent.description = urlInfo.description;
|
|
@@ -318,206 +664,274 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
318
664
|
}
|
|
319
665
|
m.extendedTextMessage = extContent;
|
|
320
666
|
}
|
|
321
|
-
else if ('contacts'
|
|
667
|
+
else if (hasNonNullishProperty(message, 'contacts')) {
|
|
322
668
|
const contactLen = message.contacts.contacts.length;
|
|
323
669
|
if (!contactLen) {
|
|
324
|
-
throw new
|
|
670
|
+
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
325
671
|
}
|
|
326
672
|
if (contactLen === 1) {
|
|
327
|
-
m.contactMessage =
|
|
673
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
328
674
|
}
|
|
329
675
|
else {
|
|
330
|
-
m.contactsArrayMessage =
|
|
676
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
331
677
|
}
|
|
332
678
|
}
|
|
333
|
-
else if ('location'
|
|
334
|
-
m.locationMessage =
|
|
679
|
+
else if (hasNonNullishProperty(message, 'location')) {
|
|
680
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
335
681
|
}
|
|
336
|
-
else if ('react'
|
|
682
|
+
else if (hasNonNullishProperty(message, 'react')) {
|
|
337
683
|
if (!message.react.senderTimestampMs) {
|
|
338
684
|
message.react.senderTimestampMs = Date.now();
|
|
339
685
|
}
|
|
340
|
-
m.reactionMessage =
|
|
686
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
341
687
|
}
|
|
342
|
-
else if ('delete'
|
|
688
|
+
else if (hasNonNullishProperty(message, 'delete')) {
|
|
343
689
|
m.protocolMessage = {
|
|
344
690
|
key: message.delete,
|
|
345
|
-
type:
|
|
691
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
346
692
|
};
|
|
347
693
|
}
|
|
348
|
-
else if ('forward'
|
|
349
|
-
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
|
+
}
|
|
350
724
|
}
|
|
351
|
-
else if ('
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
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;
|
|
356
732
|
}
|
|
357
|
-
else if ('buttonReply'
|
|
733
|
+
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
358
734
|
switch (message.type) {
|
|
359
735
|
case 'template':
|
|
360
736
|
m.templateButtonReplyMessage = {
|
|
361
737
|
selectedDisplayText: message.buttonReply.displayText,
|
|
362
738
|
selectedId: message.buttonReply.id,
|
|
363
|
-
selectedIndex: message.buttonReply.index
|
|
739
|
+
selectedIndex: message.buttonReply.index
|
|
364
740
|
};
|
|
365
741
|
break;
|
|
366
742
|
case 'plain':
|
|
367
743
|
m.buttonsResponseMessage = {
|
|
368
744
|
selectedButtonId: message.buttonReply.id,
|
|
369
745
|
selectedDisplayText: message.buttonReply.displayText,
|
|
370
|
-
type:
|
|
746
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
371
747
|
};
|
|
372
748
|
break;
|
|
373
749
|
}
|
|
374
750
|
}
|
|
375
|
-
else if ('
|
|
376
|
-
const {
|
|
377
|
-
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({
|
|
378
758
|
...message,
|
|
379
759
|
product: {
|
|
380
760
|
...message.product,
|
|
381
|
-
productImage: imageMessage
|
|
761
|
+
productImage: imageMessage
|
|
382
762
|
}
|
|
383
763
|
});
|
|
384
764
|
}
|
|
385
|
-
else if ('listReply'
|
|
765
|
+
else if (hasNonNullishProperty(message, 'listReply')) {
|
|
386
766
|
m.listResponseMessage = { ...message.listReply };
|
|
387
767
|
}
|
|
388
|
-
else if ('
|
|
389
|
-
|
|
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;
|
|
787
|
+
}
|
|
788
|
+
else if (hasNonNullishProperty(message, 'poll')) {
|
|
789
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
790
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
390
791
|
if (!Array.isArray(message.poll.values)) {
|
|
391
|
-
throw new
|
|
792
|
+
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
392
793
|
}
|
|
393
|
-
if (message.poll.selectableCount < 0
|
|
394
|
-
|
|
395
|
-
|
|
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
|
+
});
|
|
396
798
|
}
|
|
397
799
|
m.messageContextInfo = {
|
|
398
800
|
// encKey
|
|
399
|
-
messageSecret: message.poll.messageSecret ||
|
|
801
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
400
802
|
};
|
|
401
|
-
|
|
803
|
+
const pollCreationMessage = {
|
|
402
804
|
name: message.poll.name,
|
|
403
805
|
selectableOptionsCount: message.poll.selectableCount,
|
|
404
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
806
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
405
807
|
};
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
else if ('requestPhoneNumber' in message) {
|
|
413
|
-
m.requestPhoneNumberMessage = {};
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
m = await (0, exports.prepareWAMessageMedia)(message, options);
|
|
417
|
-
}
|
|
418
|
-
if ('buttons' in message && !!message.buttons) {
|
|
419
|
-
const buttonsMessage = {
|
|
420
|
-
buttons: message.buttons.map(b => ({ ...b, type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
421
|
-
};
|
|
422
|
-
if ('text' in message) {
|
|
423
|
-
buttonsMessage.contentText = message.text;
|
|
424
|
-
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;
|
|
425
811
|
}
|
|
426
812
|
else {
|
|
427
|
-
if (
|
|
428
|
-
|
|
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;
|
|
429
820
|
}
|
|
430
|
-
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
431
|
-
buttonsMessage.headerType = ButtonType[type];
|
|
432
|
-
Object.assign(buttonsMessage, m);
|
|
433
|
-
}
|
|
434
|
-
if ('footer' in message && !!message.footer) {
|
|
435
|
-
buttonsMessage.footerText = message.footer;
|
|
436
821
|
}
|
|
437
|
-
m = { buttonsMessage };
|
|
438
822
|
}
|
|
439
|
-
else if ('
|
|
440
|
-
|
|
441
|
-
|
|
823
|
+
else if (hasNonNullishProperty(message, 'album')) {
|
|
824
|
+
m.albumMessage = {
|
|
825
|
+
expectedImageCount: message.album.expectedImageCount,
|
|
826
|
+
expectedVideoCount: message.album.expectedVideoCount
|
|
442
827
|
};
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if ('caption' in message) {
|
|
448
|
-
msg.hydratedContentText = message.caption;
|
|
449
|
-
}
|
|
450
|
-
Object.assign(msg, m);
|
|
451
|
-
}
|
|
452
|
-
if ('footer' in message && !!message.footer) {
|
|
453
|
-
msg.hydratedFooterText = message.footer;
|
|
454
|
-
}
|
|
455
|
-
m = {
|
|
456
|
-
templateMessage: {
|
|
457
|
-
fourRowTemplate: msg,
|
|
458
|
-
hydratedTemplate: msg
|
|
459
|
-
}
|
|
828
|
+
}
|
|
829
|
+
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
830
|
+
m.protocolMessage = {
|
|
831
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
460
832
|
};
|
|
461
833
|
}
|
|
462
|
-
if ('
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
+
}
|
|
470
846
|
};
|
|
471
|
-
m = { listMessage };
|
|
472
847
|
}
|
|
473
|
-
|
|
848
|
+
else {
|
|
849
|
+
m = await prepareWAMessageMedia(message, options);
|
|
850
|
+
}
|
|
851
|
+
if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
474
852
|
m = { viewOnceMessage: { message: m } };
|
|
475
853
|
}
|
|
476
|
-
if ('mentions'
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
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
|
+
}
|
|
480
873
|
}
|
|
481
|
-
if ('edit'
|
|
874
|
+
if (hasOptionalProperty(message, 'edit')) {
|
|
482
875
|
m = {
|
|
483
876
|
protocolMessage: {
|
|
484
877
|
key: message.edit,
|
|
485
878
|
editedMessage: m,
|
|
486
879
|
timestampMs: Date.now(),
|
|
487
|
-
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
|
|
488
900
|
}
|
|
489
901
|
};
|
|
490
902
|
}
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
903
|
+
if (shouldIncludeReportingToken(m)) {
|
|
904
|
+
m.messageContextInfo = m.messageContextInfo || {};
|
|
905
|
+
if (!m.messageContextInfo.messageSecret) {
|
|
906
|
+
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
907
|
+
}
|
|
495
908
|
}
|
|
496
|
-
return
|
|
909
|
+
return WAProto.Message.create(m);
|
|
497
910
|
};
|
|
498
|
-
|
|
499
|
-
const generateWAMessageFromContent = (jid, message, options) => {
|
|
911
|
+
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
500
912
|
// set timestamp to now
|
|
501
913
|
// if not specified
|
|
502
914
|
if (!options.timestamp) {
|
|
503
915
|
options.timestamp = new Date();
|
|
504
916
|
}
|
|
505
|
-
const innerMessage =
|
|
506
|
-
const key =
|
|
507
|
-
const timestamp =
|
|
917
|
+
const innerMessage = normalizeMessageContent(message);
|
|
918
|
+
const key = getContentType(innerMessage);
|
|
919
|
+
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
508
920
|
const { quoted, userJid } = options;
|
|
509
|
-
if (quoted && !(
|
|
510
|
-
const participant = quoted.key.fromMe
|
|
511
|
-
|
|
512
|
-
|
|
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);
|
|
513
927
|
// strip any redundant properties
|
|
514
|
-
quotedMsg =
|
|
928
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
515
929
|
const quotedContent = quotedMsg[msgType];
|
|
516
930
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
517
931
|
delete quotedContent.contextInfo;
|
|
518
932
|
}
|
|
519
|
-
const contextInfo = innerMessage[key]
|
|
520
|
-
contextInfo.participant =
|
|
933
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
934
|
+
contextInfo.participant = jidNormalizedUser(participant);
|
|
521
935
|
contextInfo.stanzaId = quoted.key.id;
|
|
522
936
|
contextInfo.quotedMessage = quotedMsg;
|
|
523
937
|
// if a participant is quoted, then it must be a group
|
|
@@ -525,62 +939,63 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
525
939
|
if (jid !== quoted.key.remoteJid) {
|
|
526
940
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
527
941
|
}
|
|
528
|
-
innerMessage[key]
|
|
942
|
+
if (contextInfo && innerMessage[key]) {
|
|
943
|
+
/* @ts-ignore */
|
|
944
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
945
|
+
}
|
|
529
946
|
}
|
|
530
947
|
if (
|
|
531
948
|
// if we want to send a disappearing message
|
|
532
|
-
!!
|
|
949
|
+
!!options?.ephemeralExpiration &&
|
|
533
950
|
// and it's not a protocol message -- delete, toggle disappear message
|
|
534
951
|
key !== 'protocolMessage' &&
|
|
535
952
|
// already not converted to disappearing message
|
|
536
953
|
key !== 'ephemeralMessage' &&
|
|
537
|
-
//
|
|
538
|
-
!(
|
|
954
|
+
// newsletters don't support ephemeral messages
|
|
955
|
+
!isJidNewsletter(jid)) {
|
|
956
|
+
/* @ts-ignore */
|
|
539
957
|
innerMessage[key].contextInfo = {
|
|
540
958
|
...(innerMessage[key].contextInfo || {}),
|
|
541
|
-
expiration: options.ephemeralExpiration ||
|
|
959
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
542
960
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
543
961
|
};
|
|
544
962
|
}
|
|
545
|
-
message =
|
|
963
|
+
message = WAProto.Message.create(message);
|
|
546
964
|
const messageJSON = {
|
|
547
965
|
key: {
|
|
548
966
|
remoteJid: jid,
|
|
549
967
|
fromMe: true,
|
|
550
|
-
id:
|
|
968
|
+
id: options?.messageId || generateMessageIDV2()
|
|
551
969
|
},
|
|
552
970
|
message: message,
|
|
553
971
|
messageTimestamp: timestamp,
|
|
554
972
|
messageStubParameters: [],
|
|
555
|
-
participant:
|
|
556
|
-
status:
|
|
973
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
974
|
+
status: WAMessageStatus.PENDING
|
|
557
975
|
};
|
|
558
|
-
return
|
|
976
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
559
977
|
};
|
|
560
|
-
|
|
561
|
-
const generateWAMessage = async (jid, content, options) => {
|
|
562
|
-
var _a;
|
|
978
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
563
979
|
// ensure msg ID is with every log
|
|
564
|
-
options.logger =
|
|
565
|
-
|
|
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);
|
|
566
983
|
};
|
|
567
|
-
exports.generateWAMessage = generateWAMessage;
|
|
568
984
|
/** Get the key to access the true type of content */
|
|
569
|
-
const getContentType = (content) => {
|
|
985
|
+
export const getContentType = (content) => {
|
|
570
986
|
if (content) {
|
|
571
987
|
const keys = Object.keys(content);
|
|
572
988
|
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
573
989
|
return key;
|
|
574
990
|
}
|
|
575
991
|
};
|
|
576
|
-
exports.getContentType = getContentType;
|
|
577
992
|
/**
|
|
578
993
|
* Normalizes ephemeral, view once messages to regular message content
|
|
579
994
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
580
995
|
* @param content
|
|
581
996
|
* @returns
|
|
582
997
|
*/
|
|
583
|
-
const normalizeMessageContent = (content) => {
|
|
998
|
+
export const normalizeMessageContent = (content) => {
|
|
584
999
|
if (!content) {
|
|
585
1000
|
return undefined;
|
|
586
1001
|
}
|
|
@@ -594,21 +1009,22 @@ const normalizeMessageContent = (content) => {
|
|
|
594
1009
|
}
|
|
595
1010
|
return content;
|
|
596
1011
|
function getFutureProofMessage(message) {
|
|
597
|
-
return (
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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);
|
|
603
1021
|
}
|
|
604
1022
|
};
|
|
605
|
-
exports.normalizeMessageContent = normalizeMessageContent;
|
|
606
1023
|
/**
|
|
607
1024
|
* Extract the true message content from a message
|
|
608
1025
|
* Eg. extracts the inner message from a disappearing message/view once message
|
|
609
1026
|
*/
|
|
610
|
-
const extractMessageContent = (content) => {
|
|
611
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1027
|
+
export const extractMessageContent = (content) => {
|
|
612
1028
|
const extractFromTemplateMessage = (msg) => {
|
|
613
1029
|
if (msg.imageMessage) {
|
|
614
1030
|
return { imageMessage: msg.imageMessage };
|
|
@@ -624,35 +1040,39 @@ const extractMessageContent = (content) => {
|
|
|
624
1040
|
}
|
|
625
1041
|
else {
|
|
626
1042
|
return {
|
|
627
|
-
conversation: 'contentText' in msg
|
|
628
|
-
? msg.contentText
|
|
629
|
-
: ('hydratedContentText' in msg ? msg.hydratedContentText : '')
|
|
1043
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
630
1044
|
};
|
|
631
1045
|
}
|
|
632
1046
|
};
|
|
633
|
-
content =
|
|
634
|
-
if (content
|
|
1047
|
+
content = normalizeMessageContent(content);
|
|
1048
|
+
if (content?.buttonsMessage) {
|
|
635
1049
|
return extractFromTemplateMessage(content.buttonsMessage);
|
|
636
1050
|
}
|
|
637
|
-
if (
|
|
638
|
-
return extractFromTemplateMessage(
|
|
1051
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
1052
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
639
1053
|
}
|
|
640
|
-
if (
|
|
641
|
-
return extractFromTemplateMessage(
|
|
1054
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
1055
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
642
1056
|
}
|
|
643
|
-
if (
|
|
644
|
-
return extractFromTemplateMessage(
|
|
1057
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
1058
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
645
1059
|
}
|
|
646
1060
|
return content;
|
|
647
1061
|
};
|
|
648
|
-
exports.extractMessageContent = extractMessageContent;
|
|
649
1062
|
/**
|
|
650
1063
|
* Returns the device predicted by message ID
|
|
651
1064
|
*/
|
|
652
|
-
const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
653
|
-
|
|
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';
|
|
654
1074
|
/** Upserts a receipt in the message */
|
|
655
|
-
const updateMessageWithReceipt = (msg, receipt) => {
|
|
1075
|
+
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
656
1076
|
msg.userReceipt = msg.userReceipt || [];
|
|
657
1077
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
658
1078
|
if (recp) {
|
|
@@ -662,41 +1082,43 @@ const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
662
1082
|
msg.userReceipt.push(receipt);
|
|
663
1083
|
}
|
|
664
1084
|
};
|
|
665
|
-
exports.updateMessageWithReceipt = updateMessageWithReceipt;
|
|
666
1085
|
/** Update the message with a new reaction */
|
|
667
|
-
const updateMessageWithReaction = (msg, reaction) => {
|
|
668
|
-
const authorID =
|
|
669
|
-
const reactions = (msg.reactions || [])
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
reactions.push(reaction);
|
|
673
|
-
}
|
|
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);
|
|
674
1091
|
msg.reactions = reactions;
|
|
675
1092
|
};
|
|
676
|
-
exports.updateMessageWithReaction = updateMessageWithReaction;
|
|
677
1093
|
/** Update the message with a new poll update */
|
|
678
|
-
const updateMessageWithPollUpdate = (msg, update) => {
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
.filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
|
|
683
|
-
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) {
|
|
684
1098
|
reactions.push(update);
|
|
685
1099
|
}
|
|
686
1100
|
msg.pollUpdates = reactions;
|
|
687
1101
|
};
|
|
688
|
-
|
|
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
|
+
};
|
|
689
1109
|
/**
|
|
690
1110
|
* Aggregates all poll updates in a poll.
|
|
691
1111
|
* @param msg the poll creation message
|
|
692
1112
|
* @param meId your jid
|
|
693
1113
|
* @returns A list of options & their voters
|
|
694
1114
|
*/
|
|
695
|
-
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
696
|
-
|
|
697
|
-
|
|
1115
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
1116
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
1117
|
+
message?.pollCreationMessageV2?.options ||
|
|
1118
|
+
message?.pollCreationMessageV3?.options ||
|
|
1119
|
+
[];
|
|
698
1120
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
699
|
-
const hash =
|
|
1121
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString();
|
|
700
1122
|
acc[hash] = {
|
|
701
1123
|
name: opt.optionName || '',
|
|
702
1124
|
voters: []
|
|
@@ -718,14 +1140,36 @@ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
718
1140
|
};
|
|
719
1141
|
data = voteHashMap[hash];
|
|
720
1142
|
}
|
|
721
|
-
voteHashMap[hash].voters.push(
|
|
1143
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
|
|
722
1144
|
}
|
|
723
1145
|
}
|
|
724
1146
|
return Object.values(voteHashMap);
|
|
725
1147
|
}
|
|
726
|
-
|
|
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
|
+
}
|
|
727
1171
|
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
728
|
-
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
1172
|
+
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
729
1173
|
const keyMap = {};
|
|
730
1174
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
731
1175
|
if (!fromMe) {
|
|
@@ -742,40 +1186,34 @@ const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
742
1186
|
}
|
|
743
1187
|
return Object.values(keyMap);
|
|
744
1188
|
};
|
|
745
|
-
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
|
|
746
1189
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
747
1190
|
/**
|
|
748
1191
|
* Downloads the given message. Throws an error if it's not a media message
|
|
749
1192
|
*/
|
|
750
|
-
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
751
|
-
const result = await downloadMsg()
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
message = await ctx.reuploadRequest(message);
|
|
761
|
-
const result = await downloadMsg();
|
|
762
|
-
return result;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
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;
|
|
765
1203
|
}
|
|
766
1204
|
throw error;
|
|
767
1205
|
});
|
|
768
1206
|
return result;
|
|
769
1207
|
async function downloadMsg() {
|
|
770
|
-
const mContent =
|
|
1208
|
+
const mContent = extractMessageContent(message.message);
|
|
771
1209
|
if (!mContent) {
|
|
772
|
-
throw new
|
|
1210
|
+
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
773
1211
|
}
|
|
774
|
-
const contentType =
|
|
775
|
-
let mediaType = contentType
|
|
1212
|
+
const contentType = getContentType(mContent);
|
|
1213
|
+
let mediaType = contentType?.replace('Message', '');
|
|
776
1214
|
const media = mContent[contentType];
|
|
777
1215
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
778
|
-
throw new
|
|
1216
|
+
throw new Boom(`"${contentType}" message is not a media message`);
|
|
779
1217
|
}
|
|
780
1218
|
let download;
|
|
781
1219
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
@@ -788,7 +1226,7 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
788
1226
|
else {
|
|
789
1227
|
download = media;
|
|
790
1228
|
}
|
|
791
|
-
const stream = await
|
|
1229
|
+
const stream = await downloadContentFromMessage(download, mediaType, options);
|
|
792
1230
|
if (type === 'buffer') {
|
|
793
1231
|
const bufferArray = [];
|
|
794
1232
|
for await (const chunk of stream) {
|
|
@@ -799,18 +1237,17 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
799
1237
|
return stream;
|
|
800
1238
|
}
|
|
801
1239
|
};
|
|
802
|
-
exports.downloadMediaMessage = downloadMediaMessage;
|
|
803
1240
|
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
804
|
-
const assertMediaContent = (content) => {
|
|
805
|
-
content =
|
|
806
|
-
const mediaContent =
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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;
|
|
811
1248
|
if (!mediaContent) {
|
|
812
|
-
throw new
|
|
1249
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
813
1250
|
}
|
|
814
1251
|
return mediaContent;
|
|
815
1252
|
};
|
|
816
|
-
|
|
1253
|
+
//# sourceMappingURL=messages.js.map
|