@realvare/based 2.7.61 → 2.7.70
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 +25 -114
- package/WAProto/WAProto.proto +1073 -244
- package/WAProto/index.d.ts +16282 -8183
- package/WAProto/index.js +76605 -50628
- package/engine-requirements.js +10 -10
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.d.ts +1 -1
- package/lib/Defaults/index.js +5 -5
- package/lib/Socket/chats.js +53 -16
- package/lib/Socket/groups.js +53 -9
- package/lib/Socket/messages-recv.js +1298 -1237
- package/lib/Socket/messages-send.js +350 -456
- package/lib/Socket/socket.js +1 -1
- package/lib/Socket/usync.js +57 -4
- package/lib/Store/make-in-memory-store.js +28 -15
- package/lib/Types/Message.d.ts +316 -6
- package/lib/Types/Message.js +1 -1
- package/lib/Utils/cache-manager.d.ts +16 -0
- package/lib/Utils/cache-manager.js +20 -4
- package/lib/Utils/chat-utils.js +17 -13
- package/lib/Utils/decode-wa-message.js +1 -11
- package/lib/Utils/event-buffer.js +103 -2
- package/lib/Utils/generics.js +4 -5
- package/lib/Utils/index.d.ts +5 -0
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/jid-validation.d.ts +2 -0
- package/lib/Utils/jid-validation.js +36 -5
- package/lib/Utils/link-preview.js +38 -28
- package/lib/Utils/messages-media.js +21 -52
- package/lib/Utils/messages.js +540 -23
- package/lib/Utils/performance-config.d.ts +2 -0
- package/lib/Utils/performance-config.js +16 -7
- package/lib/Utils/process-message.js +124 -12
- package/lib/Utils/rate-limiter.js +15 -20
- package/lib/WABinary/jid-utils.js +257 -5
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +75 -5
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +59 -6
- package/lib/WAUSync/USyncQuery.js +64 -6
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -2
- package/package.json +7 -13
- package/WAProto/index.ts.ts +0 -53473
|
@@ -7,6 +7,7 @@ exports.makeMessagesSocket = void 0;
|
|
|
7
7
|
const boom_1 = require("@hapi/boom");
|
|
8
8
|
const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
|
|
9
9
|
const crypto_1 = require("crypto");
|
|
10
|
+
const AbortController = require("abort-controller");
|
|
10
11
|
const WAProto_1 = require("../../WAProto");
|
|
11
12
|
const Defaults_1 = require("../Defaults");
|
|
12
13
|
const Utils_1 = require("../Utils");
|
|
@@ -15,7 +16,6 @@ const link_preview_1 = require("../Utils/link-preview");
|
|
|
15
16
|
const WABinary_1 = require("../WABinary");
|
|
16
17
|
const WAUSync_1 = require("../WAUSync");
|
|
17
18
|
const newsletter_1 = require("./newsletter");
|
|
18
|
-
const rate_limiter_1 = __importDefault(require("../Utils/rate-limiter"));
|
|
19
19
|
const makeMessagesSocket = (config) => {
|
|
20
20
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: axiosOptions, patchMessageBeforeSending, cachedGroupMetadata, } = config;
|
|
21
21
|
const sock = (0, newsletter_1.makeNewsletterSocket)(config);
|
|
@@ -24,13 +24,6 @@ const makeMessagesSocket = (config) => {
|
|
|
24
24
|
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
25
25
|
useClones: false
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
// Initialize rate limiter for anti-ban protection
|
|
29
|
-
const messagesSendRate = config.messagesSendRate || 1;
|
|
30
|
-
if(messagesSendRate > 5) {
|
|
31
|
-
logger.warn(`messagesSendRate is set to a high value (${messagesSendRate}), this may increase the risk of getting banned. Recommended value is <= 3`);
|
|
32
|
-
}
|
|
33
|
-
const rateLimiter = new rate_limiter_1.default(messagesSendRate);
|
|
34
27
|
let mediaConn;
|
|
35
28
|
const refreshMediaConn = async (forceGet = false) => {
|
|
36
29
|
const media = await mediaConn;
|
|
@@ -169,11 +162,61 @@ const makeMessagesSocket = (config) => {
|
|
|
169
162
|
}
|
|
170
163
|
return deviceResults;
|
|
171
164
|
};
|
|
165
|
+
// Cache to track JIDs that have failed session fetching to prevent infinite loops
|
|
166
|
+
const failedSessionFetchCache = new Map();
|
|
167
|
+
const FAILED_SESSION_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
168
|
+
|
|
169
|
+
// Cache to track recently sent messages to prevent duplicate sends
|
|
170
|
+
const recentlySentMessagesCache = new node_cache_1.default({
|
|
171
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
172
|
+
useClones: false,
|
|
173
|
+
maxKeys: 1000 // Limit to prevent memory issues
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Cleanup function to remove expired entries from the cache
|
|
177
|
+
const cleanupFailedSessionCache = () => {
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
for (const [jid, failureTime] of failedSessionFetchCache.entries()) {
|
|
180
|
+
if (now - failureTime >= FAILED_SESSION_CACHE_TTL) {
|
|
181
|
+
failedSessionFetchCache.delete(jid);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Run cleanup every 5 minutes
|
|
187
|
+
const cleanupInterval = setInterval(cleanupFailedSessionCache, 5 * 60 * 1000);
|
|
188
|
+
|
|
189
|
+
// Helper function to check if message was recently sent
|
|
190
|
+
const wasMessageRecentlySent = (msgId, jid) => {
|
|
191
|
+
const cacheKey = `${msgId}:${jid}`;
|
|
192
|
+
return recentlySentMessagesCache.has(cacheKey);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Helper function to mark message as recently sent
|
|
196
|
+
const markMessageAsSent = (msgId, jid) => {
|
|
197
|
+
const cacheKey = `${msgId}:${jid}`;
|
|
198
|
+
recentlySentMessagesCache.set(cacheKey, true);
|
|
199
|
+
};
|
|
200
|
+
|
|
172
201
|
const assertSessions = async (jids, force) => {
|
|
173
202
|
let didFetchNewSession = false;
|
|
174
203
|
let jidsRequiringFetch = [];
|
|
175
204
|
if (force) {
|
|
176
|
-
|
|
205
|
+
// Filter out JIDs that have recently failed session fetching
|
|
206
|
+
jidsRequiringFetch = jids.filter(jid => {
|
|
207
|
+
const failureTime = failedSessionFetchCache.get(jid);
|
|
208
|
+
if (failureTime && (Date.now() - failureTime) < FAILED_SESSION_CACHE_TTL) {
|
|
209
|
+
logger.debug({ jid }, 'skipping session fetch for recently failed JID');
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// If all JIDs are filtered out, return early without attempting fetch
|
|
216
|
+
if (jidsRequiringFetch.length === 0 && jids.length > 0) {
|
|
217
|
+
logger.debug({ originalJids: jids }, 'all JIDs recently failed, skipping session fetch entirely');
|
|
218
|
+
return didFetchNewSession;
|
|
219
|
+
}
|
|
177
220
|
}
|
|
178
221
|
else {
|
|
179
222
|
const addrs = jids.map(jid => (signalRepository
|
|
@@ -183,57 +226,107 @@ const makeMessagesSocket = (config) => {
|
|
|
183
226
|
const signalId = signalRepository
|
|
184
227
|
.jidToSignalProtocolAddress(jid);
|
|
185
228
|
if (!sessions[signalId]) {
|
|
186
|
-
|
|
229
|
+
// Also check if this JID recently failed
|
|
230
|
+
const failureTime = failedSessionFetchCache.get(jid);
|
|
231
|
+
if (!failureTime || (Date.now() - failureTime) >= FAILED_SESSION_CACHE_TTL) {
|
|
232
|
+
jidsRequiringFetch.push(jid);
|
|
233
|
+
}
|
|
187
234
|
}
|
|
188
235
|
}
|
|
189
236
|
}
|
|
190
237
|
if (jidsRequiringFetch.length) {
|
|
191
238
|
logger.debug({ jidsRequiringFetch }, 'fetching sessions');
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
const TOTAL_TIMEOUT_MS = 120000; // 120 seconds
|
|
240
|
+
const abortController = new AbortController();
|
|
241
|
+
const timeout = setTimeout(() => abortController.abort(), TOTAL_TIMEOUT_MS);
|
|
242
|
+
try {
|
|
243
|
+
const BATCH_SIZE = 50;
|
|
244
|
+
for (let i = 0; i < jidsRequiringFetch.length; i += BATCH_SIZE) {
|
|
245
|
+
const batch = jidsRequiringFetch.slice(i, i + BATCH_SIZE);
|
|
246
|
+
try {
|
|
247
|
+
const result = await (0, retry_1.retryWithBackoff)(() => query({
|
|
248
|
+
tag: 'iq',
|
|
249
|
+
attrs: {
|
|
250
|
+
xmlns: 'encrypt',
|
|
251
|
+
type: 'get',
|
|
252
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
253
|
+
},
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
tag: 'key',
|
|
257
|
+
attrs: {},
|
|
258
|
+
content: batch.map(jid => ({
|
|
259
|
+
tag: 'user',
|
|
260
|
+
attrs: { jid },
|
|
261
|
+
}))
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}), {
|
|
265
|
+
retries: 4,
|
|
266
|
+
baseMs: 2000,
|
|
267
|
+
maxMs: 10000,
|
|
268
|
+
jitter: true,
|
|
269
|
+
timeoutPerAttemptMs: 25000,
|
|
270
|
+
shouldRetry: (err) => {
|
|
271
|
+
var _a;
|
|
272
|
+
const status = ((_a = err.output) === null || _a === void 0 ? void 0 : _a.statusCode) || (err === null || err === void 0 ? void 0 : err.statusCode);
|
|
273
|
+
// Don't retry "not-acceptable" (406) errors as they indicate permission issues
|
|
274
|
+
// Don't retry aborted requests as they were intentionally cancelled
|
|
275
|
+
if (status === 406 || err.message === 'aborted' || err.code === 'ABORT_ERR') {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return !status || (status >= 500 || status === 408 || status === 429) || err.message.includes('WebSocket is not open');
|
|
279
|
+
},
|
|
280
|
+
onRetry: (err, n) => logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt: n }, 'retrying fetch sessions'),
|
|
281
|
+
signal: abortController.signal
|
|
282
|
+
});
|
|
283
|
+
await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
|
|
284
|
+
didFetchNewSession = true;
|
|
285
|
+
} catch (err) {
|
|
286
|
+
// Cache failed JIDs to prevent infinite retries
|
|
287
|
+
logger.warn({ err, batch }, 'session fetch failed for batch, caching failed JIDs');
|
|
288
|
+
for (const jid of batch) {
|
|
289
|
+
failedSessionFetchCache.set(jid, Date.now());
|
|
290
|
+
}
|
|
291
|
+
// Re-throw the error so the caller knows the fetch failed
|
|
292
|
+
throw err;
|
|
207
293
|
}
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
294
|
+
}
|
|
295
|
+
} finally {
|
|
296
|
+
clearTimeout(timeout);
|
|
297
|
+
}
|
|
212
298
|
}
|
|
213
299
|
return didFetchNewSession;
|
|
214
300
|
};
|
|
215
|
-
const
|
|
301
|
+
const sendPeerMessage = async (protocolMessageContent, options = {}) => {
|
|
216
302
|
var _a;
|
|
217
|
-
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
218
303
|
if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
219
304
|
throw new boom_1.Boom('Not authenticated');
|
|
220
305
|
}
|
|
306
|
+
|
|
221
307
|
const protocolMessage = {
|
|
222
|
-
protocolMessage:
|
|
223
|
-
peerDataOperationRequestMessage: pdoMessage,
|
|
224
|
-
type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
225
|
-
}
|
|
308
|
+
protocolMessage: protocolMessageContent
|
|
226
309
|
};
|
|
310
|
+
|
|
227
311
|
const meJid = (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id);
|
|
228
312
|
const msgId = await relayMessage(meJid, protocolMessage, {
|
|
229
313
|
additionalAttributes: {
|
|
230
314
|
category: 'peer',
|
|
231
315
|
// eslint-disable-next-line camelcase
|
|
232
316
|
push_priority: 'high_force',
|
|
317
|
+
...options.additionalAttributes
|
|
233
318
|
},
|
|
319
|
+
...options
|
|
234
320
|
});
|
|
235
321
|
return msgId;
|
|
236
322
|
};
|
|
323
|
+
|
|
324
|
+
const sendPeerDataOperationMessage = async (pdoMessage) => {
|
|
325
|
+
return sendPeerMessage({
|
|
326
|
+
peerDataOperationRequestMessage: pdoMessage,
|
|
327
|
+
type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
328
|
+
});
|
|
329
|
+
};
|
|
237
330
|
const createParticipantNodes = async (jids, message, extraAttrs) => {
|
|
238
331
|
let patched = await patchMessageBeforeSending(message, jids);
|
|
239
332
|
if (!Array.isArray(patched)) {
|
|
@@ -279,6 +372,13 @@ const makeMessagesSocket = (config) => {
|
|
|
279
372
|
const isStatus = jid === statusJid;
|
|
280
373
|
const isLid = server === 'lid';
|
|
281
374
|
msgId = msgId || (0, Utils_1.generateMessageIDV2)((_a = sock.user) === null || _a === void 0 ? void 0 : _a.id);
|
|
375
|
+
|
|
376
|
+
// Check if this message was recently sent to prevent duplicate sends
|
|
377
|
+
if (wasMessageRecentlySent(msgId, jid)) {
|
|
378
|
+
logger.debug({ msgId, jid }, 'message recently sent, skipping duplicate send');
|
|
379
|
+
return msgId;
|
|
380
|
+
}
|
|
381
|
+
|
|
282
382
|
useUserDevicesCache = useUserDevicesCache !== false;
|
|
283
383
|
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
|
|
284
384
|
const participants = [];
|
|
@@ -549,9 +649,9 @@ const makeMessagesSocket = (config) => {
|
|
|
549
649
|
}
|
|
550
650
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
551
651
|
await (0, retry_1.retryWithBackoff)(({ signal }) => sendNode(stanza, { signal }), {
|
|
552
|
-
retries:
|
|
553
|
-
baseMs:
|
|
554
|
-
maxMs:
|
|
652
|
+
retries: 2, // Riduci i tentativi
|
|
653
|
+
baseMs: 100, // Riduci l'attesa iniziale
|
|
654
|
+
maxMs: 2000, // Riduci l'attesa massima
|
|
555
655
|
jitter: true,
|
|
556
656
|
timeoutPerAttemptMs: 5000,
|
|
557
657
|
shouldRetry: (err) => {
|
|
@@ -561,6 +661,9 @@ const makeMessagesSocket = (config) => {
|
|
|
561
661
|
},
|
|
562
662
|
onRetry: (err, n) => logger?.warn?.({ err, attempt: n }, 'retrying sendNode')
|
|
563
663
|
});
|
|
664
|
+
|
|
665
|
+
// Mark message as successfully sent to prevent duplicate sends
|
|
666
|
+
markMessageAsSent(msgId, jid);
|
|
564
667
|
});
|
|
565
668
|
return msgId;
|
|
566
669
|
};
|
|
@@ -666,205 +769,170 @@ const makeMessagesSocket = (config) => {
|
|
|
666
769
|
});
|
|
667
770
|
return result;
|
|
668
771
|
};
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
772
|
+
const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);
|
|
773
|
+
const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');
|
|
774
|
+
return {
|
|
775
|
+
...sock,
|
|
776
|
+
getPrivacyTokens,
|
|
777
|
+
assertSessions,
|
|
778
|
+
relayMessage,
|
|
779
|
+
sendReceipt,
|
|
780
|
+
sendReceipts,
|
|
781
|
+
readMessages,
|
|
782
|
+
sendPeerDataOperationMessage,
|
|
783
|
+
sendPeerMessage,
|
|
784
|
+
getUSyncDevices,
|
|
785
|
+
getFailedSessionCache: () => failedSessionFetchCache,
|
|
786
|
+
getFailedSessionCacheTTL: () => FAILED_SESSION_CACHE_TTL,
|
|
787
|
+
getRecentlySentMessagesCache: () => recentlySentMessagesCache,
|
|
788
|
+
wasMessageRecentlySent,
|
|
789
|
+
markMessageAsSent,
|
|
790
|
+
sendMessage: async (jid, content, options = {}) => {
|
|
791
|
+
var _a, _b, _c;
|
|
792
|
+
const userJid = authState.creds.me.id;
|
|
793
|
+
if (!options.ephemeralExpiration) {
|
|
794
|
+
if ((0, WABinary_1.isJidGroup)(jid)) {
|
|
795
|
+
const groups = await sock.groupQuery(jid, 'get', [{
|
|
796
|
+
tag: 'query',
|
|
797
|
+
attrs: {
|
|
798
|
+
request: 'interactive'
|
|
799
|
+
}
|
|
800
|
+
}]);
|
|
801
|
+
const metadata = (0, WABinary_1.getBinaryNodeChild)(groups, 'group');
|
|
802
|
+
const expiration = ((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(metadata, 'ephemeral')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.expiration) || 0;
|
|
803
|
+
options.ephemeralExpiration = expiration;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (typeof content === 'object' &&
|
|
807
|
+
'disappearingMessagesInChat' in content &&
|
|
808
|
+
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
809
|
+
(0, WABinary_1.isJidGroup)(jid)) {
|
|
810
|
+
const { disappearingMessagesInChat } = content;
|
|
811
|
+
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
|
812
|
+
(disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
|
|
813
|
+
disappearingMessagesInChat;
|
|
814
|
+
await groupToggleEphemeral(jid, value);
|
|
815
|
+
}
|
|
816
|
+
// Handle pin messages
|
|
817
|
+
if (typeof content === 'object' && 'pin' in content && content.pin) {
|
|
818
|
+
const pinData = typeof content.pin === 'object' ? content.pin : { key: content.pin };
|
|
819
|
+
// Map type: 1 = PIN_FOR_ALL, 2 = UNPIN_FOR_ALL
|
|
820
|
+
const pinType = pinData.type !== undefined ? pinData.type : (content.type !== undefined ? content.type : WAProto_1.proto.Message.PinInChatMessage.Type.PIN_FOR_ALL);
|
|
821
|
+
const msgId = (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id);
|
|
822
|
+
const pinMessage = {
|
|
823
|
+
pinInChatMessage: {
|
|
824
|
+
key: pinData.key,
|
|
825
|
+
type: pinType,
|
|
826
|
+
senderTimestampMs: Date.now()
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
// Add messageContextInfo only for PIN (type 1), not for UNPIN (type 2)
|
|
830
|
+
if (pinType === WAProto_1.proto.Message.PinInChatMessage.Type.PIN_FOR_ALL) {
|
|
831
|
+
pinMessage.messageContextInfo = {
|
|
832
|
+
messageAddOnDurationInSecs: pinData.time || content.time || 86400, // Default 24 hours
|
|
833
|
+
messageAddOnExpiryType: WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.STATIC
|
|
834
|
+
};
|
|
732
835
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
},
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
836
|
+
const fullMsg = {
|
|
837
|
+
key: {
|
|
838
|
+
remoteJid: jid,
|
|
839
|
+
fromMe: true,
|
|
840
|
+
id: msgId,
|
|
841
|
+
participant: userJid
|
|
842
|
+
},
|
|
843
|
+
message: pinMessage,
|
|
844
|
+
messageTimestamp: (0, Utils_1.unixTimestampSeconds)()
|
|
845
|
+
};
|
|
846
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata });
|
|
847
|
+
if (config.emitOwnEvents) {
|
|
848
|
+
process.nextTick(() => {
|
|
849
|
+
processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
|
|
747
850
|
});
|
|
748
851
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}, {
|
|
756
|
-
userJid,
|
|
757
|
-
upload: async (readStream, opts) => {
|
|
758
|
-
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
759
|
-
mediaHandle = up.handle;
|
|
760
|
-
return up;
|
|
761
|
-
},
|
|
762
|
-
...options,
|
|
763
|
-
});
|
|
852
|
+
return fullMsg;
|
|
853
|
+
}
|
|
854
|
+
if (typeof content === 'object' && 'album' in content && content.album) {
|
|
855
|
+
const { album, caption } = content;
|
|
856
|
+
if (caption && !album[0].caption) {
|
|
857
|
+
album[0].caption = caption;
|
|
764
858
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
859
|
+
let mediaHandle;
|
|
860
|
+
let mediaMsg;
|
|
861
|
+
const albumMsg = (0, Utils_1.generateWAMessageFromContent)(jid, {
|
|
862
|
+
albumMessage: {
|
|
863
|
+
expectedImageCount: album.filter(item => 'image' in item).length,
|
|
864
|
+
expectedVideoCount: album.filter(item => 'video' in item).length
|
|
865
|
+
}
|
|
866
|
+
}, { userJid, ...options });
|
|
867
|
+
await relayMessage(jid, albumMsg.message, {
|
|
868
|
+
messageId: albumMsg.key.id
|
|
869
|
+
});
|
|
870
|
+
for (const i in album) {
|
|
871
|
+
const media = album[i];
|
|
872
|
+
if ('image' in media) {
|
|
873
|
+
mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
|
|
874
|
+
image: media.image,
|
|
875
|
+
...(media.caption ? { caption: media.caption } : {}),
|
|
876
|
+
...options
|
|
877
|
+
}, {
|
|
878
|
+
userJid,
|
|
879
|
+
upload: async (readStream, opts) => {
|
|
880
|
+
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
881
|
+
mediaHandle = up.handle;
|
|
882
|
+
return up;
|
|
883
|
+
},
|
|
884
|
+
...options,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
else if ('video' in media) {
|
|
888
|
+
mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
|
|
889
|
+
video: media.video,
|
|
890
|
+
...(media.caption ? { caption: media.caption } : {}),
|
|
891
|
+
...(media.gifPlayback !== undefined ? { gifPlayback: media.gifPlayback } : {}),
|
|
892
|
+
...options
|
|
893
|
+
}, {
|
|
894
|
+
userJid,
|
|
895
|
+
upload: async (readStream, opts) => {
|
|
896
|
+
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
897
|
+
mediaHandle = up.handle;
|
|
898
|
+
return up;
|
|
899
|
+
},
|
|
900
|
+
...options,
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
if (mediaMsg) {
|
|
904
|
+
mediaMsg.message.messageContextInfo = {
|
|
905
|
+
messageSecret: (0, crypto_1.randomBytes)(32),
|
|
906
|
+
messageAssociation: {
|
|
907
|
+
associationType: 1,
|
|
908
|
+
parentMessageKey: albumMsg.key
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
}
|
|
773
912
|
await relayMessage(jid, mediaMsg.message, {
|
|
774
913
|
messageId: mediaMsg.key.id
|
|
775
914
|
});
|
|
776
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
915
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
777
916
|
}
|
|
917
|
+
return albumMsg;
|
|
778
918
|
}
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
else if (typeof content === 'object' && 'stickerPack' in content && content.stickerPack) {
|
|
782
|
-
// Send sticker pack metadata first, then each sticker associated with it
|
|
783
|
-
const { stickerPack } = content;
|
|
784
|
-
const stickers = stickerPack.stickers || [];
|
|
785
|
-
if (!Array.isArray(stickers) || stickers.length === 0) {
|
|
786
|
-
throw new boom_1.Boom('stickerPack requires at least one sticker', { statusCode: 400 });
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// Prepare cover thumbnail if provided
|
|
790
|
-
let thumbnailDirectPath;
|
|
791
|
-
let thumbnailEncSha256;
|
|
792
|
-
let thumbnailSha256;
|
|
793
|
-
let thumbnailHeight;
|
|
794
|
-
let thumbnailWidth;
|
|
795
|
-
if (stickerPack.cover) {
|
|
796
|
-
try {
|
|
797
|
-
const thumbMsg = await (0, Utils_1.prepareWAMessageMedia)({ image: stickerPack.cover }, {
|
|
798
|
-
logger,
|
|
799
|
-
userJid,
|
|
800
|
-
upload: async (readStream, opts) => {
|
|
801
|
-
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
802
|
-
return up;
|
|
803
|
-
},
|
|
804
|
-
mediaCache: config.mediaCache,
|
|
805
|
-
options: config.options,
|
|
806
|
-
messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
|
|
807
|
-
...options,
|
|
808
|
-
});
|
|
809
|
-
if (thumbMsg.imageMessage) {
|
|
810
|
-
thumbnailDirectPath = thumbMsg.imageMessage.directPath;
|
|
811
|
-
thumbnailEncSha256 = thumbMsg.imageMessage.fileEncSha256;
|
|
812
|
-
thumbnailSha256 = thumbMsg.imageMessage.fileSha256;
|
|
813
|
-
thumbnailHeight = thumbMsg.imageMessage.height;
|
|
814
|
-
thumbnailWidth = thumbMsg.imageMessage.width;
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
catch (err) {
|
|
818
|
-
logger === null || logger === void 0 ? void 0 : logger.warn({ err }, 'failed to prepare stickerPack cover');
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// Map stickers metadata to proto-friendly shape
|
|
823
|
-
const protoStickers = stickers.map((s, idx) => ({
|
|
824
|
-
fileName: s.fileName || `sticker_${idx}.webp`,
|
|
825
|
-
isAnimated: !!s.isAnimated,
|
|
826
|
-
emojis: Array.isArray(s.emojis) ? s.emojis : (s.emojis ? [s.emojis] : []),
|
|
827
|
-
accessibilityLabel: s.accessibilityLabel,
|
|
828
|
-
isLottie: !!s.isLottie,
|
|
829
|
-
mimetype: s.mimetype || 'image/webp'
|
|
830
|
-
}));
|
|
831
|
-
|
|
832
|
-
const stickerPackObj = {
|
|
833
|
-
name: stickerPack.name,
|
|
834
|
-
publisher: stickerPack.publisher,
|
|
835
|
-
packDescription: stickerPack.description,
|
|
836
|
-
stickers: protoStickers,
|
|
837
|
-
thumbnailDirectPath,
|
|
838
|
-
thumbnailEncSha256,
|
|
839
|
-
thumbnailSha256,
|
|
840
|
-
thumbnailHeight,
|
|
841
|
-
thumbnailWidth,
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
// Create and send the pack metadata message
|
|
845
|
-
const contentForSend = { stickerPackMessage: WAProto_1.proto.Message.StickerPackMessage.fromObject(stickerPackObj) };
|
|
846
|
-
const packMsg = (0, Utils_1.generateWAMessageFromContent)(jid, contentForSend, {
|
|
847
|
-
userJid,
|
|
848
|
-
upload: waUploadToServer,
|
|
849
|
-
mediaCache: config.mediaCache,
|
|
850
|
-
options: config.options,
|
|
851
|
-
messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
|
|
852
|
-
...options,
|
|
853
|
-
});
|
|
854
|
-
await relayMessage(jid, packMsg.message, { messageId: packMsg.key.id });
|
|
855
|
-
|
|
856
|
-
// Send each sticker associated with the pack
|
|
857
|
-
let lastMsg = packMsg;
|
|
858
|
-
for (const sticker of stickers) {
|
|
859
|
-
const stickerData = sticker.sticker || sticker.data || sticker.buffer || sticker.image || sticker.url || sticker;
|
|
860
|
-
if (!stickerData) {
|
|
861
|
-
throw new boom_1.Boom('Sticker data not found for sticker: ' + JSON.stringify(sticker), { statusCode: 400 });
|
|
862
|
-
}
|
|
863
|
-
const stickerContent = { sticker: stickerData };
|
|
919
|
+
else {
|
|
864
920
|
let mediaHandle;
|
|
865
|
-
const
|
|
921
|
+
const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
|
|
866
922
|
logger,
|
|
867
923
|
userJid,
|
|
924
|
+
getUrlInfo: text => (0, link_preview_1.getUrlInfo)(text, {
|
|
925
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
926
|
+
fetchOpts: {
|
|
927
|
+
timeout: 3000,
|
|
928
|
+
...axiosOptions || {}
|
|
929
|
+
},
|
|
930
|
+
logger,
|
|
931
|
+
uploadImage: generateHighQualityLinkPreview
|
|
932
|
+
? waUploadToServer
|
|
933
|
+
: undefined
|
|
934
|
+
}),
|
|
935
|
+
getProfilePicUrl: sock.profilePictureUrl,
|
|
868
936
|
upload: async (readStream, opts) => {
|
|
869
937
|
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
870
938
|
mediaHandle = up.handle;
|
|
@@ -875,243 +943,69 @@ const makeMessagesSocket = (config) => {
|
|
|
875
943
|
messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
|
|
876
944
|
...options,
|
|
877
945
|
});
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
946
|
+
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
947
|
+
const isEditMsg = 'edit' in content && !!content.edit;
|
|
948
|
+
const isPinMsg = 'pin' in content && !!content.pin;
|
|
949
|
+
const isKeepMsg = 'keep' in content && content.keep;
|
|
950
|
+
const isPollMessage = 'poll' in content && !!content.poll;
|
|
951
|
+
const isAiMsg = 'ai' in content && !!content.ai;
|
|
952
|
+
const additionalAttributes = {};
|
|
953
|
+
const additionalNodes = [];
|
|
954
|
+
// required for delete
|
|
955
|
+
if (isDeleteMsg) {
|
|
956
|
+
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
957
|
+
if (((0, WABinary_1.isJidGroup)(content.delete.remoteJid) && !content.delete.fromMe) || (0, WABinary_1.isJidNewsletter)(jid)) {
|
|
958
|
+
additionalAttributes.edit = '8';
|
|
884
959
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
await new Promise(resolve => setTimeout(resolve, 800));
|
|
890
|
-
}
|
|
891
|
-
return lastMsg;
|
|
892
|
-
}
|
|
893
|
-
else {
|
|
894
|
-
let mediaHandle;
|
|
895
|
-
const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
|
|
896
|
-
logger,
|
|
897
|
-
userJid,
|
|
898
|
-
getUrlInfo: text => (0, link_preview_1.getUrlInfo)(text, {
|
|
899
|
-
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
900
|
-
fetchOpts: {
|
|
901
|
-
timeout: 3000,
|
|
902
|
-
...axiosOptions || {}
|
|
903
|
-
},
|
|
904
|
-
logger,
|
|
905
|
-
uploadImage: generateHighQualityLinkPreview
|
|
906
|
-
? waUploadToServer
|
|
907
|
-
: undefined
|
|
908
|
-
}),
|
|
909
|
-
getProfilePicUrl: sock.profilePictureUrl,
|
|
910
|
-
upload: async (readStream, opts) => {
|
|
911
|
-
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
912
|
-
mediaHandle = up.handle;
|
|
913
|
-
return up;
|
|
914
|
-
},
|
|
915
|
-
mediaCache: config.mediaCache,
|
|
916
|
-
options: config.options,
|
|
917
|
-
messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
|
|
918
|
-
...options,
|
|
919
|
-
});
|
|
920
|
-
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
921
|
-
const isEditMsg = 'edit' in content && !!content.edit;
|
|
922
|
-
const isPinMsg = 'pin' in content && !!content.pin;
|
|
923
|
-
const isKeepMsg = 'keep' in content && content.keep;
|
|
924
|
-
const isPollMessage = 'poll' in content && !!content.poll;
|
|
925
|
-
const isAiMsg = options.ai === true;
|
|
926
|
-
const additionalAttributes = {};
|
|
927
|
-
const additionalNodes = [];
|
|
928
|
-
// required for delete
|
|
929
|
-
if (isDeleteMsg) {
|
|
930
|
-
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
931
|
-
if (((0, WABinary_1.isJidGroup)(content.delete.remoteJid) && !content.delete.fromMe) || (0, WABinary_1.isJidNewsletter)(jid)) {
|
|
932
|
-
additionalAttributes.edit = '8';
|
|
960
|
+
else {
|
|
961
|
+
additionalAttributes.edit = '7';
|
|
962
|
+
}
|
|
963
|
+
// required for edit message
|
|
933
964
|
}
|
|
934
|
-
else {
|
|
935
|
-
additionalAttributes.edit = '
|
|
965
|
+
else if (isEditMsg) {
|
|
966
|
+
additionalAttributes.edit = (0, WABinary_1.isJidNewsletter)(jid) ? '3' : '1';
|
|
967
|
+
// required for pin message
|
|
936
968
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
additionalAttributes.edit = (0, WABinary_1.isJidNewsletter)(jid) ? '3' : '1';
|
|
941
|
-
// required for pin message
|
|
942
|
-
}
|
|
943
|
-
else if (isPinMsg) {
|
|
944
|
-
additionalAttributes.edit = '2';
|
|
945
|
-
// required for keep message
|
|
946
|
-
}
|
|
947
|
-
else if (isKeepMsg) {
|
|
948
|
-
additionalAttributes.edit = '6';
|
|
949
|
-
// required for polling message
|
|
950
|
-
}
|
|
951
|
-
else if (isPollMessage) {
|
|
952
|
-
additionalNodes.push({
|
|
953
|
-
tag: 'meta',
|
|
954
|
-
attrs: {
|
|
955
|
-
polltype: 'creation'
|
|
956
|
-
},
|
|
957
|
-
});
|
|
958
|
-
// required to display AI icon on message
|
|
959
|
-
}
|
|
960
|
-
else if (isAiMsg) {
|
|
961
|
-
additionalNodes.push({
|
|
962
|
-
attrs: {
|
|
963
|
-
biz_bot: '1'
|
|
964
|
-
},
|
|
965
|
-
tag: "bot"
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
if (mediaHandle) {
|
|
969
|
-
additionalAttributes['media_id'] = mediaHandle;
|
|
970
|
-
}
|
|
971
|
-
if ('cachedGroupMetadata' in options) {
|
|
972
|
-
console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
|
|
973
|
-
}
|
|
974
|
-
// Add AI context if needed
|
|
975
|
-
if (isAiMsg) {
|
|
976
|
-
fullMsg.message.messageContextInfo = {
|
|
977
|
-
...fullMsg.message.messageContextInfo,
|
|
978
|
-
biz_bot: '1'
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
|
|
984
|
-
if (config.emitOwnEvents) {
|
|
985
|
-
process.nextTick(() => {
|
|
986
|
-
processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
return fullMsg;
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
|
|
993
|
-
// Helper function to send missed call note
|
|
994
|
-
const sendMissedCallNote = async (jid, media, options = {}) => {
|
|
995
|
-
const prepared = await prepareWAMessageMedia(media, { upload: sock.waUploadToServer });
|
|
996
|
-
return sock.sendMessage(jid, {
|
|
997
|
-
...prepared,
|
|
998
|
-
ptt: !!media.audio,
|
|
999
|
-
contextInfo: {
|
|
1000
|
-
externalAdReply: {
|
|
1001
|
-
title: 'Missed Call Note',
|
|
1002
|
-
body: options.callInfo?.id ? `From call ${options.callInfo.id}` : 'Recent call'
|
|
969
|
+
else if (isPinMsg) {
|
|
970
|
+
additionalAttributes.edit = '2';
|
|
971
|
+
// required for keep message
|
|
1003
972
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1037
|
-
else {
|
|
1038
|
-
try {
|
|
1039
|
-
const media = await (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
|
|
1040
|
-
if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
1041
|
-
const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];
|
|
1042
|
-
throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });
|
|
1043
|
-
}
|
|
1044
|
-
content.directPath = media.directPath;
|
|
1045
|
-
content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);
|
|
1046
|
-
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
|
|
1047
|
-
}
|
|
1048
|
-
catch (err) {
|
|
1049
|
-
error = err;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
return true;
|
|
1053
|
-
}
|
|
1054
|
-
})
|
|
1055
|
-
]);
|
|
1056
|
-
if (error) {
|
|
1057
|
-
throw error;
|
|
1058
|
-
}
|
|
1059
|
-
ev.emit('messages.update', [
|
|
1060
|
-
{ key: message.key, update: { message: message.message } }
|
|
1061
|
-
]);
|
|
1062
|
-
return message;
|
|
1063
|
-
},
|
|
1064
|
-
sendMessage: async (jid, content, options = {}) => {
|
|
1065
|
-
var _a;
|
|
1066
|
-
// Handle admin-only messages by sending private messages to each admin
|
|
1067
|
-
if (((_a = content.contextInfo) === null || _a === void 0 ? void 0 : _a.isAdminOnly) && (0, WABinary_1.isJidGroup)(jid)) {
|
|
1068
|
-
try {
|
|
1069
|
-
// Get group metadata to find admins
|
|
1070
|
-
const metadata = await sock.groupMetadata(jid);
|
|
1071
|
-
const participants = metadata.participants || [];
|
|
1072
|
-
|
|
1073
|
-
// Find admin JIDs and ensure they are properly formatted
|
|
1074
|
-
const adminJids = participants
|
|
1075
|
-
.filter(p => p.admin === 'admin' || p.admin === 'superadmin')
|
|
1076
|
-
.map(p => (0, WABinary_1.jidNormalizedUser)(p.id));
|
|
1077
|
-
|
|
1078
|
-
if (adminJids.length === 0) {
|
|
1079
|
-
throw new boom_1.Boom('No admins found in group', { statusCode: 400 });
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// Remove isAdminOnly from content to avoid recursion
|
|
1083
|
-
const contentCopy = { ...content };
|
|
1084
|
-
if (contentCopy.contextInfo) {
|
|
1085
|
-
const { isAdminOnly, ...contextInfoRest } = contentCopy.contextInfo;
|
|
1086
|
-
contentCopy.contextInfo = contextInfoRest;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// Add group context to indicate this is from a group
|
|
1090
|
-
contentCopy.contextInfo = {
|
|
1091
|
-
...contentCopy.contextInfo,
|
|
1092
|
-
groupJid: jid,
|
|
1093
|
-
adminOnlyMessage: true
|
|
1094
|
-
};
|
|
1095
|
-
|
|
1096
|
-
// Send private message to each admin
|
|
1097
|
-
const results = [];
|
|
1098
|
-
for (const adminJid of adminJids) {
|
|
1099
|
-
try {
|
|
1100
|
-
const result = await rateLimiter.add(() => sendMessageInternal(adminJid, contentCopy, options));
|
|
1101
|
-
results.push(result);
|
|
1102
|
-
} catch (error) {
|
|
1103
|
-
console.warn(`Failed to send admin-only message to ${adminJid}:`, error);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
return results.length > 0 ? results[0] : null; // Return first successful result
|
|
1108
|
-
} catch (error) {
|
|
1109
|
-
console.error('Failed to send admin-only message:', error);
|
|
1110
|
-
throw error;
|
|
973
|
+
else if (isKeepMsg) {
|
|
974
|
+
additionalAttributes.edit = '6';
|
|
975
|
+
// required for polling message
|
|
976
|
+
}
|
|
977
|
+
else if (isPollMessage) {
|
|
978
|
+
additionalNodes.push({
|
|
979
|
+
tag: 'meta',
|
|
980
|
+
attrs: {
|
|
981
|
+
polltype: 'creation'
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
// required to display AI icon on message
|
|
985
|
+
}
|
|
986
|
+
else if (isAiMsg) {
|
|
987
|
+
additionalNodes.push({
|
|
988
|
+
attrs: {
|
|
989
|
+
biz_bot: '1'
|
|
990
|
+
},
|
|
991
|
+
tag: "bot"
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
if (mediaHandle) {
|
|
995
|
+
additionalAttributes['media_id'] = mediaHandle;
|
|
996
|
+
}
|
|
997
|
+
if ('cachedGroupMetadata' in options) {
|
|
998
|
+
logger.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
|
|
999
|
+
}
|
|
1000
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
|
|
1001
|
+
if (config.emitOwnEvents) {
|
|
1002
|
+
process.nextTick(() => {
|
|
1003
|
+
processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
|
|
1004
|
+
});
|
|
1111
1005
|
}
|
|
1006
|
+
return fullMsg;
|
|
1112
1007
|
}
|
|
1113
|
-
return rateLimiter.add(() => sendMessageInternal(jid, content, options));
|
|
1114
1008
|
}
|
|
1115
1009
|
};
|
|
1116
1010
|
};
|
|
1117
|
-
exports.makeMessagesSocket = makeMessagesSocket;
|
|
1011
|
+
exports.makeMessagesSocket = makeMessagesSocket;
|