plogme 2.0.0 → 2.0.2

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.
@@ -1863,6 +1863,7 @@ export const makeChatsSocket = (config) => {
1863
1863
  getUserDisclosures,
1864
1864
  acceptTosNotice,
1865
1865
  reportSpam,
1866
+ toggleGroupHistory,
1866
1867
  getOptOutList,
1867
1868
  signPrivateCredential,
1868
1869
  getPushConfig,
@@ -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, hasValidAlbumMedia, 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 } from '../Utils/rich-message-utils.js';
8
+ import { prepareRichMenuMessage, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, 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';
@@ -16,6 +16,7 @@ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, res
16
16
  import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinaryNode, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isJidNewsletter, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
17
17
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
18
18
  import { captureUnifiedResponse, generateCodeBlockContent, generateLatexContent, generateListContent, generateTableContent, generateUnifiedResponseContent } from '../Utils/message-composer.js';
19
+ import { BotCapabilityType, buildBotRichResponse } from '../Utils/bot-capabilities.js';
19
20
  import { Baron } from './interactive-handler.js';
20
21
  import { collectMetaAIBotParticipantJids, isMetaAIBotResponse, META_AI_BOT_JID } from './aigroups.js';
21
22
  import { makeUsernameSocket } from './username.js';
@@ -554,7 +555,18 @@ export const makeMessagesSocket = (config) => {
554
555
  };
555
556
  }
556
557
  const applyForwarded = m => {
558
+ // Auto-inject trusted verification proofs configured once on the socket
559
+ // (config.metaAiRendering.verificationMetadata). Per-message proofs win:
560
+ // an envelope that already carries verificationMetadata is left untouched.
561
+ // The second target covers edited rich envelopes (protocolMessage.editedMessage,
562
+ // used by updateRichGeneration) so an in-place rich update keeps its proofs too.
563
+ const edited = m?.botForwardedMessage?.message?.protocolMessage?.editedMessage;
557
564
  const rich = m?.botForwardedMessage?.message?.richResponseMessage || m?.richResponseMessage;
565
+ if (!rich && !edited) return m;
566
+ if (hasVerifiableProofs(config?.metaAiRendering?.verificationMetadata)) {
567
+ attachBotVerificationProofs(m, config.metaAiRendering.verificationMetadata);
568
+ attachBotVerificationProofs(edited, config.metaAiRendering.verificationMetadata);
569
+ }
558
570
  if (!rich) return m;
559
571
  rich.contextInfo = { ...rich.contextInfo, isForwarded: true, forwardOrigin: 4 };
560
572
  return m.botForwardedMessage ? m : { botForwardedMessage: { message: m } };
@@ -1371,10 +1383,20 @@ export const makeMessagesSocket = (config) => {
1371
1383
  },
1372
1384
  // Additive Nexus-compatible AI-rich helpers. Existing Gen4/table/grid/carousel APIs remain unchanged.
1373
1385
  sendRichMedia: async (jid, content = {}, quoted, options = {}) => {
1374
- const message = prepareRichResponseMessage({ ...content, noDonation: options.noDonation ?? true });
1375
- const messageId = generateMessageIDV2(jid);
1376
- await relayMessage(jid, message, { messageId });
1377
- return { message, messageId };
1386
+ const contentMessage = prepareRichResponseMessage({ ...content, noDonation: options.noDonation ?? true });
1387
+ const generated = generateWAMessageFromContent(jid, contentMessage, {
1388
+ logger,
1389
+ userJid: authState.creds.me?.id,
1390
+ messageId: options.messageId || generateMessageIDV2(jid),
1391
+ quoted,
1392
+ ...options
1393
+ });
1394
+ await relayMessage(jid, generated.message, {
1395
+ messageId: generated.key.id,
1396
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1397
+ statusJidList: options.statusJidList
1398
+ });
1399
+ return { ...generated, messageId: generated.key.id };
1378
1400
  },
1379
1401
  /**
1380
1402
  * Send a native AI image/video generation card. The returned messageId
@@ -1447,18 +1469,38 @@ export const makeMessagesSocket = (config) => {
1447
1469
  else {
1448
1470
  throw new Boom('sendRichAIResponse: unknown content type — expected table, list, codeBlock or latex');
1449
1471
  }
1450
- const { message, messageId } = generated;
1451
- await relayMessage(jid, message, { messageId });
1452
- return { message, messageId };
1472
+ const fullMsg = generateWAMessageFromContent(jid, generated.message, {
1473
+ logger,
1474
+ userJid: authState.creds.me?.id,
1475
+ messageId: options.messageId || generated.messageId || generateMessageIDV2(jid),
1476
+ quoted,
1477
+ ...options
1478
+ });
1479
+ await relayMessage(jid, fullMsg.message, {
1480
+ messageId: fullMsg.key.id,
1481
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1482
+ statusJidList: options.statusJidList
1483
+ });
1484
+ return fullMsg;
1453
1485
  },
1454
1486
  captureAndResendUnifiedResponse: async (jid, metaAiMsg, quoted) => {
1455
1487
  const captured = captureUnifiedResponse(metaAiMsg);
1456
1488
  if (!captured) {
1457
1489
  throw new Boom('captureAndResendUnifiedResponse: message contains no unifiedResponse data');
1458
1490
  }
1459
- const { message, messageId } = generateUnifiedResponseContent(quoted, captured);
1460
- await relayMessage(jid, message, { messageId });
1461
- return { message, messageId };
1491
+ const generated = generateUnifiedResponseContent(quoted, captured);
1492
+ const fullMsg = generateWAMessageFromContent(jid, generated.message, {
1493
+ logger,
1494
+ userJid: authState.creds.me?.id,
1495
+ messageId: generated.messageId || generateMessageIDV2(jid),
1496
+ quoted
1497
+ });
1498
+ await relayMessage(jid, fullMsg.message, {
1499
+ messageId: fullMsg.key.id,
1500
+ useCachedGroupMetadata: options?.useCachedGroupMetadata,
1501
+ statusJidList: options?.statusJidList
1502
+ });
1503
+ return fullMsg;
1462
1504
  },
1463
1505
  updateMediaMessage: async (message) => {
1464
1506
  const content = assertMediaContent(message.message);
@@ -2322,10 +2364,30 @@ export const makeMessagesSocket = (config) => {
2322
2364
  socket.sendBotCommand = async (jid, content = {}, options = {}) => {
2323
2365
  if (!jid) throw new Boom('sendBotCommand requires a target JID', { statusCode: 400 });
2324
2366
  const { text = 'Bot command test', commandName = '', commandDescription = '', commandPrompt = '', botJid = '867051314767696@bot' } = content;
2325
- const wrapped = {
2326
- messageContextInfo: { botMetadata: { commandMetadata: { commandName, commandDescription, commandPrompt } } },
2327
- botForwardedMessage: { message: { richResponseMessage: { messageType: 1, submessages: [{ messageType: 2, messageText: text }], contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid }, forwardOrigin: 4 } } } }
2328
- };
2367
+ const wrapped = buildBotRichResponse({ text, botJid, botMetadata: { commandMetadata: { commandName, commandDescription, commandPrompt } } });
2368
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
2369
+ await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, statusJidList: options.statusJidList });
2370
+ return fullMsg;
2371
+ };
2372
+ /** Sends a planning/execution-plan rich response with WAProto capabilities. */
2373
+ socket.sendBotPlanning = async (jid, content = {}, options = {}) => {
2374
+ if (!jid) throw new Boom('sendBotPlanning requires a target JID', { statusCode: 400 });
2375
+ const { text = 'Execution plan', steps = [], queryPlan = true, botJid = '867051314767696@bot' } = content;
2376
+ const submessages = [
2377
+ { messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT, messageText: text },
2378
+ ...steps.map(step => ({
2379
+ messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT,
2380
+ messageText: typeof step === 'string' ? step : `${step.title || step.name || 'Step'}${step.body ? `: ${step.body}` : ''}`
2381
+ }))
2382
+ ];
2383
+ const wrapped = buildBotRichResponse({
2384
+ botJid,
2385
+ submessages,
2386
+ capabilities: [
2387
+ BotCapabilityType.AGENTIC_PLANNING,
2388
+ ...(queryPlan ? [BotCapabilityType.QUERY_PLAN] : [])
2389
+ ]
2390
+ });
2329
2391
  const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
2330
2392
  await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, statusJidList: options.statusJidList });
2331
2393
  return fullMsg;
@@ -2334,10 +2396,17 @@ export const makeMessagesSocket = (config) => {
2334
2396
  socket.sendBotToolResult = async (jid, content = {}, options = {}) => {
2335
2397
  if (!jid) throw new Boom('sendBotToolResult requires a target JID', { statusCode: 400 });
2336
2398
  const { text = 'Bot tool result test', toolCallId = `tool-${Date.now()}`, resolutionData, resolutionDataSerialized, botJid = '867051314767696@bot' } = content;
2337
- const wrapped = {
2338
- messageContextInfo: { botMetadata: { resolvedToolCallMetadata: { toolCallId, resolutionDataSerialized: resolutionDataSerialized ?? JSON.stringify(resolutionData ?? {}) } } },
2339
- botForwardedMessage: { message: { richResponseMessage: { messageType: 1, submessages: [{ messageType: 2, messageText: text }], contextInfo: { isForwarded: true, forwardingScore: 1, forwardedAiBotMessageInfo: { botJid }, forwardOrigin: 4 } } } }
2340
- };
2399
+ const wrapped = buildBotRichResponse({
2400
+ text,
2401
+ botJid,
2402
+ capabilities: [BotCapabilityType.AGENTIC_PLANNING],
2403
+ botMetadata: {
2404
+ resolvedToolCallMetadata: {
2405
+ toolCallId,
2406
+ resolutionDataSerialized: resolutionDataSerialized ?? JSON.stringify(resolutionData ?? {})
2407
+ }
2408
+ }
2409
+ });
2341
2410
  const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
2342
2411
  await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, statusJidList: options.statusJidList });
2343
2412
  return fullMsg;
@@ -2375,6 +2444,11 @@ export const makeMessagesSocket = (config) => {
2375
2444
  cards: normalizedCards
2376
2445
  }, options);
2377
2446
  };
2447
+ /**
2448
+ * Compatibility entry point for the former Gen4/grid menu.
2449
+ * This is additive: sendRichButtonGrid remains unchanged for existing callers.
2450
+ */
2451
+ socket.HoldOn = async (jid, grid, options = {}) => socket.sendRichButtonGrid(jid, grid, options);
2378
2452
  socket.sendWhatsAppFlow = async (jid, flow, options = {}) => {
2379
2453
  const { text, footer, image, caption, ...flowOptions } = flow || {};
2380
2454
  const button = makeWhatsAppFlowButton(flowOptions);
@@ -1,3 +1,4 @@
1
+ import { Boom } from '@hapi/boom';
1
2
  import { proto } from '../../WAProto/index.js';
2
3
  import { QueryIds, XWAPaths } from '../Types/index.js';
3
4
  import { generateProfilePicture } from '../Utils/messages-media.js';
@@ -34,6 +35,15 @@ const parseNewsletterMetadata = (result) => {
34
35
  }
35
36
  return null;
36
37
  };
38
+ const normalizeNewsletterInviteCode = (value) => {
39
+ if (typeof value !== 'string' || !value.trim()) {
40
+ throw new Boom('newsletterGetInviteInfo requires a non-empty invite code or URL', { statusCode: 400 });
41
+ }
42
+ return value.trim()
43
+ .replace(/^https?:\/\/(?:www\.)?whatsapp\.com\/channel\//i, '')
44
+ .replace(/^https?:\/\/wa\.me\/channel\//i, '')
45
+ .split(/[/?#]/, 1)[0];
46
+ };
37
47
  export const makeNewsletterSocket = (config) => {
38
48
  const sock = makeAIGroupsSocket(config);
39
49
  const { query, generateMessageTag } = sock;
@@ -72,6 +82,12 @@ export const makeNewsletterSocket = (config) => {
72
82
  return executeWMexQuery({}, QueryIds.SUBSCRIBED, XWAPaths.xwa2_newsletter_subscribed);
73
83
  },
74
84
  newsletterMetadata: async (type, key) => {
85
+ if (typeof type !== 'string' || !type.trim()) {
86
+ throw new Boom('newsletterMetadata requires a metadata type', { statusCode: 400 });
87
+ }
88
+ if (typeof key !== 'string' || !key.trim()) {
89
+ throw new Boom('newsletterMetadata requires a metadata key', { statusCode: 400 });
90
+ }
75
91
  const variables = {
76
92
  fetch_creation_time: true,
77
93
  fetch_full_image: true,
@@ -84,6 +100,14 @@ export const makeNewsletterSocket = (config) => {
84
100
  const result = await executeWMexQuery(variables, QueryIds.METADATA, XWAPaths.xwa2_newsletter_metadata);
85
101
  return parseNewsletterMetadata(result);
86
102
  },
103
+ newsletterGetInviteInfo: async (inviteCodeOrUrl) => {
104
+ const inviteCode = normalizeNewsletterInviteCode(inviteCodeOrUrl);
105
+ return executeWMexQuery(
106
+ { input: { key: inviteCode, type: 'INVITE' }, fetch_creation_time: true, fetch_full_image: true, fetch_viewer_metadata: true },
107
+ QueryIds.METADATA,
108
+ XWAPaths.xwa2_newsletter_metadata
109
+ ).then(parseNewsletterMetadata);
110
+ },
87
111
  newsletterFollow: (jid) => {
88
112
  return executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2);
89
113
  },
@@ -110,11 +134,21 @@ export const makeNewsletterSocket = (config) => {
110
134
  return await newsletterUpdate(jid, { picture: '' });
111
135
  },
112
136
  newsletterReactMessage: async (jid, serverId, reaction) => {
137
+ if (typeof jid !== 'string' || !jid.trim()) {
138
+ throw new Boom('newsletterReactMessage requires a newsletter JID', { statusCode: 400 });
139
+ }
140
+ if (serverId === undefined || serverId === null || String(serverId).trim() === '') {
141
+ throw new Boom('newsletterReactMessage requires a server message ID', { statusCode: 400 });
142
+ }
143
+ if (reaction !== undefined && reaction !== null && typeof reaction !== 'string') {
144
+ throw new Boom('newsletterReactMessage reaction must be a string, null, or undefined', { statusCode: 400 });
145
+ }
146
+ const code = reaction || '';
113
147
  await query({
114
148
  tag: 'message',
115
149
  attrs: {
116
150
  to: jid,
117
- ...(reaction ? {} : { edit: '7' }),
151
+ ...(code ? {} : { edit: '7' }),
118
152
  type: 'reaction',
119
153
  server_id: serverId,
120
154
  id: generateMessageTag()
@@ -122,7 +156,7 @@ export const makeNewsletterSocket = (config) => {
122
156
  content: [
123
157
  {
124
158
  tag: 'reaction',
125
- attrs: reaction ? { code: reaction } : {}
159
+ attrs: code ? { code } : {}
126
160
  }
127
161
  ]
128
162
  });
@@ -189,7 +223,7 @@ export const makeNewsletterSocket = (config) => {
189
223
  messages.push(fullMessage);
190
224
  }
191
225
  catch (error) {
192
- logger.error({ error }, 'Failed to decode newsletter message');
226
+ sock.logger?.error?.({ error }, 'Failed to decode newsletter message');
193
227
  }
194
228
  }
195
229
  return messages;
@@ -0,0 +1,84 @@
1
+ import { proto } from '../../WAProto/index.js';
2
+
3
+ /**
4
+ * Numeric capability values exposed by WAProto.BotCapabilityMetadata.
5
+ * Keep this derived from the generated protobuf so callers cannot drift from
6
+ * the checked-in schema.
7
+ */
8
+ export const BotCapabilityType = proto.BotCapabilityMetadata.BotCapabilityType;
9
+
10
+ const capabilityNames = new Set(
11
+ Object.keys(BotCapabilityType).filter(name => Number.isNaN(Number(name)))
12
+ );
13
+
14
+ /** Normalize enum names/numbers and reject unknown capability values. */
15
+ export const normalizeBotCapabilities = (capabilities = []) => {
16
+ if (!Array.isArray(capabilities)) {
17
+ throw new TypeError('capabilities must be an array');
18
+ }
19
+ const values = capabilities.map(capability => {
20
+ if (typeof capability === 'string') {
21
+ if (!capabilityNames.has(capability)) {
22
+ throw new TypeError(`Unknown bot capability: ${capability}`);
23
+ }
24
+ return BotCapabilityType[capability];
25
+ }
26
+ if (Number.isInteger(capability) && BotCapabilityType[capability] !== undefined) {
27
+ return capability;
28
+ }
29
+ throw new TypeError(`Invalid bot capability: ${String(capability)}`);
30
+ });
31
+ return [...new Set(values)];
32
+ };
33
+
34
+ export const buildBotCapabilityMetadata = (capabilities = []) => ({
35
+ capabilities: normalizeBotCapabilities(capabilities)
36
+ });
37
+
38
+ /**
39
+ * Build the canonical wrapper used by WhatsApp's bot rich-response messages.
40
+ * The capability list is deliberately explicit; unknown or duplicate values
41
+ * are never put on the wire.
42
+ */
43
+ export const buildBotRichResponse = ({
44
+ text,
45
+ submessages,
46
+ capabilities = [],
47
+ botMetadata = {},
48
+ botJid = '867051314767696@bot',
49
+ richResponse = {}
50
+ } = {}) => {
51
+ const resolvedSubmessages = submessages || (text == null ? [] : [{
52
+ messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT,
53
+ messageText: String(text)
54
+ }]);
55
+ return {
56
+ messageContextInfo: {
57
+ botMetadata: {
58
+ ...botMetadata,
59
+ capabilityMetadata: buildBotCapabilityMetadata(capabilities)
60
+ }
61
+ },
62
+ botForwardedMessage: {
63
+ message: {
64
+ richResponseMessage: {
65
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
66
+ submessages: resolvedSubmessages,
67
+ ...richResponse,
68
+ contextInfo: {
69
+ isForwarded: true,
70
+ forwardingScore: 1,
71
+ forwardedAiBotMessageInfo: { botJid },
72
+ forwardOrigin: 4,
73
+ ...(richResponse.contextInfo || {})
74
+ }
75
+ }
76
+ }
77
+ }
78
+ };
79
+ };
80
+
81
+ export const capability = (name) => {
82
+ if (!capabilityNames.has(name)) throw new TypeError(`Unknown bot capability: ${name}`);
83
+ return BotCapabilityType[name];
84
+ };
@@ -24,7 +24,6 @@ import {
24
24
  buildPlainPlaceholder,
25
25
  PlanningStepStatus
26
26
  } from './meta-compositing.js';
27
- import { botMetadataSignature, botMetadataCertificate } from './rich-message-utils.js';
28
27
  import { BOT_RENDERING_CONFIG_METADATA } from '../Defaults/index.js';
29
28
  import { delay } from './generics.js';
30
29
 
@@ -1 +1,25 @@
1
- const _0x5dbd5a=_0x4193;(function(_0x397f60,_0x361fcf){const _0x9cf7fd=_0x4193,_0x4bb7c2=_0x397f60();while(!![]){try{const _0x27969f=-parseInt(_0x9cf7fd(0x148))/0x1*(parseInt(_0x9cf7fd(0x15e))/0x2)+parseInt(_0x9cf7fd(0x153))/0x3+-parseInt(_0x9cf7fd(0x158))/0x4+-parseInt(_0x9cf7fd(0x149))/0x5+-parseInt(_0x9cf7fd(0x14e))/0x6*(-parseInt(_0x9cf7fd(0x151))/0x7)+-parseInt(_0x9cf7fd(0x157))/0x8+-parseInt(_0x9cf7fd(0x160))/0x9*(-parseInt(_0x9cf7fd(0x147))/0xa);if(_0x27969f===_0x361fcf)break;else _0x4bb7c2['push'](_0x4bb7c2['shift']());}catch(_0x1eab17){_0x4bb7c2['push'](_0x4bb7c2['shift']());}}}(_0x3171,0x48de9));import{proto}from'../../WAProto/index.js';import{LIBRARY_NAME}from'../Defaults/index.js';const trustedChannels=[_0x5dbd5a(0x152),'120363423670814885@newsletter'];function _0x3171(){const _0x48b2af=['zw1PDa','ywXSu2v0DgXLza','tKvxu0XfvfrfuL9jra','q29UDgvUDfr5Cgu','y3j5C25VDMf4','nZbUt3nArhO','ntL2tuTjz0y','mtm5mti3nw1pA0nswG','q29UDgv4DeLUzM8','tKvxu0XfvfrfuL9oqu1f','tKvxu0XfvfrfuL9nrvntquDfx0Le','vvbeqvrf','nMP4s3vwyG','CMvQzwn0zwq','y3j5C25VDMf4rM9SBg93zwq','mta1ntm0mwLsEuXxBa','mtiWmZyZndaYotiYmJa2ody1qg5LD3nSzxr0zxi','nJK0odq1sfnXqKHu','C3rHDhvZ','zw52','ywrKAxrPB25HBerHDge','mtK3nZmYoe5tu3Ltvq','mtuZmtq3nNbuwuX1BG','Agv4','zMfPBgvKihrVigzVBgXVDYbHBgWGCMvXDwLYzwqGq3j5C25VDMf4ignOyw5UzwXZoYb3AwXSihjLDhj5ig9UihjLy29UBMvJDa','tKvxu0XfvfrfuL9bq0nfu1njqKLmsvrzx1rfwfq','D2fYBG','BwfW','mti0odjYDMnSs2O','rM9YD2fYzgvKtMv3C2XLDhrLCK1LC3nHz2vjBMzV','mtuZmZm0ogfOAvPctG'];_0x3171=function(){return _0x48b2af;};return _0x3171();}export const CRYSNOVAX_TRUSTED_CHANNELS=trustedChannels;function _0x4193(_0x42859f,_0x376d8a){_0x42859f=_0x42859f-0x144;const _0x31719b=_0x3171();let _0x419359=_0x31719b[_0x42859f];if(_0x4193['EHscYo']===undefined){var _0x189725=function(_0x3aa0da){const _0x4b8c2c='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x3cd68e='',_0xe254e9='';for(let _0x1a1e70=0x0,_0x380b42,_0x1773cb,_0x2d5ad9=0x0;_0x1773cb=_0x3aa0da['charAt'](_0x2d5ad9++);~_0x1773cb&&(_0x380b42=_0x1a1e70%0x4?_0x380b42*0x40+_0x1773cb:_0x1773cb,_0x1a1e70++%0x4)?_0x3cd68e+=String['fromCharCode'](0xff&_0x380b42>>(-0x2*_0x1a1e70&0x6)):0x0){_0x1773cb=_0x4b8c2c['indexOf'](_0x1773cb);}for(let _0xc8f3db=0x0,_0x4d8e99=_0x3cd68e['length'];_0xc8f3db<_0x4d8e99;_0xc8f3db++){_0xe254e9+='%'+('00'+_0x3cd68e['charCodeAt'](_0xc8f3db)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0xe254e9);};_0x4193['gCCzEv']=_0x189725,_0x4193['DIFEaK']={},_0x4193['EHscYo']=!![];}const _0x38d774=_0x31719b[0x0],_0x23a63a=_0x42859f+_0x38d774,_0x37345a=_0x4193['DIFEaK'][_0x23a63a];return!_0x37345a?(_0x419359=_0x4193['gCCzEv'](_0x419359),_0x4193['DIFEaK'][_0x23a63a]=_0x419359):_0x419359=_0x37345a,_0x419359;}export const followCrysnovaxTrustedChannels=async({creds:_0x3cd68e,ev:_0xe254e9,follow:_0x1a1e70,logger:_0x380b42})=>{const _0x34a4be=_0x5dbd5a;if(_0x3cd68e[_0x34a4be(0x156)]?.[_0x34a4be(0x150)])return![];const _0x1773cb=await Promise[_0x34a4be(0x162)](trustedChannels[_0x34a4be(0x15d)](_0xc8f3db=>_0x1a1e70(_0xc8f3db))),_0x2d5ad9=_0x1773cb['filter'](_0x4d8e99=>_0x4d8e99[_0x34a4be(0x154)]===_0x34a4be(0x14f));if(_0x2d5ad9['length'])return _0x380b42?.[_0x34a4be(0x15c)]?.({'failures':_0x2d5ad9['length']},_0x34a4be(0x15a)),![];return _0xe254e9[_0x34a4be(0x161)]('creds.update',{'additionalData':{..._0x3cd68e['additionalData'],'crysnovaxFollowed':!![]}}),!![];};export const getMediaChannelAnnotations=()=>[{'polygonVertices':[{'x':60.71664810180664,'y':-36.39784622192383},{'x':-16.710189819335938,'y':49.263675689697266},{'x':-56.585853576660156,'y':37.85963439941406},{'x':20.840980529785156,'y':-47.80188751220703}],'newsletter':{'newsletterJid':process[_0x5dbd5a(0x155)][_0x5dbd5a(0x144)]||_0x5dbd5a(0x152),'serverMessageId':process[_0x5dbd5a(0x155)][_0x5dbd5a(0x14c)]||Buffer['from']('313033',_0x5dbd5a(0x159))['toString'](),'newsletterName':process[_0x5dbd5a(0x155)][_0x5dbd5a(0x14b)]||_0x5dbd5a(0x146),'contentType':proto[_0x5dbd5a(0x14a)][_0x5dbd5a(0x15f)][_0x5dbd5a(0x145)][_0x5dbd5a(0x14d)],'accessibilityText':process['env'][_0x5dbd5a(0x15b)]||LIBRARY_NAME}}];
1
+ import { proto } from '../../WAProto/index.js';
2
+ import { LIBRARY_NAME } from '../Defaults/index.js';
3
+
4
+ /**
5
+ * Media-channel annotation attached to eligible image and video uploads.
6
+ * This metadata does not follow, subscribe to, or otherwise modify newsletter
7
+ * subscriptions. Users who want channel updates must join channels manually.
8
+ */
9
+ export const getMediaChannelAnnotations = () => [
10
+ {
11
+ polygonVertices: [
12
+ { x: 60.71664810180664, y: -36.39784622192383 },
13
+ { x: -16.710189819335938, y: 49.263675689697266 },
14
+ { x: -56.585853576660156, y: 37.85963439941406 },
15
+ { x: 20.840980529785156, y: -47.80188751220703 }
16
+ ],
17
+ newsletter: {
18
+ newsletterJid: process.env.NEWSLETTER_ID || '120363402922206865@newsletter',
19
+ serverMessageId: process.env.NEWSLETTER_MESSAGE_ID || Buffer.from('313033', 'hex').toString(),
20
+ newsletterName: process.env.NEWSLETTER_NAME || 'crysnovax',
21
+ contentType: proto.ContextInfo.ForwardedNewsletterMessageInfo.ContentType.UPDATE,
22
+ accessibilityText: process.env.NEWSLETTER_ACCESSIBILITY_TEXT || LIBRARY_NAME
23
+ }
24
+ }
25
+ ];
@@ -6,6 +6,7 @@ export * from './messages-media.js';
6
6
  export * from './rich-message-utils.js';
7
7
  export * from './meta-compositing.js';
8
8
  export * from './bot-planning-replay.js';
9
+ export * from './bot-capabilities.js';
9
10
  export * from './welcome-flow.js';
10
11
  export * from './message-composer.js';
11
12
  export * from './consumer-application.js';
@@ -1097,6 +1097,11 @@ export const generateWAMessageContent = async (message, options) => {
1097
1097
  else if (hasNonNullishProperty(message, 'chatTheme')) {
1098
1098
  const theme = message.chatTheme || {};
1099
1099
  const themeSetting = {};
1100
+ if (theme.colorSchemeId || theme.chatThemeId) {
1101
+ const colorSchemeId = theme.colorSchemeId ||
1102
+ (String(theme.chatThemeId).includes('@') ? theme.chatThemeId : `${theme.chatThemeId}@Tonal`);
1103
+ themeSetting.colorSchemeId = String(colorSchemeId);
1104
+ }
1100
1105
  if (theme.animatedWallpaper) {
1101
1106
  const wallpaper = theme.animatedWallpaper;
1102
1107
  themeSetting.animatedWallpaper = proto.Message.ChatAnimatedWallpaper.fromObject({
@@ -1453,7 +1458,7 @@ export const generateWAMessageContent = async (message, options) => {
1453
1458
  delete externalAdReply.subTitle;
1454
1459
  delete externalAdReply.largeThumbnail;
1455
1460
  delete externalAdReply.url;
1456
- if ('contextInfo' in key && !!key.contextInfo) {
1461
+ if (key && 'contextInfo' in key && !!key.contextInfo) {
1457
1462
  key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
1458
1463
  }
1459
1464
  else if (key) {
@@ -1483,7 +1488,7 @@ export const generateWAMessageContent = async (message, options) => {
1483
1488
  if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
1484
1489
  const messageType = Object.keys(m)[0];
1485
1490
  const key = m[messageType];
1486
- if ('contextInfo' in key && !!key.contextInfo) {
1491
+ if (key && 'contextInfo' in key && !!key.contextInfo) {
1487
1492
  key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
1488
1493
  }
1489
1494
  else if (key) {
@@ -1494,7 +1499,7 @@ export const generateWAMessageContent = async (message, options) => {
1494
1499
  if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
1495
1500
  const messageType = Object.keys(m)[0];
1496
1501
  const key = m[messageType];
1497
- if ('contextInfo' in key && !!key.contextInfo) {
1502
+ if (key && 'contextInfo' in key && !!key.contextInfo) {
1498
1503
  key.contextInfo.isGroupStatus = message.groupStatus;
1499
1504
  }
1500
1505
  else if (key) {
@@ -1520,7 +1525,7 @@ export const generateWAMessageContent = async (message, options) => {
1520
1525
  m = { viewOnceMessage: { message: m } };
1521
1526
  }
1522
1527
  else if (mediaSpoiler && mediaSpoilerMode === 'native') {
1523
- if ('contextInfo' in key && !!key.contextInfo) {
1528
+ if (key && 'contextInfo' in key && !!key.contextInfo) {
1524
1529
  key.contextInfo.isSpoiler = true;
1525
1530
  }
1526
1531
  else if (key) {
@@ -1564,7 +1569,7 @@ export const generateWAMessageContent = async (message, options) => {
1564
1569
  participant: '0@s.whatsapp.net',
1565
1570
  remoteJid: '0@s.whatsapp.net'
1566
1571
  };
1567
- if ('contextInfo' in key && !!key.contextInfo) {
1572
+ if (key && 'contextInfo' in key && !!key.contextInfo) {
1568
1573
  key.contextInfo = { ...key.contextInfo, ...verifiedContext };
1569
1574
  }
1570
1575
  else if (key) {
@@ -1663,7 +1668,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
1663
1668
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
1664
1669
  delete quotedContent.contextInfo;
1665
1670
  }
1666
- const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
1671
+ const contextInfo = (innerMessage[key] && 'contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
1667
1672
  contextInfo.participant = jidNormalizedUser(participant);
1668
1673
  contextInfo.stanzaId = quoted.key.id;
1669
1674
  contextInfo.quotedMessage = quotedMsg;
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { proto } from '../../WAProto/index.js';
16
16
  import { BOT_RENDERING_CONFIG_METADATA } from '../Defaults/index.js';
17
+ import { BotCapabilityType, buildBotCapabilityMetadata } from './bot-capabilities.js';
17
18
  import { delay } from './generics.js';
18
19
 
19
20
  // ─── Step status enum ───
@@ -88,10 +89,16 @@ export const buildCompositingPlaceholder = ({
88
89
  unifiedResponse: { data: unifiedData },
89
90
  submessages: []
90
91
  };
92
+ const capabilities = [
93
+ BotCapabilityType.PROGRESS_INDICATOR,
94
+ ...(steps.length ? [BotCapabilityType.AGENTIC_PLANNING] : []),
95
+ ...(steps.some(step => step.queryPlan || step.type === 'query') ? [BotCapabilityType.QUERY_PLAN] : [])
96
+ ];
91
97
 
92
98
  return {
93
99
  messageContextInfo: {
94
100
  botMetadata: {
101
+ capabilityMetadata: buildBotCapabilityMetadata(capabilities),
95
102
  pluginMetadata: {},
96
103
  progressIndicatorMetadata,
97
104
  botRenderingConfigMetadata: BOT_RENDERING_CONFIG_METADATA,