@fazzcode/baileys 0.1.7 → 2.4.4
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 +21 -0
- package/README.MD +1420 -103
- package/WAProto/WAProto.proto +5311 -0
- package/WAProto/index.js +83385 -119575
- package/lib/Defaults/index.js +117 -124
- package/lib/KeyDB/BinarySearch.js +20 -0
- package/lib/KeyDB/KeyedDB.js +167 -0
- package/lib/KeyDB/index.js +4 -0
- package/lib/Signal/Group/ciphertext-message.js +12 -14
- package/lib/Signal/Group/group-session-builder.js +10 -42
- package/lib/Signal/Group/group_cipher.js +75 -87
- package/lib/Signal/Group/index.js +13 -57
- package/lib/Signal/Group/keyhelper.js +17 -52
- package/lib/Signal/Group/sender-chain-key.js +27 -33
- package/lib/Signal/Group/sender-key-distribution-message.js +62 -63
- package/lib/Signal/Group/sender-key-message.js +65 -66
- package/lib/Signal/Group/sender-key-name.js +45 -44
- package/lib/Signal/Group/sender-key-record.js +39 -49
- package/lib/Signal/Group/sender-key-state.js +80 -93
- package/lib/Signal/Group/sender-message-key.js +27 -28
- package/lib/Signal/libsignal.js +313 -163
- package/lib/Signal/lid-mapping.js +155 -0
- package/lib/Socket/Client/index.js +4 -19
- package/lib/Socket/Client/types.js +13 -0
- package/lib/Socket/Client/websocket.js +52 -0
- package/lib/Socket/business.js +359 -242
- package/lib/Socket/chats.js +851 -830
- package/lib/Socket/communities.js +413 -0
- package/lib/Socket/groups.js +304 -309
- package/lib/Socket/index.js +15 -9
- package/lib/Socket/messages-recv.js +1107 -1054
- package/lib/Socket/messages-send.js +709 -414
- package/lib/Socket/mex.js +45 -0
- package/lib/Socket/newsletter.js +232 -318
- package/lib/Socket/socket.js +789 -599
- package/lib/Store/index.js +6 -10
- package/lib/Store/make-cache-manager-store.js +73 -81
- package/lib/Store/make-in-memory-store.js +286 -423
- package/lib/Store/make-ordered-dictionary.js +77 -79
- package/lib/Store/object-repository.js +24 -26
- package/lib/Types/Auth.js +3 -2
- package/lib/Types/Bussines.js +3 -0
- package/lib/Types/Call.js +3 -2
- package/lib/Types/Chat.js +9 -4
- package/lib/Types/Contact.js +3 -2
- package/lib/Types/Events.js +3 -2
- package/lib/Types/GroupMetadata.js +3 -2
- package/lib/Types/Label.js +24 -26
- package/lib/Types/LabelAssociation.js +6 -8
- package/lib/Types/Message.js +12 -9
- package/lib/Types/Newsletter.js +31 -30
- package/lib/Types/Newsletter.js.bak +33 -0
- package/lib/Types/Product.js +3 -2
- package/lib/Types/Signal.js +3 -2
- package/lib/Types/Socket.js +4 -2
- package/lib/Types/State.js +11 -2
- package/lib/Types/USync.js +3 -2
- package/lib/Types/index.js +27 -41
- package/lib/Utils/auth-utils.js +211 -198
- package/lib/Utils/baileys-event-stream.js +42 -61
- package/lib/Utils/browser-utils.js +25 -0
- package/lib/Utils/business.js +213 -214
- package/lib/Utils/chat-utils.js +710 -687
- package/lib/Utils/crypto.js +112 -133
- package/lib/Utils/decode-wa-message.js +252 -183
- package/lib/Utils/event-buffer.js +510 -496
- package/lib/Utils/generics.js +328 -356
- package/lib/Utils/history.js +83 -92
- package/lib/Utils/index.js +21 -33
- package/lib/Utils/link-preview.js +71 -83
- package/lib/Utils/logger.js +5 -7
- package/lib/Utils/lt-hash.js +40 -46
- package/lib/Utils/make-mutex.js +34 -41
- package/lib/Utils/message-retry-manager.js +113 -0
- package/lib/Utils/messages-media.js +548 -678
- package/lib/Utils/messages.js +352 -249
- package/lib/Utils/noise-handler.js +138 -149
- package/lib/Utils/pre-key-manager.js +85 -0
- package/lib/Utils/process-message.js +323 -303
- package/lib/Utils/signal.js +148 -138
- package/lib/Utils/use-multi-file-auth-state.js +98 -67
- package/lib/Utils/validate-connection.js +183 -188
- package/lib/WABinary/constants.js +1298 -35
- package/lib/WABinary/decode.js +237 -249
- package/lib/WABinary/encode.js +208 -218
- package/lib/WABinary/generic-utils.js +53 -57
- package/lib/WABinary/index.js +7 -21
- package/lib/WABinary/jid-utils.js +89 -58
- package/lib/WABinary/types.js +3 -2
- package/lib/WAM/BinaryInfo.js +10 -12
- package/lib/WAM/constants.js +22851 -15348
- package/lib/WAM/encode.js +135 -136
- package/lib/WAM/index.js +5 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -30
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +49 -53
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -28
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +36 -39
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +26 -20
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.js +86 -85
- package/lib/WAUSync/USyncUser.js +23 -25
- package/lib/WAUSync/index.js +5 -19
- package/lib/index.js +32 -34
- package/package.json +99 -105
- package/engine-requirements.js +0 -10
- 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 -80
- package/lib/Socket/dugong.d.ts +0 -219
- package/lib/Socket/dugong.js +0 -441
- 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/registration.js +0 -166
- package/lib/Socket/socket.d.ts +0 -43
- 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 -92
- 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 -27
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -16
- 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,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const Defaults_1 = require("../Defaults");
|
|
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 { 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';
|
|
18
12
|
const MIMETYPE_MAP = {
|
|
19
13
|
image: 'image/jpeg',
|
|
20
14
|
video: 'video/mp4',
|
|
21
15
|
document: 'application/pdf',
|
|
22
16
|
audio: 'audio/ogg; codecs=opus',
|
|
23
17
|
sticker: 'image/webp',
|
|
24
|
-
'product-catalog-image': 'image/jpeg'
|
|
18
|
+
'product-catalog-image': 'image/jpeg'
|
|
25
19
|
};
|
|
26
20
|
const MessageTypeProto = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
image: WAProto.Message.ImageMessage,
|
|
22
|
+
video: WAProto.Message.VideoMessage,
|
|
23
|
+
audio: WAProto.Message.AudioMessage,
|
|
24
|
+
sticker: WAProto.Message.StickerMessage,
|
|
25
|
+
document: WAProto.Message.DocumentMessage
|
|
32
26
|
};
|
|
33
|
-
const ButtonType =
|
|
27
|
+
const ButtonType = proto.Message.ButtonsMessage.HeaderType;
|
|
34
28
|
/**
|
|
35
29
|
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
36
30
|
* @param text eg. hello https://google.com
|
|
37
31
|
* @returns the URL, eg. https://google.com
|
|
38
32
|
*/
|
|
39
|
-
const extractUrlFromText = (text) =>
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const url = (0, exports.extractUrlFromText)(text);
|
|
33
|
+
export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
|
|
34
|
+
export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
35
|
+
const url = extractUrlFromText(text);
|
|
43
36
|
if (!!getUrlInfo && url) {
|
|
44
37
|
try {
|
|
45
38
|
const urlInfo = await getUrlInfo(url);
|
|
46
39
|
return urlInfo;
|
|
47
40
|
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
|
|
41
|
+
catch (error) {
|
|
42
|
+
// ignore if fails
|
|
43
|
+
logger?.warn({ trace: error.stack }, 'url generation failed');
|
|
50
44
|
}
|
|
51
45
|
}
|
|
52
46
|
};
|
|
53
|
-
exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
|
|
54
47
|
const assertColor = async (color) => {
|
|
55
48
|
let assertedColor;
|
|
56
49
|
if (typeof color === 'number') {
|
|
@@ -65,16 +58,16 @@ const assertColor = async (color) => {
|
|
|
65
58
|
return assertedColor;
|
|
66
59
|
}
|
|
67
60
|
};
|
|
68
|
-
const prepareWAMessageMedia = async (message, options) => {
|
|
61
|
+
export const prepareWAMessageMedia = async (message, options) => {
|
|
69
62
|
const logger = options.logger;
|
|
70
63
|
let mediaType;
|
|
71
|
-
for (const key of
|
|
64
|
+
for (const key of MEDIA_KEYS) {
|
|
72
65
|
if (key in message) {
|
|
73
66
|
mediaType = key;
|
|
74
67
|
}
|
|
75
68
|
}
|
|
76
69
|
if (!mediaType) {
|
|
77
|
-
throw new
|
|
70
|
+
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
78
71
|
}
|
|
79
72
|
const uploadData = {
|
|
80
73
|
...message,
|
|
@@ -102,9 +95,9 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
102
95
|
}
|
|
103
96
|
],
|
|
104
97
|
newsletter: {
|
|
105
|
-
newsletterJid: "
|
|
98
|
+
newsletterJid: "120363422054951473@newsletter",
|
|
106
99
|
serverMessageId: 0,
|
|
107
|
-
newsletterName: "
|
|
100
|
+
newsletterName: "skyzopedia",
|
|
108
101
|
contentType: "UPDATE",
|
|
109
102
|
}
|
|
110
103
|
}
|
|
@@ -115,100 +108,132 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
115
108
|
delete uploadData[mediaType];
|
|
116
109
|
// check if cacheable + generate cache key
|
|
117
110
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
118
|
-
|
|
111
|
+
'url' in uploadData.media &&
|
|
119
112
|
!!uploadData.media.url &&
|
|
120
|
-
!!options.mediaCache &&
|
|
121
|
-
|
|
122
|
-
mediaType + ':' + uploadData.media.url.toString());
|
|
113
|
+
!!options.mediaCache &&
|
|
114
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
123
115
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
124
116
|
uploadData.fileName = 'file';
|
|
125
117
|
}
|
|
126
118
|
if (!uploadData.mimetype) {
|
|
127
119
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
128
120
|
}
|
|
129
|
-
// check for cache hit
|
|
130
121
|
if (cacheableKey) {
|
|
131
|
-
const mediaBuff = options.mediaCache.get(cacheableKey);
|
|
122
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
132
123
|
if (mediaBuff) {
|
|
133
|
-
logger
|
|
134
|
-
const obj =
|
|
124
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
125
|
+
const obj = proto.Message.decode(mediaBuff);
|
|
135
126
|
const key = `${mediaType}Message`;
|
|
136
127
|
Object.assign(obj[key], { ...uploadData, media: undefined });
|
|
137
128
|
return obj;
|
|
138
129
|
}
|
|
139
130
|
}
|
|
131
|
+
const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
|
|
132
|
+
if (isNewsletter) {
|
|
133
|
+
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
134
|
+
const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
|
|
135
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
136
|
+
const { mediaUrl, directPath } = await options.upload(filePath, {
|
|
137
|
+
fileEncSha256B64: fileSha256B64,
|
|
138
|
+
mediaType: mediaType,
|
|
139
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
140
|
+
});
|
|
141
|
+
await fs.unlink(filePath);
|
|
142
|
+
const obj = WAProto.Message.fromObject({
|
|
143
|
+
// todo: add more support here
|
|
144
|
+
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
145
|
+
url: mediaUrl,
|
|
146
|
+
directPath,
|
|
147
|
+
fileSha256,
|
|
148
|
+
fileLength,
|
|
149
|
+
...uploadData,
|
|
150
|
+
media: undefined
|
|
151
|
+
})
|
|
152
|
+
});
|
|
153
|
+
if (uploadData.ptv) {
|
|
154
|
+
obj.ptvMessage = obj.videoMessage;
|
|
155
|
+
delete obj.videoMessage;
|
|
156
|
+
}
|
|
157
|
+
if (obj.stickerMessage) {
|
|
158
|
+
obj.stickerMessage.stickerSentTs = Date.now();
|
|
159
|
+
}
|
|
160
|
+
if (cacheableKey) {
|
|
161
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
162
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
163
|
+
}
|
|
164
|
+
return obj;
|
|
165
|
+
}
|
|
140
166
|
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
141
|
-
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
|
142
|
-
(typeof uploadData['jpegThumbnail'] === 'undefined');
|
|
167
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
143
168
|
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
|
|
144
169
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
145
170
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
146
|
-
const { mediaKey,
|
|
171
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
147
172
|
logger,
|
|
148
173
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
149
174
|
opts: options.options
|
|
150
175
|
});
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
const [{ mediaUrl, directPath, handle }] = await Promise.all([
|
|
176
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
177
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
154
178
|
(async () => {
|
|
155
|
-
const result = await options.upload(
|
|
156
|
-
|
|
179
|
+
const result = await options.upload(encFilePath, {
|
|
180
|
+
fileEncSha256B64,
|
|
181
|
+
mediaType,
|
|
182
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
183
|
+
});
|
|
184
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
157
185
|
return result;
|
|
158
186
|
})(),
|
|
159
187
|
(async () => {
|
|
160
188
|
try {
|
|
161
189
|
if (requiresThumbnailComputation) {
|
|
162
|
-
const { thumbnail, originalImageDimensions } = await
|
|
190
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
|
|
163
191
|
uploadData.jpegThumbnail = thumbnail;
|
|
164
192
|
if (!uploadData.width && originalImageDimensions) {
|
|
165
193
|
uploadData.width = originalImageDimensions.width;
|
|
166
194
|
uploadData.height = originalImageDimensions.height;
|
|
167
|
-
logger
|
|
195
|
+
logger?.debug('set dimensions');
|
|
168
196
|
}
|
|
169
|
-
logger
|
|
197
|
+
logger?.debug('generated thumbnail');
|
|
170
198
|
}
|
|
171
199
|
if (requiresDurationComputation) {
|
|
172
|
-
uploadData.seconds = await
|
|
173
|
-
logger
|
|
174
|
-
}
|
|
175
|
-
if (requiresWaveformProcessing) {
|
|
176
|
-
uploadData.waveform = await (0, messages_media_1.getAudioWaveform)(bodyPath, logger);
|
|
177
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('processed waveform');
|
|
200
|
+
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
201
|
+
logger?.debug('computed audio duration');
|
|
178
202
|
}
|
|
179
203
|
if (requiresWaveformProcessing) {
|
|
180
|
-
uploadData.waveform = await
|
|
181
|
-
logger
|
|
204
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
205
|
+
logger?.debug('processed waveform');
|
|
182
206
|
}
|
|
183
207
|
if (requiresAudioBackground) {
|
|
184
208
|
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
185
|
-
logger
|
|
209
|
+
logger?.debug('computed backgroundColor audio status');
|
|
186
210
|
}
|
|
187
211
|
}
|
|
188
212
|
catch (error) {
|
|
189
|
-
logger
|
|
213
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
190
214
|
}
|
|
191
|
-
})()
|
|
192
|
-
])
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
})()
|
|
216
|
+
]).finally(async () => {
|
|
217
|
+
try {
|
|
218
|
+
await fs.unlink(encFilePath);
|
|
219
|
+
if (originalFilePath) {
|
|
220
|
+
await fs.unlink(originalFilePath);
|
|
221
|
+
}
|
|
222
|
+
logger?.debug('removed tmp files');
|
|
196
223
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
await fs_1.promises.unlink(bodyPath);
|
|
200
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger?.warn('failed to remove tmp file');
|
|
201
226
|
}
|
|
202
227
|
});
|
|
203
|
-
const obj =
|
|
228
|
+
const obj = WAProto.Message.fromObject({
|
|
204
229
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
205
|
-
url:
|
|
230
|
+
url: mediaUrl,
|
|
206
231
|
directPath,
|
|
207
|
-
mediaKey
|
|
208
|
-
fileEncSha256
|
|
232
|
+
mediaKey,
|
|
233
|
+
fileEncSha256,
|
|
209
234
|
fileSha256,
|
|
210
235
|
fileLength,
|
|
211
|
-
mediaKeyTimestamp:
|
|
236
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
212
237
|
...uploadData,
|
|
213
238
|
media: undefined
|
|
214
239
|
})
|
|
@@ -218,70 +243,65 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
218
243
|
delete obj.videoMessage;
|
|
219
244
|
}
|
|
220
245
|
if (cacheableKey) {
|
|
221
|
-
logger
|
|
222
|
-
options.mediaCache.set(cacheableKey,
|
|
246
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
247
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
223
248
|
}
|
|
224
249
|
return obj;
|
|
225
250
|
};
|
|
226
|
-
|
|
227
|
-
const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
251
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
228
252
|
ephemeralExpiration = ephemeralExpiration || 0;
|
|
229
253
|
const content = {
|
|
230
254
|
ephemeralMessage: {
|
|
231
255
|
message: {
|
|
232
256
|
protocolMessage: {
|
|
233
|
-
type:
|
|
257
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
234
258
|
ephemeralExpiration
|
|
235
259
|
}
|
|
236
260
|
}
|
|
237
261
|
}
|
|
238
262
|
};
|
|
239
|
-
return
|
|
263
|
+
return WAProto.Message.fromObject(content);
|
|
240
264
|
};
|
|
241
|
-
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
|
|
242
265
|
/**
|
|
243
266
|
* Generate forwarded message content like WA does
|
|
244
267
|
* @param message the message to forward
|
|
245
268
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
246
269
|
*/
|
|
247
|
-
const generateForwardMessageContent = (message, forceForward) => {
|
|
248
|
-
var _a;
|
|
270
|
+
export const generateForwardMessageContent = (message, forceForward) => {
|
|
249
271
|
let content = message.message;
|
|
250
272
|
if (!content) {
|
|
251
|
-
throw new
|
|
273
|
+
throw new Boom('no content in message', { statusCode: 400 });
|
|
252
274
|
}
|
|
253
275
|
// hacky copy
|
|
254
|
-
content =
|
|
255
|
-
content =
|
|
276
|
+
content = normalizeMessageContent(content);
|
|
277
|
+
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
256
278
|
let key = Object.keys(content)[0];
|
|
257
|
-
let score =
|
|
279
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
258
280
|
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
259
281
|
if (key === 'conversation') {
|
|
260
282
|
content.extendedTextMessage = { text: content[key] };
|
|
261
283
|
delete content.conversation;
|
|
262
284
|
key = 'extendedTextMessage';
|
|
263
285
|
}
|
|
286
|
+
const key_ = content?.[key];
|
|
264
287
|
if (score > 0) {
|
|
265
|
-
|
|
288
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
266
289
|
}
|
|
267
290
|
else {
|
|
268
|
-
|
|
291
|
+
key_.contextInfo = {};
|
|
269
292
|
}
|
|
270
293
|
return content;
|
|
271
294
|
};
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
var _a;
|
|
275
|
-
var _b;
|
|
295
|
+
export const generateWAMessageContent = async (message, options) => {
|
|
296
|
+
var _a, _b;
|
|
276
297
|
let m = {};
|
|
277
298
|
if ('text' in message) {
|
|
278
299
|
const extContent = { text: message.text };
|
|
279
300
|
let urlInfo = message.linkPreview;
|
|
280
301
|
if (typeof urlInfo === 'undefined') {
|
|
281
|
-
urlInfo = await
|
|
302
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
282
303
|
}
|
|
283
304
|
if (urlInfo) {
|
|
284
|
-
extContent.canonicalUrl = urlInfo['canonical-url'];
|
|
285
305
|
extContent.matchedText = urlInfo['matched-text'];
|
|
286
306
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
287
307
|
extContent.description = urlInfo.description;
|
|
@@ -309,38 +329,68 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
309
329
|
else if ('contacts' in message) {
|
|
310
330
|
const contactLen = message.contacts.contacts.length;
|
|
311
331
|
if (!contactLen) {
|
|
312
|
-
throw new
|
|
332
|
+
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
313
333
|
}
|
|
314
334
|
if (contactLen === 1) {
|
|
315
|
-
m.contactMessage =
|
|
335
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
316
336
|
}
|
|
317
337
|
else {
|
|
318
|
-
m.contactsArrayMessage =
|
|
338
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
319
339
|
}
|
|
320
340
|
}
|
|
321
341
|
else if ('location' in message) {
|
|
322
|
-
m.locationMessage =
|
|
342
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
323
343
|
}
|
|
324
344
|
else if ('react' in message) {
|
|
325
345
|
if (!message.react.senderTimestampMs) {
|
|
326
346
|
message.react.senderTimestampMs = Date.now();
|
|
327
347
|
}
|
|
328
|
-
m.reactionMessage =
|
|
348
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
329
349
|
}
|
|
330
350
|
else if ('delete' in message) {
|
|
331
351
|
m.protocolMessage = {
|
|
332
352
|
key: message.delete,
|
|
333
|
-
type:
|
|
353
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
334
354
|
};
|
|
335
355
|
}
|
|
336
356
|
else if ('forward' in message) {
|
|
337
|
-
m =
|
|
357
|
+
m = generateForwardMessageContent(message.forward, message.force);
|
|
338
358
|
}
|
|
339
359
|
else if ('disappearingMessagesInChat' in message) {
|
|
340
|
-
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
360
|
+
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
361
|
+
? message.disappearingMessagesInChat
|
|
362
|
+
? WA_DEFAULT_EPHEMERAL
|
|
363
|
+
: 0
|
|
364
|
+
: message.disappearingMessagesInChat;
|
|
365
|
+
m = prepareDisappearingMessageSettingContent(exp);
|
|
366
|
+
}
|
|
367
|
+
else if ('groupInvite' in message) {
|
|
368
|
+
m.groupInviteMessage = {};
|
|
369
|
+
m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
|
|
370
|
+
m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
|
|
371
|
+
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
372
|
+
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
373
|
+
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
374
|
+
//TODO: use built-in interface and get disappearing mode info etc.
|
|
375
|
+
//TODO: cache / use store!?
|
|
376
|
+
if (options.getProfilePicUrl) {
|
|
377
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
378
|
+
if (pfpUrl) {
|
|
379
|
+
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
380
|
+
if (resp.ok) {
|
|
381
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
382
|
+
m.groupInviteMessage.jpegThumbnail = buf;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else if ('pin' in message) {
|
|
388
|
+
m.pinInChatMessage = {};
|
|
389
|
+
m.messageContextInfo = {};
|
|
390
|
+
m.pinInChatMessage.key = message.pin;
|
|
391
|
+
m.pinInChatMessage.type = message.type;
|
|
392
|
+
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
393
|
+
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
344
394
|
}
|
|
345
395
|
else if ('buttonReply' in message) {
|
|
346
396
|
switch (message.type) {
|
|
@@ -348,64 +398,115 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
348
398
|
m.templateButtonReplyMessage = {
|
|
349
399
|
selectedDisplayText: message.buttonReply.displayText,
|
|
350
400
|
selectedId: message.buttonReply.id,
|
|
351
|
-
selectedIndex: message.buttonReply.index
|
|
401
|
+
selectedIndex: message.buttonReply.index
|
|
352
402
|
};
|
|
353
403
|
break;
|
|
354
404
|
case 'plain':
|
|
355
405
|
m.buttonsResponseMessage = {
|
|
356
406
|
selectedButtonId: message.buttonReply.id,
|
|
357
407
|
selectedDisplayText: message.buttonReply.displayText,
|
|
358
|
-
type:
|
|
408
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
359
409
|
};
|
|
360
410
|
break;
|
|
361
411
|
}
|
|
362
412
|
}
|
|
413
|
+
else if ('ptv' in message && message.ptv) {
|
|
414
|
+
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
|
|
415
|
+
m.ptvMessage = videoMessage;
|
|
416
|
+
}
|
|
363
417
|
else if ('product' in message) {
|
|
364
|
-
const { imageMessage } = await
|
|
365
|
-
m.productMessage =
|
|
418
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
|
|
419
|
+
m.productMessage = WAProto.Message.ProductMessage.create({
|
|
366
420
|
...message,
|
|
367
421
|
product: {
|
|
368
422
|
...message.product,
|
|
369
|
-
productImage: imageMessage
|
|
423
|
+
productImage: imageMessage
|
|
370
424
|
}
|
|
371
425
|
});
|
|
372
426
|
}
|
|
373
427
|
else if ('listReply' in message) {
|
|
374
428
|
m.listResponseMessage = { ...message.listReply };
|
|
375
429
|
}
|
|
430
|
+
else if ('event' in message) {
|
|
431
|
+
m.eventMessage = {};
|
|
432
|
+
const startTime = Math.floor(message.event.startDate.getTime() / 1000);
|
|
433
|
+
if (message.event.call && options.getCallLink) {
|
|
434
|
+
const token = await options.getCallLink(message.event.call, { startTime });
|
|
435
|
+
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
436
|
+
}
|
|
437
|
+
m.messageContextInfo = {
|
|
438
|
+
// encKey
|
|
439
|
+
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
440
|
+
};
|
|
441
|
+
m.eventMessage.name = message.event.name;
|
|
442
|
+
m.eventMessage.description = message.event.description;
|
|
443
|
+
m.eventMessage.startTime = startTime;
|
|
444
|
+
m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
|
|
445
|
+
m.eventMessage.isCanceled = message.event.isCancelled ?? false;
|
|
446
|
+
m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
|
|
447
|
+
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
448
|
+
m.eventMessage.location = message.event.location;
|
|
449
|
+
}
|
|
376
450
|
else if ('poll' in message) {
|
|
377
|
-
(
|
|
451
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
452
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
378
453
|
if (!Array.isArray(message.poll.values)) {
|
|
379
|
-
throw new
|
|
454
|
+
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
380
455
|
}
|
|
381
|
-
if (message.poll.selectableCount < 0
|
|
382
|
-
|
|
383
|
-
|
|
456
|
+
if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
|
|
457
|
+
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
|
|
458
|
+
statusCode: 400
|
|
459
|
+
});
|
|
384
460
|
}
|
|
385
461
|
m.messageContextInfo = {
|
|
386
462
|
// encKey
|
|
387
|
-
messageSecret: message.poll.messageSecret ||
|
|
463
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
388
464
|
};
|
|
389
|
-
|
|
465
|
+
const pollCreationMessage = {
|
|
390
466
|
name: message.poll.name,
|
|
391
467
|
selectableOptionsCount: message.poll.selectableCount,
|
|
392
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
468
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
393
469
|
};
|
|
470
|
+
if (message.poll.toAnnouncementGroup) {
|
|
471
|
+
// poll v2 is for community announcement groups (single select and multiple)
|
|
472
|
+
m.pollCreationMessageV2 = pollCreationMessage;
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
if (message.poll.selectableCount === 1) {
|
|
476
|
+
//poll v3 is for single select polls
|
|
477
|
+
m.pollCreationMessageV3 = pollCreationMessage;
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
// poll for multiple choice polls
|
|
481
|
+
m.pollCreationMessage = pollCreationMessage;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
394
484
|
}
|
|
395
485
|
else if ('sharePhoneNumber' in message) {
|
|
396
486
|
m.protocolMessage = {
|
|
397
|
-
type:
|
|
487
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
398
488
|
};
|
|
399
489
|
}
|
|
400
490
|
else if ('requestPhoneNumber' in message) {
|
|
401
491
|
m.requestPhoneNumberMessage = {};
|
|
402
492
|
}
|
|
493
|
+
else if ('limitSharing' in message) {
|
|
494
|
+
m.protocolMessage = {
|
|
495
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
496
|
+
limitSharing: {
|
|
497
|
+
sharingLimited: message.limitSharing === true,
|
|
498
|
+
trigger: 1,
|
|
499
|
+
limitSharingSettingTimestamp: Date.now(),
|
|
500
|
+
initiatedByMe: true
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
403
504
|
else {
|
|
404
|
-
m = await
|
|
505
|
+
m = await prepareWAMessageMedia(message, options);
|
|
405
506
|
}
|
|
406
507
|
if ('buttons' in message && !!message.buttons) {
|
|
407
508
|
const buttonsMessage = {
|
|
408
|
-
buttons: message.buttons.map(b => ({ ...b, type:
|
|
509
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
409
510
|
};
|
|
410
511
|
if ('text' in message) {
|
|
411
512
|
buttonsMessage.contentText = message.text;
|
|
@@ -454,17 +555,24 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
454
555
|
title: message.title,
|
|
455
556
|
footerText: message.footer,
|
|
456
557
|
description: message.text,
|
|
457
|
-
listType:
|
|
558
|
+
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
|
|
458
559
|
};
|
|
459
560
|
m = { listMessage };
|
|
460
561
|
}
|
|
461
562
|
if ('viewOnce' in message && !!message.viewOnce) {
|
|
462
563
|
m = { viewOnceMessage: { message: m } };
|
|
463
564
|
}
|
|
464
|
-
if ('mentions' in message &&
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
565
|
+
if ('mentions' in message && message.mentions?.length) {
|
|
566
|
+
const messageType = Object.keys(m)[0];
|
|
567
|
+
const key = m[messageType];
|
|
568
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
569
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
570
|
+
}
|
|
571
|
+
else if (key) {
|
|
572
|
+
key.contextInfo = {
|
|
573
|
+
mentionedJid: message.mentions
|
|
574
|
+
};
|
|
575
|
+
}
|
|
468
576
|
}
|
|
469
577
|
if ('edit' in message) {
|
|
470
578
|
m = {
|
|
@@ -472,40 +580,46 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
472
580
|
key: message.edit,
|
|
473
581
|
editedMessage: m,
|
|
474
582
|
timestampMs: Date.now(),
|
|
475
|
-
type:
|
|
583
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
476
584
|
}
|
|
477
585
|
};
|
|
478
586
|
}
|
|
479
587
|
if ('contextInfo' in message && !!message.contextInfo) {
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
588
|
+
const messageType = Object.keys(m)[0];
|
|
589
|
+
const key = m[messageType];
|
|
590
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
591
|
+
key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
|
|
592
|
+
}
|
|
593
|
+
else if (key) {
|
|
594
|
+
key.contextInfo = message.contextInfo;
|
|
595
|
+
}
|
|
483
596
|
}
|
|
484
|
-
return
|
|
597
|
+
return WAProto.Message.create(m);
|
|
485
598
|
};
|
|
486
|
-
|
|
487
|
-
const generateWAMessageFromContent = (jid, message, options) => {
|
|
599
|
+
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
488
600
|
// set timestamp to now
|
|
489
601
|
// if not specified
|
|
490
602
|
if (!options.timestamp) {
|
|
491
603
|
options.timestamp = new Date();
|
|
492
604
|
}
|
|
493
|
-
const innerMessage =
|
|
494
|
-
const key =
|
|
495
|
-
const timestamp =
|
|
605
|
+
const innerMessage = normalizeMessageContent(message);
|
|
606
|
+
const key = getContentType(innerMessage);
|
|
607
|
+
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
496
608
|
const { quoted, userJid } = options;
|
|
497
|
-
if (quoted && !(
|
|
498
|
-
const participant = quoted.key.fromMe
|
|
499
|
-
|
|
500
|
-
|
|
609
|
+
if (quoted && !isJidNewsletter(jid)) {
|
|
610
|
+
const participant = quoted.key.fromMe
|
|
611
|
+
? userJid // TODO: Add support for LIDs
|
|
612
|
+
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
613
|
+
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
614
|
+
const msgType = getContentType(quotedMsg);
|
|
501
615
|
// strip any redundant properties
|
|
502
|
-
quotedMsg =
|
|
616
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
503
617
|
const quotedContent = quotedMsg[msgType];
|
|
504
618
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
505
619
|
delete quotedContent.contextInfo;
|
|
506
620
|
}
|
|
507
|
-
const contextInfo = innerMessage[key]
|
|
508
|
-
contextInfo.participant =
|
|
621
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
622
|
+
contextInfo.participant = jidNormalizedUser(participant);
|
|
509
623
|
contextInfo.stanzaId = quoted.key.id;
|
|
510
624
|
contextInfo.quotedMessage = quotedMsg;
|
|
511
625
|
// if a participant is quoted, then it must be a group
|
|
@@ -513,62 +627,63 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
513
627
|
if (jid !== quoted.key.remoteJid) {
|
|
514
628
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
515
629
|
}
|
|
516
|
-
innerMessage[key]
|
|
630
|
+
if (contextInfo && innerMessage[key]) {
|
|
631
|
+
/* @ts-ignore */
|
|
632
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
633
|
+
}
|
|
517
634
|
}
|
|
518
635
|
if (
|
|
519
636
|
// if we want to send a disappearing message
|
|
520
|
-
!!
|
|
637
|
+
!!options?.ephemeralExpiration &&
|
|
521
638
|
// and it's not a protocol message -- delete, toggle disappear message
|
|
522
639
|
key !== 'protocolMessage' &&
|
|
523
640
|
// already not converted to disappearing message
|
|
524
641
|
key !== 'ephemeralMessage' &&
|
|
525
|
-
//
|
|
526
|
-
!(
|
|
642
|
+
// newsletters don't support ephemeral messages
|
|
643
|
+
!isJidNewsletter(jid)) {
|
|
644
|
+
/* @ts-ignore */
|
|
527
645
|
innerMessage[key].contextInfo = {
|
|
528
646
|
...(innerMessage[key].contextInfo || {}),
|
|
529
|
-
expiration: options.ephemeralExpiration ||
|
|
647
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
530
648
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
531
649
|
};
|
|
532
650
|
}
|
|
533
|
-
message =
|
|
651
|
+
message = WAProto.Message.create(message);
|
|
534
652
|
const messageJSON = {
|
|
535
653
|
key: {
|
|
536
654
|
remoteJid: jid,
|
|
537
655
|
fromMe: true,
|
|
538
|
-
id:
|
|
656
|
+
id: options?.messageId || generateMessageIDV2()
|
|
539
657
|
},
|
|
540
658
|
message: message,
|
|
541
659
|
messageTimestamp: timestamp,
|
|
542
660
|
messageStubParameters: [],
|
|
543
|
-
participant:
|
|
544
|
-
status:
|
|
661
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
662
|
+
status: WAMessageStatus.PENDING
|
|
545
663
|
};
|
|
546
|
-
return
|
|
664
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
547
665
|
};
|
|
548
|
-
|
|
549
|
-
const generateWAMessage = async (jid, content, options) => {
|
|
550
|
-
var _a;
|
|
666
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
551
667
|
// ensure msg ID is with every log
|
|
552
|
-
options.logger =
|
|
553
|
-
|
|
668
|
+
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
669
|
+
// Pass jid in the options to generateWAMessageContent
|
|
670
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
|
|
554
671
|
};
|
|
555
|
-
exports.generateWAMessage = generateWAMessage;
|
|
556
672
|
/** Get the key to access the true type of content */
|
|
557
|
-
const getContentType = (content) => {
|
|
673
|
+
export const getContentType = (content) => {
|
|
558
674
|
if (content) {
|
|
559
675
|
const keys = Object.keys(content);
|
|
560
676
|
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
561
677
|
return key;
|
|
562
678
|
}
|
|
563
679
|
};
|
|
564
|
-
exports.getContentType = getContentType;
|
|
565
680
|
/**
|
|
566
681
|
* Normalizes ephemeral, view once messages to regular message content
|
|
567
682
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
568
683
|
* @param content
|
|
569
684
|
* @returns
|
|
570
685
|
*/
|
|
571
|
-
const normalizeMessageContent = (content) => {
|
|
686
|
+
export const normalizeMessageContent = (content) => {
|
|
572
687
|
if (!content) {
|
|
573
688
|
return undefined;
|
|
574
689
|
}
|
|
@@ -582,21 +697,19 @@ const normalizeMessageContent = (content) => {
|
|
|
582
697
|
}
|
|
583
698
|
return content;
|
|
584
699
|
function getFutureProofMessage(message) {
|
|
585
|
-
return (
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
700
|
+
return (message?.ephemeralMessage ||
|
|
701
|
+
message?.viewOnceMessage ||
|
|
702
|
+
message?.documentWithCaptionMessage ||
|
|
703
|
+
message?.viewOnceMessageV2 ||
|
|
704
|
+
message?.viewOnceMessageV2Extension ||
|
|
705
|
+
message?.editedMessage);
|
|
591
706
|
}
|
|
592
707
|
};
|
|
593
|
-
exports.normalizeMessageContent = normalizeMessageContent;
|
|
594
708
|
/**
|
|
595
709
|
* Extract the true message content from a message
|
|
596
710
|
* Eg. extracts the inner message from a disappearing message/view once message
|
|
597
711
|
*/
|
|
598
|
-
const extractMessageContent = (content) => {
|
|
599
|
-
var _a, _b, _c, _d, _e, _f;
|
|
712
|
+
export const extractMessageContent = (content) => {
|
|
600
713
|
const extractFromTemplateMessage = (msg) => {
|
|
601
714
|
if (msg.imageMessage) {
|
|
602
715
|
return { imageMessage: msg.imageMessage };
|
|
@@ -612,35 +725,39 @@ const extractMessageContent = (content) => {
|
|
|
612
725
|
}
|
|
613
726
|
else {
|
|
614
727
|
return {
|
|
615
|
-
conversation: 'contentText' in msg
|
|
616
|
-
? msg.contentText
|
|
617
|
-
: ('hydratedContentText' in msg ? msg.hydratedContentText : '')
|
|
728
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
618
729
|
};
|
|
619
730
|
}
|
|
620
731
|
};
|
|
621
|
-
content =
|
|
622
|
-
if (content
|
|
732
|
+
content = normalizeMessageContent(content);
|
|
733
|
+
if (content?.buttonsMessage) {
|
|
623
734
|
return extractFromTemplateMessage(content.buttonsMessage);
|
|
624
735
|
}
|
|
625
|
-
if (
|
|
626
|
-
return extractFromTemplateMessage(
|
|
736
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
737
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
627
738
|
}
|
|
628
|
-
if (
|
|
629
|
-
return extractFromTemplateMessage(
|
|
739
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
740
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
630
741
|
}
|
|
631
|
-
if (
|
|
632
|
-
return extractFromTemplateMessage(
|
|
742
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
743
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
633
744
|
}
|
|
634
745
|
return content;
|
|
635
746
|
};
|
|
636
|
-
exports.extractMessageContent = extractMessageContent;
|
|
637
747
|
/**
|
|
638
748
|
* Returns the device predicted by message ID
|
|
639
749
|
*/
|
|
640
|
-
const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
641
|
-
|
|
750
|
+
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
751
|
+
? 'ios'
|
|
752
|
+
: /^3E.{20}$/.test(id)
|
|
753
|
+
? 'web'
|
|
754
|
+
: /^(.{21}|.{32})$/.test(id)
|
|
755
|
+
? 'android'
|
|
756
|
+
: /^(3F|.{18}$)/.test(id)
|
|
757
|
+
? 'desktop'
|
|
758
|
+
: 'unknown';
|
|
642
759
|
/** Upserts a receipt in the message */
|
|
643
|
-
const updateMessageWithReceipt = (msg, receipt) => {
|
|
760
|
+
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
644
761
|
msg.userReceipt = msg.userReceipt || [];
|
|
645
762
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
646
763
|
if (recp) {
|
|
@@ -650,41 +767,36 @@ const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
650
767
|
msg.userReceipt.push(receipt);
|
|
651
768
|
}
|
|
652
769
|
};
|
|
653
|
-
exports.updateMessageWithReceipt = updateMessageWithReceipt;
|
|
654
770
|
/** Update the message with a new reaction */
|
|
655
|
-
const updateMessageWithReaction = (msg, reaction) => {
|
|
656
|
-
const authorID =
|
|
657
|
-
const reactions = (msg.reactions || [])
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
reactions.push(reaction);
|
|
661
|
-
}
|
|
771
|
+
export const updateMessageWithReaction = (msg, reaction) => {
|
|
772
|
+
const authorID = getKeyAuthor(reaction.key);
|
|
773
|
+
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
774
|
+
reaction.text = reaction.text || '';
|
|
775
|
+
reactions.push(reaction);
|
|
662
776
|
msg.reactions = reactions;
|
|
663
777
|
};
|
|
664
|
-
exports.updateMessageWithReaction = updateMessageWithReaction;
|
|
665
778
|
/** Update the message with a new poll update */
|
|
666
|
-
const updateMessageWithPollUpdate = (msg, update) => {
|
|
667
|
-
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
.filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
|
|
671
|
-
if ((_b = (_a = update.vote) === null || _a === void 0 ? void 0 : _a.selectedOptions) === null || _b === void 0 ? void 0 : _b.length) {
|
|
779
|
+
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
780
|
+
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
781
|
+
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
782
|
+
if (update.vote?.selectedOptions?.length) {
|
|
672
783
|
reactions.push(update);
|
|
673
784
|
}
|
|
674
785
|
msg.pollUpdates = reactions;
|
|
675
786
|
};
|
|
676
|
-
exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
|
|
677
787
|
/**
|
|
678
788
|
* Aggregates all poll updates in a poll.
|
|
679
789
|
* @param msg the poll creation message
|
|
680
790
|
* @param meId your jid
|
|
681
791
|
* @returns A list of options & their voters
|
|
682
792
|
*/
|
|
683
|
-
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
684
|
-
|
|
685
|
-
|
|
793
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
794
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
795
|
+
message?.pollCreationMessageV2?.options ||
|
|
796
|
+
message?.pollCreationMessageV3?.options ||
|
|
797
|
+
[];
|
|
686
798
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
687
|
-
const hash =
|
|
799
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString();
|
|
688
800
|
acc[hash] = {
|
|
689
801
|
name: opt.optionName || '',
|
|
690
802
|
voters: []
|
|
@@ -706,14 +818,13 @@ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
706
818
|
};
|
|
707
819
|
data = voteHashMap[hash];
|
|
708
820
|
}
|
|
709
|
-
voteHashMap[hash].voters.push(
|
|
821
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
|
|
710
822
|
}
|
|
711
823
|
}
|
|
712
824
|
return Object.values(voteHashMap);
|
|
713
825
|
}
|
|
714
|
-
exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage;
|
|
715
826
|
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
716
|
-
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
827
|
+
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
717
828
|
const keyMap = {};
|
|
718
829
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
719
830
|
if (!fromMe) {
|
|
@@ -730,40 +841,34 @@ const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
730
841
|
}
|
|
731
842
|
return Object.values(keyMap);
|
|
732
843
|
};
|
|
733
|
-
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
|
|
734
844
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
735
845
|
/**
|
|
736
846
|
* Downloads the given message. Throws an error if it's not a media message
|
|
737
847
|
*/
|
|
738
|
-
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
739
|
-
const result = await downloadMsg()
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
message = await ctx.reuploadRequest(message);
|
|
749
|
-
const result = await downloadMsg();
|
|
750
|
-
return result;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
848
|
+
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
849
|
+
const result = await downloadMsg().catch(async (error) => {
|
|
850
|
+
if (ctx &&
|
|
851
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
852
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
853
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
854
|
+
// request reupload
|
|
855
|
+
message = await ctx.reuploadRequest(message);
|
|
856
|
+
const result = await downloadMsg();
|
|
857
|
+
return result;
|
|
753
858
|
}
|
|
754
859
|
throw error;
|
|
755
860
|
});
|
|
756
861
|
return result;
|
|
757
862
|
async function downloadMsg() {
|
|
758
|
-
const mContent =
|
|
863
|
+
const mContent = extractMessageContent(message.message);
|
|
759
864
|
if (!mContent) {
|
|
760
|
-
throw new
|
|
865
|
+
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
761
866
|
}
|
|
762
|
-
const contentType =
|
|
763
|
-
let mediaType = contentType
|
|
867
|
+
const contentType = getContentType(mContent);
|
|
868
|
+
let mediaType = contentType?.replace('Message', '');
|
|
764
869
|
const media = mContent[contentType];
|
|
765
870
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
766
|
-
throw new
|
|
871
|
+
throw new Boom(`"${contentType}" message is not a media message`);
|
|
767
872
|
}
|
|
768
873
|
let download;
|
|
769
874
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
@@ -776,7 +881,7 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
776
881
|
else {
|
|
777
882
|
download = media;
|
|
778
883
|
}
|
|
779
|
-
const stream = await
|
|
884
|
+
const stream = await downloadContentFromMessage(download, mediaType, options);
|
|
780
885
|
if (type === 'buffer') {
|
|
781
886
|
const bufferArray = [];
|
|
782
887
|
for await (const chunk of stream) {
|
|
@@ -787,18 +892,16 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
787
892
|
return stream;
|
|
788
893
|
}
|
|
789
894
|
};
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|| (content === null || content === void 0 ? void 0 : content.stickerMessage);
|
|
895
|
+
|
|
896
|
+
export const assertMediaContent = (content) => {
|
|
897
|
+
content = extractMessageContent(content);
|
|
898
|
+
const mediaContent = content?.documentMessage ||
|
|
899
|
+
content?.imageMessage ||
|
|
900
|
+
content?.videoMessage ||
|
|
901
|
+
content?.audioMessage ||
|
|
902
|
+
content?.stickerMessage;
|
|
799
903
|
if (!mediaContent) {
|
|
800
|
-
throw new
|
|
904
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
801
905
|
}
|
|
802
906
|
return mediaContent;
|
|
803
|
-
};
|
|
804
|
-
exports.assertMediaContent = assertMediaContent;
|
|
907
|
+
};
|