plogme 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -10
- package/lib/Socket/messages-send.js +262 -133
- package/lib/Utils/messages.js +1 -23
- package/lib/Utils/rich-message-utils.js +320 -7
- package/lib/index.d.ts +58 -1
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@ import { Boom } from '@hapi/boom';
|
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import { proto } from '../../WAProto/index.js';
|
|
5
5
|
import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
6
|
-
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, buildLinkPreview, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, extractImageThumb, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, makeWhatsAppFlowButton, getUrlFromDirectPath, getWAUploadToServer,
|
|
6
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, buildLinkPreview, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, extractImageThumb, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, makeWhatsAppFlowButton, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, prepareWAMessageMedia, shouldIncludeBizBinaryNode, unixTimestampSeconds } from '../Utils/index.js';
|
|
7
7
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
8
|
-
import { prepareRichMenuMessage, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareHtmlMessage, prepareRichHtmlMessage, attachBotVerificationProofs, hasVerifiableProofs } from '../Utils/rich-message-utils.js';
|
|
8
|
+
import { prepareRichMenuMessage, prepareA2UICommandMenu, prepareAIRichCommandMenu, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareGridImageMessage, prepareVerticalAlbumMessage, prepareVideoGridMessage, prepareHtmlMessage, prepareRichHtmlMessage, attachBotVerificationProofs, hasVerifiableProofs } from '../Utils/rich-message-utils.js';
|
|
9
9
|
import { generateSlotMachineHtml } from '../Utils/games/slot-machine.js';
|
|
10
10
|
import { generateMiniAppHtml } from '../Utils/games/miniapp.js';
|
|
11
11
|
import { generateWebsitePreviewHtml, generateLinkPreviewHtml } from '../Utils/games/website-preview.js';
|
|
@@ -1033,6 +1033,53 @@ export const makeMessagesSocket = (config) => {
|
|
|
1033
1033
|
return msgId;
|
|
1034
1034
|
};
|
|
1035
1035
|
|
|
1036
|
+
/**
|
|
1037
|
+
* Relay a rich envelope and then replay it as a type-14 edit. Some
|
|
1038
|
+
* WhatsApp clients otherwise show a Download prompt before rendering the
|
|
1039
|
+
* AIRich unifiedResponse. The edit is only applied to bot-forwarded rich
|
|
1040
|
+
* envelopes, so ordinary messages are never duplicated accidentally.
|
|
1041
|
+
* Set renderRichResponse: false for callers that explicitly do not want
|
|
1042
|
+
* the compatibility replay.
|
|
1043
|
+
*/
|
|
1044
|
+
const relayRichMessage = async (jid, fullMsg, options = {}) => {
|
|
1045
|
+
const relayOptions = {
|
|
1046
|
+
messageId: fullMsg.key.id,
|
|
1047
|
+
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1048
|
+
statusJidList: options.statusJidList,
|
|
1049
|
+
...options.relayOptions,
|
|
1050
|
+
};
|
|
1051
|
+
await relayMessage(jid, fullMsg.message, relayOptions);
|
|
1052
|
+
|
|
1053
|
+
const richEnvelope = fullMsg.message?.botForwardedMessage?.message?.richResponseMessage;
|
|
1054
|
+
if (options.renderRichResponse !== false && richEnvelope) {
|
|
1055
|
+
const editContent = proto.Message.fromObject({
|
|
1056
|
+
botForwardedMessage: {
|
|
1057
|
+
message: {
|
|
1058
|
+
protocolMessage: {
|
|
1059
|
+
key: {
|
|
1060
|
+
remoteJid: jid,
|
|
1061
|
+
fromMe: true,
|
|
1062
|
+
id: fullMsg.key.id,
|
|
1063
|
+
},
|
|
1064
|
+
type: proto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
|
|
1065
|
+
editedMessage: fullMsg.message,
|
|
1066
|
+
},
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
});
|
|
1070
|
+
const editMessage = generateWAMessageFromContent(jid, editContent, {
|
|
1071
|
+
logger,
|
|
1072
|
+
userJid: authState.creds.me?.id,
|
|
1073
|
+
messageId: generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1074
|
+
});
|
|
1075
|
+
await relayMessage(jid, editMessage.message, {
|
|
1076
|
+
...relayOptions,
|
|
1077
|
+
messageId: editMessage.key.id,
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
return fullMsg;
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1036
1083
|
const getMessageType = (message) => {
|
|
1037
1084
|
if (!message)
|
|
1038
1085
|
return 'text';
|
|
@@ -1229,18 +1276,39 @@ export const makeMessagesSocket = (config) => {
|
|
|
1229
1276
|
},
|
|
1230
1277
|
sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
|
|
1231
1278
|
const { message, messageId } = generateTableContent(title, headers, rows, quoted, options);
|
|
1232
|
-
|
|
1233
|
-
|
|
1279
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1280
|
+
logger,
|
|
1281
|
+
userJid: authState.creds.me?.id,
|
|
1282
|
+
messageId: options.messageId || messageId || generateMessageIDV2(jid),
|
|
1283
|
+
quoted,
|
|
1284
|
+
...options
|
|
1285
|
+
});
|
|
1286
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1287
|
+
return fullMsg;
|
|
1234
1288
|
},
|
|
1235
1289
|
sendList: async (jid, title, items, quoted, options = {}) => {
|
|
1236
1290
|
const { message, messageId } = generateListContent(title, items, quoted, options);
|
|
1237
|
-
|
|
1238
|
-
|
|
1291
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1292
|
+
logger,
|
|
1293
|
+
userJid: authState.creds.me?.id,
|
|
1294
|
+
messageId: options.messageId || messageId || generateMessageIDV2(jid),
|
|
1295
|
+
quoted,
|
|
1296
|
+
...options
|
|
1297
|
+
});
|
|
1298
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1299
|
+
return fullMsg;
|
|
1239
1300
|
},
|
|
1240
1301
|
sendCodeBlock: async (jid, code, quoted, options = {}) => {
|
|
1241
1302
|
const { message, messageId } = generateCodeBlockContent(code, quoted, options);
|
|
1242
|
-
|
|
1243
|
-
|
|
1303
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1304
|
+
logger,
|
|
1305
|
+
userJid: authState.creds.me?.id,
|
|
1306
|
+
messageId: options.messageId || messageId || generateMessageIDV2(jid),
|
|
1307
|
+
quoted,
|
|
1308
|
+
...options
|
|
1309
|
+
});
|
|
1310
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1311
|
+
return fullMsg;
|
|
1244
1312
|
},
|
|
1245
1313
|
sendCarousel: async (jid, cards, options = {}) => {
|
|
1246
1314
|
if (!Array.isArray(cards) || cards.length === 0) {
|
|
@@ -1276,8 +1344,15 @@ export const makeMessagesSocket = (config) => {
|
|
|
1276
1344
|
},
|
|
1277
1345
|
sendLatex: async (jid, quoted, options = {}) => {
|
|
1278
1346
|
const { message, messageId } = generateLatexContent(quoted, { expressions: [], ...options });
|
|
1279
|
-
|
|
1280
|
-
|
|
1347
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1348
|
+
logger,
|
|
1349
|
+
userJid: authState.creds.me?.id,
|
|
1350
|
+
messageId: options.messageId || messageId || generateMessageIDV2(jid),
|
|
1351
|
+
quoted,
|
|
1352
|
+
...options
|
|
1353
|
+
});
|
|
1354
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1355
|
+
return fullMsg;
|
|
1281
1356
|
},
|
|
1282
1357
|
/**
|
|
1283
1358
|
* Send LaTeX as rendered PNG image (like Meta AI does).
|
|
@@ -1305,8 +1380,14 @@ export const makeMessagesSocket = (config) => {
|
|
|
1305
1380
|
headerText,
|
|
1306
1381
|
footer
|
|
1307
1382
|
});
|
|
1308
|
-
|
|
1309
|
-
|
|
1383
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1384
|
+
logger,
|
|
1385
|
+
userJid: authState.creds.me?.id,
|
|
1386
|
+
messageId: options.messageId || messageId || generateMessageIDV2(jid),
|
|
1387
|
+
...options
|
|
1388
|
+
});
|
|
1389
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1390
|
+
return fullMsg;
|
|
1310
1391
|
},
|
|
1311
1392
|
sendMetaAI: async (a, b, c = {}) => {
|
|
1312
1393
|
// baron signature: (jid, text, opts) — README signature: (text, opts)
|
|
@@ -1391,11 +1472,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1391
1472
|
quoted,
|
|
1392
1473
|
...options
|
|
1393
1474
|
});
|
|
1394
|
-
await
|
|
1395
|
-
messageId: generated.key.id,
|
|
1396
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1397
|
-
statusJidList: options.statusJidList
|
|
1398
|
-
});
|
|
1475
|
+
await relayRichMessage(jid, generated, options);
|
|
1399
1476
|
return { ...generated, messageId: generated.key.id };
|
|
1400
1477
|
},
|
|
1401
1478
|
/**
|
|
@@ -1408,9 +1485,15 @@ export const makeMessagesSocket = (config) => {
|
|
|
1408
1485
|
responseId: options.responseId,
|
|
1409
1486
|
forwarded: options.forwarded !== false
|
|
1410
1487
|
});
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
|
|
1488
|
+
const fullMsg = generateWAMessageFromContent(jid, generated.message, {
|
|
1489
|
+
logger,
|
|
1490
|
+
userJid: authState.creds.me?.id,
|
|
1491
|
+
messageId: options.messageId || generated.messageId || generateMessageIDV2(jid),
|
|
1492
|
+
quoted,
|
|
1493
|
+
...options,
|
|
1494
|
+
});
|
|
1495
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1496
|
+
return { ...generated, messageId: fullMsg.key.id };
|
|
1414
1497
|
},
|
|
1415
1498
|
/**
|
|
1416
1499
|
* Replace the generation primitive in an existing rich response.
|
|
@@ -1476,14 +1559,47 @@ export const makeMessagesSocket = (config) => {
|
|
|
1476
1559
|
quoted,
|
|
1477
1560
|
...options
|
|
1478
1561
|
});
|
|
1479
|
-
await
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1562
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1563
|
+
return fullMsg;
|
|
1564
|
+
},
|
|
1565
|
+
/**
|
|
1566
|
+
* Send one tolerant AIRich/unified response containing every supported
|
|
1567
|
+
* section present in `content`. Missing sections are intentionally
|
|
1568
|
+
* ignored by prepareRichResponseMessage instead of causing a partial
|
|
1569
|
+
* response to fail. This is the stable entry point for Plogme/CODY
|
|
1570
|
+
* callers that may receive optional text, links, media, products,
|
|
1571
|
+
* reels, tables, code, latex, or suggestion data.
|
|
1572
|
+
*/
|
|
1573
|
+
sendPlogmeMessage: async (jid, content = {}, quoted, options = {}) => {
|
|
1574
|
+
if (!jid) throw new Boom('sendPlogmeMessage requires a target JID', { statusCode: 400 });
|
|
1575
|
+
if (!content || typeof content !== 'object' || Array.isArray(content)) {
|
|
1576
|
+
throw new Boom('sendPlogmeMessage content must be an object', { statusCode: 400 });
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
const wrapped = prepareRichResponseMessage({
|
|
1580
|
+
...content,
|
|
1581
|
+
// Avoid silently appending the library donation link to a
|
|
1582
|
+
// unified response unless the caller explicitly opts in.
|
|
1583
|
+
noDonation: options.noDonation ?? true,
|
|
1483
1584
|
});
|
|
1585
|
+
const fullMsg = generateWAMessageFromContent(jid, wrapped, {
|
|
1586
|
+
logger,
|
|
1587
|
+
userJid: authState.creds.me?.id,
|
|
1588
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1589
|
+
quoted,
|
|
1590
|
+
...options,
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1594
|
+
|
|
1595
|
+
if (config.emitOwnEvents) {
|
|
1596
|
+
process.nextTick(async () => {
|
|
1597
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1484
1600
|
return fullMsg;
|
|
1485
1601
|
},
|
|
1486
|
-
captureAndResendUnifiedResponse: async (jid, metaAiMsg, quoted) => {
|
|
1602
|
+
captureAndResendUnifiedResponse: async (jid, metaAiMsg, quoted, options = {}) => {
|
|
1487
1603
|
const captured = captureUnifiedResponse(metaAiMsg);
|
|
1488
1604
|
if (!captured) {
|
|
1489
1605
|
throw new Boom('captureAndResendUnifiedResponse: message contains no unifiedResponse data');
|
|
@@ -1495,11 +1611,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1495
1611
|
messageId: generated.messageId || generateMessageIDV2(jid),
|
|
1496
1612
|
quoted
|
|
1497
1613
|
});
|
|
1498
|
-
await
|
|
1499
|
-
messageId: fullMsg.key.id,
|
|
1500
|
-
useCachedGroupMetadata: options?.useCachedGroupMetadata,
|
|
1501
|
-
statusJidList: options?.statusJidList
|
|
1502
|
-
});
|
|
1614
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1503
1615
|
return fullMsg;
|
|
1504
1616
|
},
|
|
1505
1617
|
updateMediaMessage: async (message) => {
|
|
@@ -2039,6 +2151,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
2039
2151
|
}
|
|
2040
2152
|
// ── NORMAL MESSAGE HANDLING ──
|
|
2041
2153
|
else {
|
|
2154
|
+
if ('album' in content) {
|
|
2155
|
+
return socket.sendAlbumMessage(jid, content.album, options);
|
|
2156
|
+
}
|
|
2042
2157
|
const fullMsg = await generateWAMessage(jid, content, {
|
|
2043
2158
|
logger,
|
|
2044
2159
|
userJid,
|
|
@@ -2127,13 +2242,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
2127
2242
|
delete content.ai;
|
|
2128
2243
|
}
|
|
2129
2244
|
|
|
2130
|
-
if ('album' in content) {
|
|
2131
|
-
if (!Array.isArray(content.album) || content.album.length < 2) {
|
|
2132
|
-
throw new Boom('Album requires at least two image or video items', { statusCode: 400 });
|
|
2133
|
-
}
|
|
2134
|
-
fullMsg.message.messageContextInfo ||= {};
|
|
2135
|
-
fullMsg.message.messageContextInfo.messageSecret ||= randomBytes(32);
|
|
2136
|
-
}
|
|
2137
2245
|
await relayMessage(jid, fullMsg.message, {
|
|
2138
2246
|
messageId: fullMsg.key.id,
|
|
2139
2247
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
@@ -2150,55 +2258,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
2150
2258
|
});
|
|
2151
2259
|
}
|
|
2152
2260
|
|
|
2153
|
-
if ('album' in content) {
|
|
2154
|
-
const { delayMs = 1500 } = options;
|
|
2155
|
-
for (const [messageIndex, albumMedia] of content.album.entries()) {
|
|
2156
|
-
// Give the album collection stanza time to register before
|
|
2157
|
-
// its associated media children are relayed. This mirrors
|
|
2158
|
-
// the ordering used by WhatsApp clients and avoids the
|
|
2159
|
-
// children being treated as unrelated media messages.
|
|
2160
|
-
await delay(delayMs);
|
|
2161
|
-
const albumMsg = await generateWAMessage(jid, albumMedia, {
|
|
2162
|
-
logger,
|
|
2163
|
-
userJid,
|
|
2164
|
-
upload: waUploadToServer,
|
|
2165
|
-
mediaCache: config.mediaCache,
|
|
2166
|
-
options: config.options,
|
|
2167
|
-
...options,
|
|
2168
|
-
messageId: generateMessageIDV2(userJid)
|
|
2169
|
-
});
|
|
2170
|
-
if (!hasValidAlbumMedia(normalizeMessageContent(albumMsg.message))) {
|
|
2171
|
-
throw new Boom('Invalid message type for album', { statusCode: 400 });
|
|
2172
|
-
}
|
|
2173
|
-
albumMsg.message.messageContextInfo ||= {};
|
|
2174
|
-
// WhatsApp expects an encrypted context secret on each album
|
|
2175
|
-
// child. Without it, clients may show the album as an
|
|
2176
|
-
// unsupported message or fail to group the media together.
|
|
2177
|
-
albumMsg.message.messageContextInfo.messageSecret ||= randomBytes(32);
|
|
2178
|
-
albumMsg.message.messageContextInfo.messageAssociation = {
|
|
2179
|
-
parentMessageKey: fullMsg.key,
|
|
2180
|
-
// WhatsApp album child messages use association type 1.
|
|
2181
|
-
// The generated MEDIA_ALBUM enum value (63) is a message
|
|
2182
|
-
// category, not the child-association wire value.
|
|
2183
|
-
associationType: 1,
|
|
2184
|
-
messageIndex
|
|
2185
|
-
};
|
|
2186
|
-
await relayMessage(jid, albumMsg.message, {
|
|
2187
|
-
messageId: albumMsg.key.id,
|
|
2188
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2189
|
-
addBizAttributes: isNeedBizAttrs,
|
|
2190
|
-
statusJidList: options.statusJidList,
|
|
2191
|
-
additionalAttributes,
|
|
2192
|
-
additionalNodes
|
|
2193
|
-
});
|
|
2194
|
-
if (config.emitOwnEvents) {
|
|
2195
|
-
process.nextTick(async () => {
|
|
2196
|
-
await messageMutex.mutex(() => upsertMessage(albumMsg, 'append'));
|
|
2197
|
-
});
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
2261
|
return fullMsg;
|
|
2203
2262
|
}
|
|
2204
2263
|
}
|
|
@@ -2216,11 +2275,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2216
2275
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2217
2276
|
...options,
|
|
2218
2277
|
});
|
|
2219
|
-
await
|
|
2220
|
-
messageId: fullMsg.key.id,
|
|
2221
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2222
|
-
statusJidList: options.statusJidList,
|
|
2223
|
-
});
|
|
2278
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2224
2279
|
if (config.emitOwnEvents) {
|
|
2225
2280
|
process.nextTick(async () => {
|
|
2226
2281
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
@@ -2241,11 +2296,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2241
2296
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2242
2297
|
...options,
|
|
2243
2298
|
});
|
|
2244
|
-
await
|
|
2245
|
-
messageId: fullMsg.key.id,
|
|
2246
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2247
|
-
statusJidList: options.statusJidList,
|
|
2248
|
-
});
|
|
2299
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2249
2300
|
if (config.emitOwnEvents) {
|
|
2250
2301
|
process.nextTick(async () => {
|
|
2251
2302
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
@@ -2259,7 +2310,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2259
2310
|
if (!content.copyText) throw new Boom('sendRichTextCopy requires copyText content', { statusCode: 400 });
|
|
2260
2311
|
const wrapped = prepareRichQuoteMessage(content);
|
|
2261
2312
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2262
|
-
await
|
|
2313
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2263
2314
|
return fullMsg;
|
|
2264
2315
|
};
|
|
2265
2316
|
/** Sends Markdown text containing blockquotes in a forwarded GenAI rich response. */
|
|
@@ -2267,7 +2318,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2267
2318
|
if (!jid) throw new Boom('sendMarkdownQuote requires a target JID', { statusCode: 400 });
|
|
2268
2319
|
const wrapped = prepareRichQuoteMessage(content);
|
|
2269
2320
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2270
|
-
await
|
|
2321
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2271
2322
|
return fullMsg;
|
|
2272
2323
|
};
|
|
2273
2324
|
/** Sends Markdown blockquotes followed by a GenAI COPY_TO_CLIPBOARD action. */
|
|
@@ -2276,7 +2327,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2276
2327
|
if (!content.copyText) throw new Boom('sendQuoteCopy requires copyText content', { statusCode: 400 });
|
|
2277
2328
|
const wrapped = prepareRichQuoteMessage(content);
|
|
2278
2329
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2279
|
-
await
|
|
2330
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2280
2331
|
return fullMsg;
|
|
2281
2332
|
};
|
|
2282
2333
|
/**
|
|
@@ -2291,11 +2342,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2291
2342
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2292
2343
|
...options,
|
|
2293
2344
|
});
|
|
2294
|
-
await
|
|
2295
|
-
messageId: fullMsg.key.id,
|
|
2296
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2297
|
-
statusJidList: options.statusJidList,
|
|
2298
|
-
});
|
|
2345
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2299
2346
|
if (config.emitOwnEvents) {
|
|
2300
2347
|
process.nextTick(async () => {
|
|
2301
2348
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
@@ -2315,11 +2362,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2315
2362
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2316
2363
|
...options,
|
|
2317
2364
|
});
|
|
2318
|
-
await
|
|
2319
|
-
messageId: fullMsg.key.id,
|
|
2320
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2321
|
-
statusJidList: options.statusJidList,
|
|
2322
|
-
});
|
|
2365
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2323
2366
|
if (config.emitOwnEvents) {
|
|
2324
2367
|
process.nextTick(async () => {
|
|
2325
2368
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
@@ -2336,11 +2379,53 @@ export const makeMessagesSocket = (config) => {
|
|
|
2336
2379
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2337
2380
|
...options,
|
|
2338
2381
|
});
|
|
2339
|
-
await
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2382
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2383
|
+
if (config.emitOwnEvents) {
|
|
2384
|
+
process.nextTick(async () => {
|
|
2385
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
return fullMsg;
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* Sends the A2UI interactive card/menu format used by WhatsApp's large
|
|
2392
|
+
* command-card presentation. This is intentionally separate from richMenu
|
|
2393
|
+
* because it is an interactiveMessage, not an AIRich unifiedResponse.
|
|
2394
|
+
*/
|
|
2395
|
+
socket.sendA2UICommandMenu = async (jid, content = {}, options = {}) => {
|
|
2396
|
+
if (!jid) throw new Boom('sendA2UICommandMenu requires a target JID', { statusCode: 400 });
|
|
2397
|
+
const { image, ...menuContent } = content || {};
|
|
2398
|
+
let imageMessage;
|
|
2399
|
+
if (image) {
|
|
2400
|
+
const prepared = await prepareWAMessageMedia({ image }, { upload: waUploadToServer });
|
|
2401
|
+
imageMessage = prepared.imageMessage;
|
|
2402
|
+
}
|
|
2403
|
+
const wrapped = prepareA2UICommandMenu({ ...menuContent, imageMessage });
|
|
2404
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
2405
|
+
logger,
|
|
2406
|
+
userJid: authState.creds.me?.id,
|
|
2407
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
2408
|
+
...options,
|
|
2343
2409
|
});
|
|
2410
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, ...options.relayOptions });
|
|
2411
|
+
if (config.emitOwnEvents) {
|
|
2412
|
+
process.nextTick(async () => {
|
|
2413
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
return fullMsg;
|
|
2417
|
+
};
|
|
2418
|
+
/** Sends the manually serialized AIRich command card shown in the screenshot. */
|
|
2419
|
+
socket.sendAIRichCommandMenu = async (jid, content = {}, options = {}) => {
|
|
2420
|
+
if (!jid) throw new Boom('sendAIRichCommandMenu requires a target JID', { statusCode: 400 });
|
|
2421
|
+
const wrapped = prepareAIRichCommandMenu(content);
|
|
2422
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
2423
|
+
logger,
|
|
2424
|
+
userJid: authState.creds.me?.id,
|
|
2425
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
2426
|
+
...options,
|
|
2427
|
+
});
|
|
2428
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2344
2429
|
if (config.emitOwnEvents) {
|
|
2345
2430
|
process.nextTick(async () => {
|
|
2346
2431
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
@@ -2357,7 +2442,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2357
2442
|
botForwardedMessage: { message: { richResponseMessage: { messageType: 1, submessages: [{ messageType: 2, messageText: text }], contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid }, forwardOrigin: 4 } } } }
|
|
2358
2443
|
};
|
|
2359
2444
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2360
|
-
await
|
|
2445
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2361
2446
|
return fullMsg;
|
|
2362
2447
|
};
|
|
2363
2448
|
/** Sends a rich response with experimental BotCommandMetadata. */
|
|
@@ -2366,7 +2451,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2366
2451
|
const { text = 'Bot command test', commandName = '', commandDescription = '', commandPrompt = '', botJid = '867051314767696@bot' } = content;
|
|
2367
2452
|
const wrapped = buildBotRichResponse({ text, botJid, botMetadata: { commandMetadata: { commandName, commandDescription, commandPrompt } } });
|
|
2368
2453
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2369
|
-
await
|
|
2454
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2370
2455
|
return fullMsg;
|
|
2371
2456
|
};
|
|
2372
2457
|
/** Sends a planning/execution-plan rich response with WAProto capabilities. */
|
|
@@ -2389,7 +2474,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2389
2474
|
]
|
|
2390
2475
|
});
|
|
2391
2476
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2392
|
-
await
|
|
2477
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2393
2478
|
return fullMsg;
|
|
2394
2479
|
};
|
|
2395
2480
|
/** Sends a rich response with experimental BotResolvedToolCallMetadata. */
|
|
@@ -2408,7 +2493,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2408
2493
|
}
|
|
2409
2494
|
});
|
|
2410
2495
|
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
2411
|
-
await
|
|
2496
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2412
2497
|
return fullMsg;
|
|
2413
2498
|
};
|
|
2414
2499
|
socket.sendStatus = async (content, options = {}) => {
|
|
@@ -2611,25 +2696,73 @@ export const makeMessagesSocket = (config) => {
|
|
|
2611
2696
|
|
|
2612
2697
|
|
|
2613
2698
|
/**
|
|
2614
|
-
* Sends
|
|
2615
|
-
*
|
|
2616
|
-
*
|
|
2699
|
+
* Sends a full-width vertical AIRich album. Unlike the legacy
|
|
2700
|
+
* albumMessage collection or compact GRID_IMAGE layout, this is one
|
|
2701
|
+
* forwarded unified-response message with one large media item per row.
|
|
2617
2702
|
*/
|
|
2618
2703
|
socket.sendAlbumMessage = async (jid, medias = [], options = {}) => {
|
|
2619
2704
|
if (!jid) throw new Boom('sendAlbumMessage requires a target JID', { statusCode: 400 });
|
|
2620
2705
|
if (!Array.isArray(medias) || medias.length < 2) {
|
|
2621
2706
|
throw new Boom('sendAlbumMessage requires at least two media items', { statusCode: 400 });
|
|
2622
2707
|
}
|
|
2623
|
-
const
|
|
2624
|
-
const
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2708
|
+
const albumItems = medias.map((media) => {
|
|
2709
|
+
const raw = media?.type && media?.data ? media.data : (media?.image ?? media);
|
|
2710
|
+
if (media?.video || media?.type === 'video') {
|
|
2711
|
+
const video = media?.video || raw;
|
|
2712
|
+
const sourceUrl = typeof video === 'string' ? video : (video?.url || video?.videoUrl || '');
|
|
2713
|
+
if (!sourceUrl) throw new Boom('Each video album item requires a video URL', { statusCode: 400 });
|
|
2714
|
+
return { type: 'video', video: { ...video, url: sourceUrl } };
|
|
2629
2715
|
}
|
|
2630
|
-
return
|
|
2716
|
+
return {
|
|
2717
|
+
type: 'image',
|
|
2718
|
+
image: raw
|
|
2719
|
+
};
|
|
2720
|
+
});
|
|
2721
|
+
const wrapped = prepareVerticalAlbumMessage({
|
|
2722
|
+
items: albumItems,
|
|
2723
|
+
verificationMetadata: options.verificationMetadata
|
|
2631
2724
|
});
|
|
2632
|
-
|
|
2725
|
+
const fullMsg = generateWAMessageFromContent(jid, wrapped, {
|
|
2726
|
+
logger,
|
|
2727
|
+
userJid: authState.creds.me?.id,
|
|
2728
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
2729
|
+
...options
|
|
2730
|
+
});
|
|
2731
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2732
|
+
if (config.emitOwnEvents) {
|
|
2733
|
+
process.nextTick(async () => {
|
|
2734
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
return fullMsg;
|
|
2738
|
+
};
|
|
2739
|
+
|
|
2740
|
+
/**
|
|
2741
|
+
* Sends a unified-response video grid. This uses GenAIVideoPrimitive items
|
|
2742
|
+
* inside GenAIGridLayoutViewModel; it is not the image-only GRID_IMAGE type.
|
|
2743
|
+
*/
|
|
2744
|
+
socket.sendVideoGridMessage = async (jid, videos = [], options = {}) => {
|
|
2745
|
+
if (!jid) throw new Boom('sendVideoGridMessage requires a target JID', { statusCode: 400 });
|
|
2746
|
+
if (!Array.isArray(videos) || videos.length < 2) {
|
|
2747
|
+
throw new Boom('sendVideoGridMessage requires at least two videos', { statusCode: 400 });
|
|
2748
|
+
}
|
|
2749
|
+
const wrapped = prepareVideoGridMessage({
|
|
2750
|
+
videos,
|
|
2751
|
+
verificationMetadata: options.verificationMetadata
|
|
2752
|
+
});
|
|
2753
|
+
const fullMsg = generateWAMessageFromContent(jid, wrapped, {
|
|
2754
|
+
logger,
|
|
2755
|
+
userJid: authState.creds.me?.id,
|
|
2756
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
2757
|
+
...options
|
|
2758
|
+
});
|
|
2759
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2760
|
+
if (config.emitOwnEvents) {
|
|
2761
|
+
process.nextTick(async () => {
|
|
2762
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
return fullMsg;
|
|
2633
2766
|
};
|
|
2634
2767
|
|
|
2635
2768
|
|
|
@@ -2688,11 +2821,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
2688
2821
|
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2689
2822
|
...options
|
|
2690
2823
|
});
|
|
2691
|
-
await
|
|
2692
|
-
messageId: fullMsg.key.id,
|
|
2693
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2694
|
-
statusJidList: options.statusJidList
|
|
2695
|
-
});
|
|
2824
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2696
2825
|
return fullMsg;
|
|
2697
2826
|
};
|
|
2698
2827
|
|
package/lib/Utils/messages.js
CHANGED
|
@@ -1061,30 +1061,8 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1061
1061
|
};
|
|
1062
1062
|
delete m.orderMessage.orderText;
|
|
1063
1063
|
}
|
|
1064
|
-
// Lia@Changes 31-01-26 --- Add support for album messages
|
|
1065
1064
|
else if (hasNonNullishProperty(message, 'album')) {
|
|
1066
|
-
|
|
1067
|
-
throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
1068
|
-
}
|
|
1069
|
-
let videoCount = 0;
|
|
1070
|
-
for (let i = 0; i < message.album.length; i++) {
|
|
1071
|
-
if (message.album[i].video)
|
|
1072
|
-
videoCount++;
|
|
1073
|
-
}
|
|
1074
|
-
;
|
|
1075
|
-
let imageCount = 0;
|
|
1076
|
-
for (let i = 0; i < message.album.length; i++) {
|
|
1077
|
-
if (message.album[i].image)
|
|
1078
|
-
imageCount++;
|
|
1079
|
-
}
|
|
1080
|
-
;
|
|
1081
|
-
if ((videoCount + imageCount) < 2) {
|
|
1082
|
-
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
1083
|
-
}
|
|
1084
|
-
m.albumMessage = {
|
|
1085
|
-
expectedImageCount: imageCount,
|
|
1086
|
-
expectedVideoCount: videoCount
|
|
1087
|
-
};
|
|
1065
|
+
throw new Boom('Legacy albumMessage is removed; use socket.sendAlbumMessage() for an AIRich GRID_IMAGE album', { statusCode: 400 });
|
|
1088
1066
|
}
|
|
1089
1067
|
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
1090
1068
|
m.protocolMessage = {
|