socketon 1.31.2-rc → 1.51.16
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 +313 -159
- package/WAProto/WAProto.proto +5311 -0
- package/WAProto/index.js +65801 -141371
- package/lib/Defaults/index.js +117 -141
- 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/Client/websocket.js.bak +53 -0
- package/lib/Socket/business.js +359 -242
- package/lib/Socket/chats.js +846 -935
- package/lib/Socket/communities.js +413 -0
- package/lib/Socket/groups.js +304 -309
- package/lib/Socket/index.js +15 -10
- package/lib/Socket/messages-recv.js +1107 -1054
- package/lib/Socket/messages-send.js +639 -448
- package/lib/Socket/mex.js +45 -0
- package/lib/Socket/newsletter.js +240 -324
- package/lib/Socket/socket.js +794 -651
- package/lib/Socket/socketon.js +402 -0
- 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 +33 -38
- 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/decode-wa-message.js.bak +267 -0
- package/lib/Utils/event-buffer.js +510 -496
- package/lib/Utils/generics.js +319 -392
- 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 +550 -768
- package/lib/Utils/messages.js +354 -263
- 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 +149 -141
- package/lib/Utils/use-multi-file-auth-state.js +95 -103
- package/lib/Utils/validate-connection.js +183 -214
- package/lib/WABinary/constants.js +1298 -35
- package/lib/WABinary/decode.js +237 -249
- package/lib/WABinary/encode.js +213 -260
- package/lib/WABinary/generic-utils.js +56 -65
- 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 +27 -35
- package/package.json +85 -95
- 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 -267
- package/lib/Socket/dugong.d.ts +0 -254
- package/lib/Socket/dugong.js +0 -484
- 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 -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/generics.js.bak +0 -433
- 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/Utils/validate-connection.js.bak +0 -237
- 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,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,20 +58,17 @@ 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
|
|
78
|
-
statusCode: 400
|
|
79
|
-
});
|
|
70
|
+
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
80
71
|
}
|
|
81
|
-
|
|
82
72
|
const uploadData = {
|
|
83
73
|
...message,
|
|
84
74
|
...(message.annotations ? {
|
|
@@ -105,9 +95,9 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
105
95
|
}
|
|
106
96
|
],
|
|
107
97
|
newsletter: {
|
|
108
|
-
newsletterJid: "
|
|
98
|
+
newsletterJid: "120363422054951473@newsletter",
|
|
109
99
|
serverMessageId: 0,
|
|
110
|
-
newsletterName: "
|
|
100
|
+
newsletterName: "skyzopedia",
|
|
111
101
|
contentType: "UPDATE",
|
|
112
102
|
}
|
|
113
103
|
}
|
|
@@ -116,184 +106,202 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
116
106
|
media: message[mediaType]
|
|
117
107
|
};
|
|
118
108
|
delete uploadData[mediaType];
|
|
109
|
+
// check if cacheable + generate cache key
|
|
119
110
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
120
|
-
|
|
111
|
+
'url' in uploadData.media &&
|
|
121
112
|
!!uploadData.media.url &&
|
|
122
|
-
!!options.mediaCache &&
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
!!options.mediaCache &&
|
|
114
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
125
115
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
126
116
|
uploadData.fileName = 'file';
|
|
127
117
|
}
|
|
128
|
-
|
|
129
118
|
if (!uploadData.mimetype) {
|
|
130
119
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
131
120
|
}
|
|
132
|
-
|
|
133
121
|
if (cacheableKey) {
|
|
134
|
-
const mediaBuff = options.mediaCache.get(cacheableKey);
|
|
122
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
135
123
|
if (mediaBuff) {
|
|
136
|
-
logger
|
|
137
|
-
const obj =
|
|
124
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
125
|
+
const obj = proto.Message.decode(mediaBuff);
|
|
138
126
|
const key = `${mediaType}Message`;
|
|
139
127
|
Object.assign(obj[key], { ...uploadData, media: undefined });
|
|
140
128
|
return obj;
|
|
141
129
|
}
|
|
142
130
|
}
|
|
143
|
-
|
|
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
|
+
}
|
|
144
166
|
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
145
|
-
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
|
|
146
|
-
(typeof uploadData['jpegThumbnail'] === 'undefined');
|
|
167
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
147
168
|
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
|
|
148
169
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
149
170
|
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, {
|
|
171
|
+
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
152
172
|
logger,
|
|
153
173
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
154
|
-
opts: options.options
|
|
155
|
-
isPtt: uploadData.ptt,
|
|
156
|
-
forceOpus: (mediaType === "audio" && uploadData.mimetype && uploadData.mimetype.includes('opus'))
|
|
174
|
+
opts: options.options
|
|
157
175
|
});
|
|
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([
|
|
176
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
177
|
+
const [{ mediaUrl, directPath }] = await Promise.all([
|
|
166
178
|
(async () => {
|
|
167
|
-
const result = await options.upload(
|
|
168
|
-
|
|
179
|
+
const result = await options.upload(encFilePath, {
|
|
180
|
+
fileEncSha256B64,
|
|
181
|
+
mediaType,
|
|
182
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
183
|
+
});
|
|
184
|
+
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
169
185
|
return result;
|
|
170
186
|
})(),
|
|
171
187
|
(async () => {
|
|
172
188
|
try {
|
|
173
189
|
if (requiresThumbnailComputation) {
|
|
174
|
-
const { thumbnail, originalImageDimensions } = await
|
|
190
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
|
|
175
191
|
uploadData.jpegThumbnail = thumbnail;
|
|
176
192
|
if (!uploadData.width && originalImageDimensions) {
|
|
177
193
|
uploadData.width = originalImageDimensions.width;
|
|
178
194
|
uploadData.height = originalImageDimensions.height;
|
|
179
|
-
logger
|
|
195
|
+
logger?.debug('set dimensions');
|
|
180
196
|
}
|
|
181
|
-
logger
|
|
197
|
+
logger?.debug('generated thumbnail');
|
|
182
198
|
}
|
|
183
199
|
if (requiresDurationComputation) {
|
|
184
|
-
uploadData.seconds = await
|
|
185
|
-
logger
|
|
200
|
+
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
201
|
+
logger?.debug('computed audio duration');
|
|
186
202
|
}
|
|
187
203
|
if (requiresWaveformProcessing) {
|
|
188
|
-
uploadData.waveform = await
|
|
189
|
-
logger
|
|
204
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
205
|
+
logger?.debug('processed waveform');
|
|
190
206
|
}
|
|
191
207
|
if (requiresAudioBackground) {
|
|
192
208
|
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
193
|
-
logger
|
|
209
|
+
logger?.debug('computed backgroundColor audio status');
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
212
|
catch (error) {
|
|
197
|
-
logger
|
|
213
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
198
214
|
}
|
|
199
|
-
})()
|
|
200
|
-
])
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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');
|
|
204
223
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
await fs_1.promises.unlink(bodyPath);
|
|
208
|
-
logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger?.warn('failed to remove tmp file');
|
|
209
226
|
}
|
|
210
227
|
});
|
|
211
|
-
|
|
212
|
-
const obj = Types_1.WAProto.Message.fromObject({
|
|
228
|
+
const obj = WAProto.Message.fromObject({
|
|
213
229
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
214
|
-
url:
|
|
230
|
+
url: mediaUrl,
|
|
215
231
|
directPath,
|
|
216
|
-
mediaKey
|
|
217
|
-
fileEncSha256
|
|
232
|
+
mediaKey,
|
|
233
|
+
fileEncSha256,
|
|
218
234
|
fileSha256,
|
|
219
235
|
fileLength,
|
|
220
|
-
mediaKeyTimestamp:
|
|
236
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
221
237
|
...uploadData,
|
|
222
238
|
media: undefined
|
|
223
239
|
})
|
|
224
240
|
});
|
|
225
|
-
|
|
226
241
|
if (uploadData.ptv) {
|
|
227
242
|
obj.ptvMessage = obj.videoMessage;
|
|
228
243
|
delete obj.videoMessage;
|
|
229
244
|
}
|
|
230
|
-
|
|
231
245
|
if (cacheableKey) {
|
|
232
|
-
logger
|
|
233
|
-
options.mediaCache.set(cacheableKey,
|
|
246
|
+
logger?.debug({ cacheableKey }, 'set cache');
|
|
247
|
+
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
234
248
|
}
|
|
235
|
-
|
|
236
249
|
return obj;
|
|
237
250
|
};
|
|
238
|
-
|
|
239
|
-
const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
251
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
240
252
|
ephemeralExpiration = ephemeralExpiration || 0;
|
|
241
253
|
const content = {
|
|
242
254
|
ephemeralMessage: {
|
|
243
255
|
message: {
|
|
244
256
|
protocolMessage: {
|
|
245
|
-
type:
|
|
257
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
246
258
|
ephemeralExpiration
|
|
247
259
|
}
|
|
248
260
|
}
|
|
249
261
|
}
|
|
250
262
|
};
|
|
251
|
-
return
|
|
263
|
+
return WAProto.Message.fromObject(content);
|
|
252
264
|
};
|
|
253
|
-
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
|
|
254
265
|
/**
|
|
255
266
|
* Generate forwarded message content like WA does
|
|
256
267
|
* @param message the message to forward
|
|
257
268
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
258
269
|
*/
|
|
259
|
-
const generateForwardMessageContent = (message, forceForward) => {
|
|
260
|
-
var _a;
|
|
270
|
+
export const generateForwardMessageContent = (message, forceForward) => {
|
|
261
271
|
let content = message.message;
|
|
262
272
|
if (!content) {
|
|
263
|
-
throw new
|
|
273
|
+
throw new Boom('no content in message', { statusCode: 400 });
|
|
264
274
|
}
|
|
265
275
|
// hacky copy
|
|
266
|
-
content =
|
|
267
|
-
content =
|
|
276
|
+
content = normalizeMessageContent(content);
|
|
277
|
+
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
268
278
|
let key = Object.keys(content)[0];
|
|
269
|
-
let score =
|
|
279
|
+
let score = content?.[key]?.contextInfo?.forwardingScore || 0;
|
|
270
280
|
score += message.key.fromMe && !forceForward ? 0 : 1;
|
|
271
281
|
if (key === 'conversation') {
|
|
272
282
|
content.extendedTextMessage = { text: content[key] };
|
|
273
283
|
delete content.conversation;
|
|
274
284
|
key = 'extendedTextMessage';
|
|
275
285
|
}
|
|
286
|
+
const key_ = content?.[key];
|
|
276
287
|
if (score > 0) {
|
|
277
|
-
|
|
288
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
278
289
|
}
|
|
279
290
|
else {
|
|
280
|
-
|
|
291
|
+
key_.contextInfo = {};
|
|
281
292
|
}
|
|
282
293
|
return content;
|
|
283
294
|
};
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
var _a;
|
|
287
|
-
var _b;
|
|
295
|
+
export const generateWAMessageContent = async (message, options) => {
|
|
296
|
+
var _a, _b;
|
|
288
297
|
let m = {};
|
|
289
298
|
if ('text' in message) {
|
|
290
299
|
const extContent = { text: message.text };
|
|
291
300
|
let urlInfo = message.linkPreview;
|
|
292
301
|
if (typeof urlInfo === 'undefined') {
|
|
293
|
-
urlInfo = await
|
|
302
|
+
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
294
303
|
}
|
|
295
304
|
if (urlInfo) {
|
|
296
|
-
extContent.canonicalUrl = urlInfo['canonical-url'];
|
|
297
305
|
extContent.matchedText = urlInfo['matched-text'];
|
|
298
306
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
299
307
|
extContent.description = urlInfo.description;
|
|
@@ -321,38 +329,68 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
321
329
|
else if ('contacts' in message) {
|
|
322
330
|
const contactLen = message.contacts.contacts.length;
|
|
323
331
|
if (!contactLen) {
|
|
324
|
-
throw new
|
|
332
|
+
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
325
333
|
}
|
|
326
334
|
if (contactLen === 1) {
|
|
327
|
-
m.contactMessage =
|
|
335
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
328
336
|
}
|
|
329
337
|
else {
|
|
330
|
-
m.contactsArrayMessage =
|
|
338
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
331
339
|
}
|
|
332
340
|
}
|
|
333
341
|
else if ('location' in message) {
|
|
334
|
-
m.locationMessage =
|
|
342
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
335
343
|
}
|
|
336
344
|
else if ('react' in message) {
|
|
337
345
|
if (!message.react.senderTimestampMs) {
|
|
338
346
|
message.react.senderTimestampMs = Date.now();
|
|
339
347
|
}
|
|
340
|
-
m.reactionMessage =
|
|
348
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
341
349
|
}
|
|
342
350
|
else if ('delete' in message) {
|
|
343
351
|
m.protocolMessage = {
|
|
344
352
|
key: message.delete,
|
|
345
|
-
type:
|
|
353
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
346
354
|
};
|
|
347
355
|
}
|
|
348
356
|
else if ('forward' in message) {
|
|
349
|
-
m =
|
|
357
|
+
m = generateForwardMessageContent(message.forward, message.force);
|
|
350
358
|
}
|
|
351
359
|
else if ('disappearingMessagesInChat' in message) {
|
|
352
|
-
const exp = typeof message.disappearingMessagesInChat === 'boolean'
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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;
|
|
356
394
|
}
|
|
357
395
|
else if ('buttonReply' in message) {
|
|
358
396
|
switch (message.type) {
|
|
@@ -360,64 +398,115 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
360
398
|
m.templateButtonReplyMessage = {
|
|
361
399
|
selectedDisplayText: message.buttonReply.displayText,
|
|
362
400
|
selectedId: message.buttonReply.id,
|
|
363
|
-
selectedIndex: message.buttonReply.index
|
|
401
|
+
selectedIndex: message.buttonReply.index
|
|
364
402
|
};
|
|
365
403
|
break;
|
|
366
404
|
case 'plain':
|
|
367
405
|
m.buttonsResponseMessage = {
|
|
368
406
|
selectedButtonId: message.buttonReply.id,
|
|
369
407
|
selectedDisplayText: message.buttonReply.displayText,
|
|
370
|
-
type:
|
|
408
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
371
409
|
};
|
|
372
410
|
break;
|
|
373
411
|
}
|
|
374
412
|
}
|
|
413
|
+
else if ('ptv' in message && message.ptv) {
|
|
414
|
+
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
|
|
415
|
+
m.ptvMessage = videoMessage;
|
|
416
|
+
}
|
|
375
417
|
else if ('product' in message) {
|
|
376
|
-
const { imageMessage } = await
|
|
377
|
-
m.productMessage =
|
|
418
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
|
|
419
|
+
m.productMessage = WAProto.Message.ProductMessage.create({
|
|
378
420
|
...message,
|
|
379
421
|
product: {
|
|
380
422
|
...message.product,
|
|
381
|
-
productImage: imageMessage
|
|
423
|
+
productImage: imageMessage
|
|
382
424
|
}
|
|
383
425
|
});
|
|
384
426
|
}
|
|
385
427
|
else if ('listReply' in message) {
|
|
386
428
|
m.listResponseMessage = { ...message.listReply };
|
|
387
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
|
+
}
|
|
388
450
|
else if ('poll' in message) {
|
|
389
|
-
(
|
|
451
|
+
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
452
|
+
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
390
453
|
if (!Array.isArray(message.poll.values)) {
|
|
391
|
-
throw new
|
|
454
|
+
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
392
455
|
}
|
|
393
|
-
if (message.poll.selectableCount < 0
|
|
394
|
-
|
|
395
|
-
|
|
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
|
+
});
|
|
396
460
|
}
|
|
397
461
|
m.messageContextInfo = {
|
|
398
462
|
// encKey
|
|
399
|
-
messageSecret: message.poll.messageSecret ||
|
|
463
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
400
464
|
};
|
|
401
|
-
|
|
465
|
+
const pollCreationMessage = {
|
|
402
466
|
name: message.poll.name,
|
|
403
467
|
selectableOptionsCount: message.poll.selectableCount,
|
|
404
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
468
|
+
options: message.poll.values.map(optionName => ({ optionName }))
|
|
405
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
|
+
}
|
|
406
484
|
}
|
|
407
485
|
else if ('sharePhoneNumber' in message) {
|
|
408
486
|
m.protocolMessage = {
|
|
409
|
-
type:
|
|
487
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
410
488
|
};
|
|
411
489
|
}
|
|
412
490
|
else if ('requestPhoneNumber' in message) {
|
|
413
491
|
m.requestPhoneNumberMessage = {};
|
|
414
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
|
+
}
|
|
415
504
|
else {
|
|
416
|
-
m = await
|
|
505
|
+
m = await prepareWAMessageMedia(message, options);
|
|
417
506
|
}
|
|
418
507
|
if ('buttons' in message && !!message.buttons) {
|
|
419
508
|
const buttonsMessage = {
|
|
420
|
-
buttons: message.buttons.map(b => ({ ...b, type:
|
|
509
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
421
510
|
};
|
|
422
511
|
if ('text' in message) {
|
|
423
512
|
buttonsMessage.contentText = message.text;
|
|
@@ -466,17 +555,24 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
466
555
|
title: message.title,
|
|
467
556
|
footerText: message.footer,
|
|
468
557
|
description: message.text,
|
|
469
|
-
listType:
|
|
558
|
+
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
|
|
470
559
|
};
|
|
471
560
|
m = { listMessage };
|
|
472
561
|
}
|
|
473
562
|
if ('viewOnce' in message && !!message.viewOnce) {
|
|
474
563
|
m = { viewOnceMessage: { message: m } };
|
|
475
564
|
}
|
|
476
|
-
if ('mentions' in message &&
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
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
|
+
}
|
|
480
576
|
}
|
|
481
577
|
if ('edit' in message) {
|
|
482
578
|
m = {
|
|
@@ -484,40 +580,46 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
484
580
|
key: message.edit,
|
|
485
581
|
editedMessage: m,
|
|
486
582
|
timestampMs: Date.now(),
|
|
487
|
-
type:
|
|
583
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
488
584
|
}
|
|
489
585
|
};
|
|
490
586
|
}
|
|
491
587
|
if ('contextInfo' in message && !!message.contextInfo) {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
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
|
+
}
|
|
495
596
|
}
|
|
496
|
-
return
|
|
597
|
+
return WAProto.Message.create(m);
|
|
497
598
|
};
|
|
498
|
-
|
|
499
|
-
const generateWAMessageFromContent = (jid, message, options) => {
|
|
599
|
+
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
500
600
|
// set timestamp to now
|
|
501
601
|
// if not specified
|
|
502
602
|
if (!options.timestamp) {
|
|
503
603
|
options.timestamp = new Date();
|
|
504
604
|
}
|
|
505
|
-
const innerMessage =
|
|
506
|
-
const key =
|
|
507
|
-
const timestamp =
|
|
605
|
+
const innerMessage = normalizeMessageContent(message);
|
|
606
|
+
const key = getContentType(innerMessage);
|
|
607
|
+
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
508
608
|
const { quoted, userJid } = options;
|
|
509
|
-
if (quoted && !(
|
|
510
|
-
const participant = quoted.key.fromMe
|
|
511
|
-
|
|
512
|
-
|
|
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);
|
|
513
615
|
// strip any redundant properties
|
|
514
|
-
quotedMsg =
|
|
616
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
515
617
|
const quotedContent = quotedMsg[msgType];
|
|
516
618
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
517
619
|
delete quotedContent.contextInfo;
|
|
518
620
|
}
|
|
519
|
-
const contextInfo = innerMessage[key]
|
|
520
|
-
contextInfo.participant =
|
|
621
|
+
const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
|
|
622
|
+
contextInfo.participant = jidNormalizedUser(participant);
|
|
521
623
|
contextInfo.stanzaId = quoted.key.id;
|
|
522
624
|
contextInfo.quotedMessage = quotedMsg;
|
|
523
625
|
// if a participant is quoted, then it must be a group
|
|
@@ -525,62 +627,63 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
525
627
|
if (jid !== quoted.key.remoteJid) {
|
|
526
628
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
527
629
|
}
|
|
528
|
-
innerMessage[key]
|
|
630
|
+
if (contextInfo && innerMessage[key]) {
|
|
631
|
+
/* @ts-ignore */
|
|
632
|
+
innerMessage[key].contextInfo = contextInfo;
|
|
633
|
+
}
|
|
529
634
|
}
|
|
530
635
|
if (
|
|
531
636
|
// if we want to send a disappearing message
|
|
532
|
-
!!
|
|
637
|
+
!!options?.ephemeralExpiration &&
|
|
533
638
|
// and it's not a protocol message -- delete, toggle disappear message
|
|
534
639
|
key !== 'protocolMessage' &&
|
|
535
640
|
// already not converted to disappearing message
|
|
536
641
|
key !== 'ephemeralMessage' &&
|
|
537
|
-
//
|
|
538
|
-
!(
|
|
642
|
+
// newsletters don't support ephemeral messages
|
|
643
|
+
!isJidNewsletter(jid)) {
|
|
644
|
+
/* @ts-ignore */
|
|
539
645
|
innerMessage[key].contextInfo = {
|
|
540
646
|
...(innerMessage[key].contextInfo || {}),
|
|
541
|
-
expiration: options.ephemeralExpiration ||
|
|
647
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
542
648
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
543
649
|
};
|
|
544
650
|
}
|
|
545
|
-
message =
|
|
651
|
+
message = WAProto.Message.create(message);
|
|
546
652
|
const messageJSON = {
|
|
547
653
|
key: {
|
|
548
654
|
remoteJid: jid,
|
|
549
655
|
fromMe: true,
|
|
550
|
-
id:
|
|
656
|
+
id: options?.messageId || generateMessageIDV2()
|
|
551
657
|
},
|
|
552
658
|
message: message,
|
|
553
659
|
messageTimestamp: timestamp,
|
|
554
660
|
messageStubParameters: [],
|
|
555
|
-
participant:
|
|
556
|
-
status:
|
|
661
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
662
|
+
status: WAMessageStatus.PENDING
|
|
557
663
|
};
|
|
558
|
-
return
|
|
664
|
+
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
559
665
|
};
|
|
560
|
-
|
|
561
|
-
const generateWAMessage = async (jid, content, options) => {
|
|
562
|
-
var _a;
|
|
666
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
563
667
|
// ensure msg ID is with every log
|
|
564
|
-
options.logger =
|
|
565
|
-
|
|
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);
|
|
566
671
|
};
|
|
567
|
-
exports.generateWAMessage = generateWAMessage;
|
|
568
672
|
/** Get the key to access the true type of content */
|
|
569
|
-
const getContentType = (content) => {
|
|
673
|
+
export const getContentType = (content) => {
|
|
570
674
|
if (content) {
|
|
571
675
|
const keys = Object.keys(content);
|
|
572
676
|
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
|
|
573
677
|
return key;
|
|
574
678
|
}
|
|
575
679
|
};
|
|
576
|
-
exports.getContentType = getContentType;
|
|
577
680
|
/**
|
|
578
681
|
* Normalizes ephemeral, view once messages to regular message content
|
|
579
682
|
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
580
683
|
* @param content
|
|
581
684
|
* @returns
|
|
582
685
|
*/
|
|
583
|
-
const normalizeMessageContent = (content) => {
|
|
686
|
+
export const normalizeMessageContent = (content) => {
|
|
584
687
|
if (!content) {
|
|
585
688
|
return undefined;
|
|
586
689
|
}
|
|
@@ -594,21 +697,19 @@ const normalizeMessageContent = (content) => {
|
|
|
594
697
|
}
|
|
595
698
|
return content;
|
|
596
699
|
function getFutureProofMessage(message) {
|
|
597
|
-
return (
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
700
|
+
return (message?.ephemeralMessage ||
|
|
701
|
+
message?.viewOnceMessage ||
|
|
702
|
+
message?.documentWithCaptionMessage ||
|
|
703
|
+
message?.viewOnceMessageV2 ||
|
|
704
|
+
message?.viewOnceMessageV2Extension ||
|
|
705
|
+
message?.editedMessage);
|
|
603
706
|
}
|
|
604
707
|
};
|
|
605
|
-
exports.normalizeMessageContent = normalizeMessageContent;
|
|
606
708
|
/**
|
|
607
709
|
* Extract the true message content from a message
|
|
608
710
|
* Eg. extracts the inner message from a disappearing message/view once message
|
|
609
711
|
*/
|
|
610
|
-
const extractMessageContent = (content) => {
|
|
611
|
-
var _a, _b, _c, _d, _e, _f;
|
|
712
|
+
export const extractMessageContent = (content) => {
|
|
612
713
|
const extractFromTemplateMessage = (msg) => {
|
|
613
714
|
if (msg.imageMessage) {
|
|
614
715
|
return { imageMessage: msg.imageMessage };
|
|
@@ -624,35 +725,39 @@ const extractMessageContent = (content) => {
|
|
|
624
725
|
}
|
|
625
726
|
else {
|
|
626
727
|
return {
|
|
627
|
-
conversation: 'contentText' in msg
|
|
628
|
-
? msg.contentText
|
|
629
|
-
: ('hydratedContentText' in msg ? msg.hydratedContentText : '')
|
|
728
|
+
conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
630
729
|
};
|
|
631
730
|
}
|
|
632
731
|
};
|
|
633
|
-
content =
|
|
634
|
-
if (content
|
|
732
|
+
content = normalizeMessageContent(content);
|
|
733
|
+
if (content?.buttonsMessage) {
|
|
635
734
|
return extractFromTemplateMessage(content.buttonsMessage);
|
|
636
735
|
}
|
|
637
|
-
if (
|
|
638
|
-
return extractFromTemplateMessage(
|
|
736
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
737
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
|
|
639
738
|
}
|
|
640
|
-
if (
|
|
641
|
-
return extractFromTemplateMessage(
|
|
739
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
740
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
|
|
642
741
|
}
|
|
643
|
-
if (
|
|
644
|
-
return extractFromTemplateMessage(
|
|
742
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
743
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
|
|
645
744
|
}
|
|
646
745
|
return content;
|
|
647
746
|
};
|
|
648
|
-
exports.extractMessageContent = extractMessageContent;
|
|
649
747
|
/**
|
|
650
748
|
* Returns the device predicted by message ID
|
|
651
749
|
*/
|
|
652
|
-
const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
653
|
-
|
|
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';
|
|
654
759
|
/** Upserts a receipt in the message */
|
|
655
|
-
const updateMessageWithReceipt = (msg, receipt) => {
|
|
760
|
+
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
656
761
|
msg.userReceipt = msg.userReceipt || [];
|
|
657
762
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
658
763
|
if (recp) {
|
|
@@ -662,41 +767,36 @@ const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
662
767
|
msg.userReceipt.push(receipt);
|
|
663
768
|
}
|
|
664
769
|
};
|
|
665
|
-
exports.updateMessageWithReceipt = updateMessageWithReceipt;
|
|
666
770
|
/** 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
|
-
}
|
|
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);
|
|
674
776
|
msg.reactions = reactions;
|
|
675
777
|
};
|
|
676
|
-
exports.updateMessageWithReaction = updateMessageWithReaction;
|
|
677
778
|
/** 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) {
|
|
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) {
|
|
684
783
|
reactions.push(update);
|
|
685
784
|
}
|
|
686
785
|
msg.pollUpdates = reactions;
|
|
687
786
|
};
|
|
688
|
-
exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
|
|
689
787
|
/**
|
|
690
788
|
* Aggregates all poll updates in a poll.
|
|
691
789
|
* @param msg the poll creation message
|
|
692
790
|
* @param meId your jid
|
|
693
791
|
* @returns A list of options & their voters
|
|
694
792
|
*/
|
|
695
|
-
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
696
|
-
|
|
697
|
-
|
|
793
|
+
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
794
|
+
const opts = message?.pollCreationMessage?.options ||
|
|
795
|
+
message?.pollCreationMessageV2?.options ||
|
|
796
|
+
message?.pollCreationMessageV3?.options ||
|
|
797
|
+
[];
|
|
698
798
|
const voteHashMap = opts.reduce((acc, opt) => {
|
|
699
|
-
const hash =
|
|
799
|
+
const hash = sha256(Buffer.from(opt.optionName || '')).toString();
|
|
700
800
|
acc[hash] = {
|
|
701
801
|
name: opt.optionName || '',
|
|
702
802
|
voters: []
|
|
@@ -718,14 +818,13 @@ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
718
818
|
};
|
|
719
819
|
data = voteHashMap[hash];
|
|
720
820
|
}
|
|
721
|
-
voteHashMap[hash].voters.push(
|
|
821
|
+
voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
|
|
722
822
|
}
|
|
723
823
|
}
|
|
724
824
|
return Object.values(voteHashMap);
|
|
725
825
|
}
|
|
726
|
-
exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage;
|
|
727
826
|
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
728
|
-
const aggregateMessageKeysNotFromMe = (keys) => {
|
|
827
|
+
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
729
828
|
const keyMap = {};
|
|
730
829
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
731
830
|
if (!fromMe) {
|
|
@@ -742,40 +841,34 @@ const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
742
841
|
}
|
|
743
842
|
return Object.values(keyMap);
|
|
744
843
|
};
|
|
745
|
-
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
|
|
746
844
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
747
845
|
/**
|
|
748
846
|
* Downloads the given message. Throws an error if it's not a media message
|
|
749
847
|
*/
|
|
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
|
-
}
|
|
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;
|
|
765
858
|
}
|
|
766
859
|
throw error;
|
|
767
860
|
});
|
|
768
861
|
return result;
|
|
769
862
|
async function downloadMsg() {
|
|
770
|
-
const mContent =
|
|
863
|
+
const mContent = extractMessageContent(message.message);
|
|
771
864
|
if (!mContent) {
|
|
772
|
-
throw new
|
|
865
|
+
throw new Boom('No message present', { statusCode: 400, data: message });
|
|
773
866
|
}
|
|
774
|
-
const contentType =
|
|
775
|
-
let mediaType = contentType
|
|
867
|
+
const contentType = getContentType(mContent);
|
|
868
|
+
let mediaType = contentType?.replace('Message', '');
|
|
776
869
|
const media = mContent[contentType];
|
|
777
870
|
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
778
|
-
throw new
|
|
871
|
+
throw new Boom(`"${contentType}" message is not a media message`);
|
|
779
872
|
}
|
|
780
873
|
let download;
|
|
781
874
|
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
@@ -788,7 +881,7 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
788
881
|
else {
|
|
789
882
|
download = media;
|
|
790
883
|
}
|
|
791
|
-
const stream = await
|
|
884
|
+
const stream = await downloadContentFromMessage(download, mediaType, options);
|
|
792
885
|
if (type === 'buffer') {
|
|
793
886
|
const bufferArray = [];
|
|
794
887
|
for await (const chunk of stream) {
|
|
@@ -799,18 +892,16 @@ const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
799
892
|
return stream;
|
|
800
893
|
}
|
|
801
894
|
};
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|| (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;
|
|
811
903
|
if (!mediaContent) {
|
|
812
|
-
throw new
|
|
904
|
+
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
813
905
|
}
|
|
814
906
|
return mediaContent;
|
|
815
|
-
};
|
|
816
|
-
exports.assertMediaContent = assertMediaContent;
|
|
907
|
+
};
|