plogme 2.0.3 → 2.0.5

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.
@@ -5,7 +5,7 @@ import { proto } from '../../WAProto/index.js';
5
5
  import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
6
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, prepareGridImageMessage, prepareHtmlMessage, prepareRichHtmlMessage, attachBotVerificationProofs, hasVerifiableProofs } from '../Utils/rich-message-utils.js';
8
+ import { prepareRichMenuMessage, prepareA2UIMessage, prepareA2UICommandMenu, prepareAIRichCommandMenu, prepareSocialEntityMessage, prepareInstagramProfileMessage, 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';
@@ -1051,7 +1051,7 @@ export const makeMessagesSocket = (config) => {
1051
1051
  await relayMessage(jid, fullMsg.message, relayOptions);
1052
1052
 
1053
1053
  const richEnvelope = fullMsg.message?.botForwardedMessage?.message?.richResponseMessage;
1054
- if (options.renderRichResponse !== false && richEnvelope) {
1054
+ if (options.renderRichResponse !== false && (richEnvelope || options.forceRichEdit || fullMsg.message?.interactiveMessage?.bloksWidget)) {
1055
1055
  const editContent = proto.Message.fromObject({
1056
1056
  botForwardedMessage: {
1057
1057
  message: {
@@ -1276,18 +1276,39 @@ export const makeMessagesSocket = (config) => {
1276
1276
  },
1277
1277
  sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
1278
1278
  const { message, messageId } = generateTableContent(title, headers, rows, quoted, options);
1279
- await relayMessage(jid, message, { messageId });
1280
- return { message, messageId };
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;
1281
1288
  },
1282
1289
  sendList: async (jid, title, items, quoted, options = {}) => {
1283
1290
  const { message, messageId } = generateListContent(title, items, quoted, options);
1284
- await relayMessage(jid, message, { messageId });
1285
- return { message, messageId };
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;
1286
1300
  },
1287
1301
  sendCodeBlock: async (jid, code, quoted, options = {}) => {
1288
1302
  const { message, messageId } = generateCodeBlockContent(code, quoted, options);
1289
- await relayMessage(jid, message, { messageId });
1290
- return { message, messageId };
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;
1291
1312
  },
1292
1313
  sendCarousel: async (jid, cards, options = {}) => {
1293
1314
  if (!Array.isArray(cards) || cards.length === 0) {
@@ -1323,8 +1344,15 @@ export const makeMessagesSocket = (config) => {
1323
1344
  },
1324
1345
  sendLatex: async (jid, quoted, options = {}) => {
1325
1346
  const { message, messageId } = generateLatexContent(quoted, { expressions: [], ...options });
1326
- await relayMessage(jid, message, { messageId });
1327
- return { message, messageId };
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;
1328
1356
  },
1329
1357
  /**
1330
1358
  * Send LaTeX as rendered PNG image (like Meta AI does).
@@ -1352,8 +1380,14 @@ export const makeMessagesSocket = (config) => {
1352
1380
  headerText,
1353
1381
  footer
1354
1382
  });
1355
- await relayMessage(jid, message, { messageId });
1356
- return { message, messageId };
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;
1357
1391
  },
1358
1392
  sendMetaAI: async (a, b, c = {}) => {
1359
1393
  // baron signature: (jid, text, opts) — README signature: (text, opts)
@@ -2353,6 +2387,103 @@ export const makeMessagesSocket = (config) => {
2353
2387
  }
2354
2388
  return fullMsg;
2355
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,
2409
+ });
2410
+ await relayRichMessage(jid, fullMsg, { ...options, forceRichEdit: true });
2411
+ if (config.emitOwnEvents) {
2412
+ process.nextTick(async () => {
2413
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
2414
+ });
2415
+ }
2416
+ return fullMsg;
2417
+ };
2418
+ /** Send arbitrary A2UI/interactive payloads; sendA2UICommandMenu is a preset wrapper. */
2419
+ socket.sendA2UIMessage = async (jid, content = {}, options = {}) => {
2420
+ if (!jid) throw new Boom('sendA2UIMessage requires a target JID', { statusCode: 400 });
2421
+ const { image, ...payload } = content || {};
2422
+ let imageMessage;
2423
+ if (image) {
2424
+ const prepared = await prepareWAMessageMedia({ image }, { upload: waUploadToServer });
2425
+ imageMessage = prepared.imageMessage;
2426
+ }
2427
+ const wrapped = prepareA2UIMessage({ ...payload, imageMessage });
2428
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2429
+ logger,
2430
+ userJid: authState.creds.me?.id,
2431
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
2432
+ ...options,
2433
+ });
2434
+ await relayRichMessage(jid, fullMsg, { ...options, forceRichEdit: true });
2435
+ if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
2436
+ return fullMsg;
2437
+ };
2438
+
2439
+ /** Sends the manually serialized AIRich command card shown in the screenshot. */
2440
+ socket.sendAIRichCommandMenu = async (jid, content = {}, options = {}) => {
2441
+ if (!jid) throw new Boom('sendAIRichCommandMenu requires a target JID', { statusCode: 400 });
2442
+ const wrapped = prepareAIRichCommandMenu(content);
2443
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2444
+ logger,
2445
+ userJid: authState.creds.me?.id,
2446
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
2447
+ ...options,
2448
+ });
2449
+ await relayRichMessage(jid, fullMsg, options);
2450
+ if (config.emitOwnEvents) {
2451
+ process.nextTick(async () => {
2452
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
2453
+ });
2454
+ }
2455
+ return fullMsg;
2456
+ };
2457
+ /** Send a provider-neutral AIRich social/entity result card. */
2458
+ socket.sendSocialEntity = async (jid, content = {}, options = {}) => {
2459
+ if (!jid) throw new Boom('sendSocialEntity requires a target JID', { statusCode: 400 });
2460
+ const wrapped = prepareSocialEntityMessage(content);
2461
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2462
+ logger,
2463
+ userJid: authState.creds.me?.id,
2464
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
2465
+ ...options,
2466
+ });
2467
+ await relayRichMessage(jid, fullMsg, options);
2468
+ if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
2469
+ return fullMsg;
2470
+ };
2471
+
2472
+ /** Send the primary Instagram profile result using the universal social/entity API. */
2473
+ socket.sendInstagramProfile = async (jid, content = {}, options = {}) => {
2474
+ if (!jid) throw new Boom('sendInstagramProfile requires a target JID', { statusCode: 400 });
2475
+ const wrapped = prepareInstagramProfileMessage(content);
2476
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2477
+ logger,
2478
+ userJid: authState.creds.me?.id,
2479
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
2480
+ ...options,
2481
+ });
2482
+ await relayRichMessage(jid, fullMsg, options);
2483
+ if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
2484
+ return fullMsg;
2485
+ };
2486
+
2356
2487
  /** Sends a rich response with experimental BotInfrastructureDiagnostics metadata. */
2357
2488
  socket.sendBotDiagnostics = async (jid, content = {}, options = {}) => {
2358
2489
  if (!jid) throw new Boom('sendBotDiagnostics requires a target JID', { statusCode: 400 });
@@ -2616,38 +2747,58 @@ export const makeMessagesSocket = (config) => {
2616
2747
 
2617
2748
 
2618
2749
  /**
2619
- * Sends an AIRich GRID_IMAGE album. Unlike the legacy albumMessage
2620
- * collection, this is one forwarded unified-response message containing
2621
- * the requested image primitives.
2750
+ * Sends a full-width vertical AIRich album. Unlike the legacy
2751
+ * albumMessage collection or compact GRID_IMAGE layout, this is one
2752
+ * forwarded unified-response message with one large media item per row.
2622
2753
  */
2623
2754
  socket.sendAlbumMessage = async (jid, medias = [], options = {}) => {
2624
2755
  if (!jid) throw new Boom('sendAlbumMessage requires a target JID', { statusCode: 400 });
2625
2756
  if (!Array.isArray(medias) || medias.length < 2) {
2626
2757
  throw new Boom('sendAlbumMessage requires at least two media items', { statusCode: 400 });
2627
2758
  }
2628
- const imageUrls = medias.map((media) => {
2759
+ const albumItems = medias.map((media) => {
2629
2760
  const raw = media?.type && media?.data ? media.data : (media?.image ?? media);
2630
2761
  if (media?.video || media?.type === 'video') {
2631
- throw new Boom('GRID_IMAGE albums support images only', { statusCode: 400 });
2632
- }
2633
- const sourceUrl = typeof raw === 'string' ? raw : (raw?.sourceUrl || raw?.url || '');
2634
- if (!sourceUrl) {
2635
- throw new Boom('Each GRID_IMAGE album item requires an image URL', { statusCode: 400 });
2762
+ const video = media?.video || raw;
2763
+ const sourceUrl = typeof video === 'string' ? video : (video?.url || video?.videoUrl || '');
2764
+ if (!sourceUrl) throw new Boom('Each video album item requires a video URL', { statusCode: 400 });
2765
+ return { type: 'video', video: { ...video, url: sourceUrl } };
2636
2766
  }
2637
2767
  return {
2638
- sourceUrl,
2639
- imagePreviewUrl: raw?.imagePreviewUrl || raw?.previewUrl || sourceUrl,
2640
- imageHighResUrl: raw?.imageHighResUrl || raw?.highResUrl || raw?.url || sourceUrl,
2641
- darkModePreviewUrl: raw?.darkModePreviewUrl,
2642
- darkModeHighResUrl: raw?.darkModeHighResUrl,
2643
- width: raw?.width || media?.width || 600,
2644
- height: raw?.height || media?.height || 400,
2645
- darkWidth: raw?.darkWidth || media?.darkWidth,
2646
- darkHeight: raw?.darkHeight || media?.darkHeight
2768
+ type: 'image',
2769
+ image: raw
2647
2770
  };
2648
2771
  });
2649
- const wrapped = prepareGridImageMessage({
2650
- imageUrls,
2772
+ const wrapped = prepareVerticalAlbumMessage({
2773
+ items: albumItems,
2774
+ verificationMetadata: options.verificationMetadata
2775
+ });
2776
+ const fullMsg = generateWAMessageFromContent(jid, wrapped, {
2777
+ logger,
2778
+ userJid: authState.creds.me?.id,
2779
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
2780
+ ...options
2781
+ });
2782
+ await relayRichMessage(jid, fullMsg, options);
2783
+ if (config.emitOwnEvents) {
2784
+ process.nextTick(async () => {
2785
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
2786
+ });
2787
+ }
2788
+ return fullMsg;
2789
+ };
2790
+
2791
+ /**
2792
+ * Sends a unified-response video grid. This uses GenAIVideoPrimitive items
2793
+ * inside GenAIGridLayoutViewModel; it is not the image-only GRID_IMAGE type.
2794
+ */
2795
+ socket.sendVideoGridMessage = async (jid, videos = [], options = {}) => {
2796
+ if (!jid) throw new Boom('sendVideoGridMessage requires a target JID', { statusCode: 400 });
2797
+ if (!Array.isArray(videos) || videos.length < 2) {
2798
+ throw new Boom('sendVideoGridMessage requires at least two videos', { statusCode: 400 });
2799
+ }
2800
+ const wrapped = prepareVideoGridMessage({
2801
+ videos,
2651
2802
  verificationMetadata: options.verificationMetadata
2652
2803
  });
2653
2804
  const fullMsg = generateWAMessageFromContent(jid, wrapped, {
@@ -2692,11 +2843,7 @@ export const makeMessagesSocket = (config) => {
2692
2843
  messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2693
2844
  ...options
2694
2845
  });
2695
- await relayMessage(jid, fullMsg.message, {
2696
- messageId: fullMsg.key.id,
2697
- useCachedGroupMetadata: options.useCachedGroupMetadata,
2698
- statusJidList: options.statusJidList
2699
- });
2846
+ await relayRichMessage(jid, fullMsg, { ...options, forceRichEdit: true });
2700
2847
  return fullMsg;
2701
2848
  };
2702
2849
 
@@ -1592,7 +1592,16 @@ export const generateWAMessageContent = async (message, options) => {
1592
1592
  m = { lottieStickerMessage: { message: m } };
1593
1593
  }
1594
1594
  else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
1595
- m = { viewOnceMessage: { message: m } };
1595
+ // Text view-once clients expect the flag on the inner
1596
+ // ExtendedTextMessage. Media continues to use the standard outer
1597
+ // viewOnceMessage envelope.
1598
+ if (m.extendedTextMessage) {
1599
+ m.extendedTextMessage.viewOnce = true;
1600
+ }
1601
+ else {
1602
+ m = { viewOnceMessage: { message: m } };
1603
+ }
1604
+ delete message.viewOnce;
1596
1605
  }
1597
1606
  // Lia@Changes 03-02-26 --- Add "viewOnceV2" boolean to wrap message into viewOnceMessageV2 like "viewOnce"
1598
1607
  else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {