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