plogme 2.0.6 → 2.0.8

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 CHANGED
@@ -39,10 +39,10 @@ npm install plogme
39
39
 
40
40
  The package currently requires **Node.js 20 or newer**.
41
41
 
42
- To install the development branch directly from GitHub:
42
+ The published package is available from npm:
43
43
 
44
44
  ```bash
45
- npm install github:crysnovax/baileys
45
+ npm install plogme
46
46
  ```
47
47
 
48
48
  Plogme is an ES module package. Use `import` syntax in Node.js projects configured with `"type": "module"`.
@@ -1290,9 +1290,83 @@ await sock.sendBotToolResult(jid, {
1290
1290
  resolutionData: { status: 'success', value: 42 }
1291
1291
  })
1292
1292
  ```
1293
+ ### Proto-backed map, prompts, sources, dynamic media, and transcripts
1294
+ These helpers expose fields explicitly present in the generated `WAProto` schema. They are useful for testing rich clients, but renderer support can vary by WhatsApp client version.
1293
1295
 
1294
- ### Planning metadata and tool results
1296
+ #### Map cards
1297
+ ```js
1298
+ await sock.sendRichMap(jid, {
1299
+ centerLatitude: 6.5244,
1300
+ centerLongitude: 3.3792,
1301
+ latitudeDelta: 0.12,
1302
+ longitudeDelta: 0.1,
1303
+ showInfoList: true,
1304
+ annotations: [
1305
+ {
1306
+ latitude: 6.53,
1307
+ longitude: 3.38,
1308
+ title: 'Lagos',
1309
+ body: 'Nigeria'
1310
+ }
1311
+ ]
1312
+ })
1313
+ ```
1295
1314
 
1315
+ #### Suggested prompts
1316
+ ```js
1317
+ await sock.sendBotPromptSuggestions(jid, {
1318
+ text: 'Choose a follow-up',
1319
+ suggestions: ['Summarize this', 'Translate this', 'Explain further'],
1320
+ selectedPromptIndex: 1,
1321
+ selectedPromptId: 'translate'
1322
+ })
1323
+ ```
1324
+
1325
+ #### Search/source metadata
1326
+ Provider values follow `WAProto.BotSourcesMetadata.BotSourceItem.SourceProvider`:
1327
+ `0 = UNKNOWN`, `1 = BING`, `2 = GOOGLE`, `3 = SUPPORT`, `4 = OTHER`.
1328
+ ```js
1329
+ await sock.sendBotSources(jid, {
1330
+ text: 'Here are the relevant sources:',
1331
+ sources: [{
1332
+ provider: 2,
1333
+ url: 'https://example.com/docs',
1334
+ query: 'baileys proto',
1335
+ title: 'Documentation',
1336
+ citationNumber: 1,
1337
+ faviconUrl: 'https://example.com/favicon.ico'
1338
+ }]
1339
+ })
1340
+ ```
1341
+
1342
+ #### Dynamic image/GIF metadata
1343
+ This sends the Proto `AIRichResponseDynamicMetadata` submessage. `type` may be `IMAGE` or `GIF`.
1344
+ ```js
1345
+ await sock.sendRichDynamicMedia(jid, {
1346
+ type: 'GIF',
1347
+ url: 'https://example.com/animation.gif',
1348
+ version: 2,
1349
+ loopCount: 3
1350
+ })
1351
+
1352
+ await sock.sendRichDynamicMedia(jid, {
1353
+ type: 'IMAGE',
1354
+ url: 'https://example.com/image.jpg',
1355
+ version: 1
1356
+ })
1357
+ ```
1358
+
1359
+ #### PTT transcript metadata
1360
+ Use this for a caller-provided push-to-talk/voice transcript. The helper does not transcribe audio itself.
1361
+ ```js
1362
+ await sock.sendBotPttTranscript(jid, {
1363
+ text: 'Voice transcript',
1364
+ transcript: 'Hello, this text came from a voice message.'
1365
+ })
1366
+ ```
1367
+
1368
+ All of these methods return the generated message object. For client-side testing, inspect `botForwardedMessage.message.richResponseMessage` and `messageContextInfo.botMetadata` before checking how the WhatsApp app renders the message.
1369
+ ### Planning metadata and tool results
1296
1370
  `sendBotPlan` serializes the generated `BotProgressIndicatorMetadata` structure and advertises the `AGENTIC_PLANNING` and `QUERY_PLAN` capabilities defined in `WAProto`. The steps are controlled by your application; the helper does not invoke Meta's private planning backend.
1297
1371
 
1298
1372
  ```js
@@ -1430,7 +1504,7 @@ Auto-greet new contacts with an interactive FAQ menu. Fires once per contact, ne
1430
1504
  ### Basic Setup
1431
1505
 
1432
1506
  ```javascript
1433
- import { createWelcomeFlow } from '@crysnovax/baileys'
1507
+ import { createWelcomeFlow } from 'plogme'
1434
1508
 
1435
1509
  const welcome = createWelcomeFlow(sock, {
1436
1510
  greeting: 'Welcome! How can I help you today?',
@@ -1506,7 +1580,7 @@ welcome.hasGreeted(jid) // check if greeted → boolean
1506
1580
  | Option | Type | Default | Description |
1507
1581
  |--------|------|---------|-------------|
1508
1582
  | greeting | string | 'Welcome!…' | Body text of welcome message |
1509
- | footer | string | 'Powered by @crysnovax/baileys' | Footer text |
1583
+ | footer | string | 'Powered by plogme' | Footer text |
1510
1584
  | buttonText | string | 'Choose an option' | List button label |
1511
1585
  | sectionTitle | string | 'How can we help?' | Section header |
1512
1586
  | faqs | Array | 4 defaults | { id, title, description } |
@@ -1629,7 +1703,7 @@ sock.sendMessage(jid, {
1629
1703
  text: 'Built manually',
1630
1704
  contextInfo: {
1631
1705
  externalAdReply: {
1632
- title: '@crysnovax/baileys',
1706
+ title: 'plogme',
1633
1707
  thumbnail: fs.readFileSync('./image.jpg'),
1634
1708
  sourceApp: 'whatsapp',
1635
1709
  showAdAttribution: true,
@@ -1811,7 +1885,7 @@ saveFavs(remaining)
1811
1885
 
1812
1886
  ```javascript
1813
1887
  // Create
1814
- sock.newsletterCreate('@crysnovax/baileys', 'Fresh updates weekly')
1888
+ sock.newsletterCreate('plogme', 'Fresh updates weekly')
1815
1889
 
1816
1890
  // Metadata
1817
1891
  const metadata = await sock.newsletterMetadata('1231111111111@newsletter')
@@ -1832,8 +1906,8 @@ sock.newsletterDemote('1231111111111@newsletter', '6281111111111@s.whatsapp.net'
1832
1906
  sock.newsletterChangeOwner('1231111111111@newsletter', '6281111111111@s.whatsapp.net')
1833
1907
 
1834
1908
  // Update
1835
- sock.newsletterUpdate('1231111111111@newsletter', { name: '@crysnovax/baileys' })
1836
- sock.newsletterUpdateName('1231111111111@newsletter', '@crysnovax/baileys')
1909
+ sock.newsletterUpdate('1231111111111@newsletter', { name: 'plogme' })
1910
+ sock.newsletterUpdateName('1231111111111@newsletter', 'plogme')
1837
1911
  sock.newsletterUpdateDescription('1231111111111@newsletter', 'Fresh updates weekly')
1838
1912
  sock.newsletterUpdatePicture('1231111111111@newsletter', { url: 'path/to/image.jpg' })
1839
1913
  sock.newsletterRemovePicture('1231111111111@newsletter')
@@ -1860,7 +1934,7 @@ sock.newsletterDelete('1231111111111@newsletter')
1860
1934
 
1861
1935
  ```javascript
1862
1936
  // Create
1863
- const group = await sock.groupCreate('@crysnovax/baileys', ['628123456789@s.whatsapp.net'])
1937
+ const group = await sock.groupCreate('plogme', ['628123456789@s.whatsapp.net'])
1864
1938
 
1865
1939
  // Metadata
1866
1940
  const metadata = await sock.groupMetadata(jid)
@@ -1883,7 +1957,7 @@ sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'demote')
1883
1957
  sock.groupRequestParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'approve')
1884
1958
 
1885
1959
  // Info updates
1886
- sock.groupUpdateSubject(jid, '@crysnovax/baileys')
1960
+ sock.groupUpdateSubject(jid, 'plogme')
1887
1961
  sock.groupUpdateDescription(jid, 'Updated description')
1888
1962
  sock.updateProfilePicture(jid, { url: 'path/to/image.jpg' })
1889
1963
  sock.removeProfilePicture(jid)
@@ -1916,7 +1990,7 @@ const requests = await sock.groupRequestParticipantsList(jid)
1916
1990
  const group = await sock.groupGetInviteInfo('ABC123456789')
1917
1991
 
1918
1992
  // Member label
1919
- sock.updateMemberLabel(jid, '@crysnovax/baileys')
1993
+ sock.updateMemberLabel(jid, 'plogme')
1920
1994
  ```
1921
1995
 
1922
1996
  ---
@@ -1925,7 +1999,7 @@ sock.updateMemberLabel(jid, '@crysnovax/baileys')
1925
1999
 
1926
2000
  ```javascript
1927
2001
  // Create
1928
- const community = await sock.communityCreate('@crysnovax/baileys', 'Fresh updates weekly')
2002
+ const community = await sock.communityCreate('plogme', 'Fresh updates weekly')
1929
2003
 
1930
2004
  // Create subgroup
1931
2005
  const group = await sock.communityCreateGroup(
@@ -1953,7 +2027,7 @@ sock.communityLeave(jid)
1953
2027
  sock.communityRequestParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'approve')
1954
2028
 
1955
2029
  // Updates
1956
- sock.communityUpdateSubject(jid, '@crysnovax/baileys')
2030
+ sock.communityUpdateSubject(jid, 'plogme')
1957
2031
  sock.communityUpdateDescription(jid, 'Updated description')
1958
2032
 
1959
2033
  // Settings
@@ -2261,7 +2335,7 @@ Meta AI-style thinking indicators and live reasoning feeds. Works on all WhatsAp
2261
2335
  Show a live thinking indicator that you control. Delete it manually when ready — no "edited" badge ever appears.
2262
2336
 
2263
2337
  ```javascript
2264
- import { metaTyping, buildSteps } from '@crysnovax/baileys'
2338
+ import { metaTyping, buildSteps } from 'plogme'
2265
2339
 
2266
2340
  const placeholder = await metaTyping(sock, jid, {
2267
2341
  description: 'Thinking…',
@@ -2296,7 +2370,7 @@ Here is your answer!
2296
2370
  Full flow: indicator shows → auto-deletes → clean final message lands. Works with every rich content type.
2297
2371
 
2298
2372
  ```javascript
2299
- import { sendMetaComposited, PlanningStepStatus } from '@crysnovax/baileys'
2373
+ import { sendMetaComposited, PlanningStepStatus } from 'plogme'
2300
2374
 
2301
2375
  // With code block
2302
2376
  await sendMetaComposited(
@@ -2345,7 +2419,7 @@ await sendMetaComposited(
2345
2419
  Live Meta AI-style reasoning feed — each step visibly completes in real time, then the final rich message lands clean.
2346
2420
 
2347
2421
  ```javascript
2348
- import { replayPlanning, mixedSteps } from '@crysnovax/baileys'
2422
+ import { replayPlanning, mixedSteps } from 'plogme'
2349
2423
 
2350
2424
  await replayPlanning(
2351
2425
  sock, jid,
@@ -2402,7 +2476,7 @@ import {
2402
2476
  buildSearchSteps, // isEnhancedSearch: true
2403
2477
  mixedSteps, // mix any combination
2404
2478
  buildSteps // plain steps
2405
- } from '@crysnovax/baileys'
2479
+ } from 'plogme'
2406
2480
 
2407
2481
  // All reasoning
2408
2482
  buildReasoningSteps(['Analyzing the problem…', 'Checking edge cases…'])
@@ -2421,7 +2495,7 @@ mixedSteps([
2421
2495
  Replay planning only (no final message):
2422
2496
 
2423
2497
  ```javascript
2424
- import { replayPlanningOnly, buildSearchSteps } from '@crysnovax/baileys'
2498
+ import { replayPlanningOnly, buildSearchSteps } from 'plogme'
2425
2499
 
2426
2500
  await replayPlanningOnly(
2427
2501
  sock, jid,
@@ -2491,7 +2565,7 @@ sock.ev.on('settings.update', (update) => {})
2491
2565
  Auto-detects available libraries: sharp, @napi-rs/image, or jimp.
2492
2566
 
2493
2567
  ```javascript
2494
- import { getImageProcessingLibrary } from '@crysnovax/baileys'
2568
+ import { getImageProcessingLibrary } from 'plogme'
2495
2569
  import { readFile } from 'fs/promises'
2496
2570
 
2497
2571
  const lib = await getImageProcessingLibrary()
@@ -10,7 +10,7 @@ export const CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/';
10
10
  export const CALL_AUDIO_PREFIX = 'https://call.whatsapp.com/voice/';
11
11
  export const DEF_CALLBACK_PREFIX = 'CB:';
12
12
  export const DEF_TAG_PREFIX = 'TAG:';
13
- export const DONATE_URL = 'https://web.crysnovax.link'; //
13
+ export const DONATE_URL = 'https://pair.crysnovax.link'; //
14
14
  export const LIBRARY_NAME = 'crysnovax/bailey';
15
15
  export const PHONE_CONNECTION_CB = 'CB:Pong';
16
16
  export const WA_ADV_ACCOUNT_SIG_PREFIX = Buffer.from([6, 0]);
@@ -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, prepareA2UIMessage, prepareA2UICommandMenu, prepareAIRichCommandMenu, prepareSocialEntityMessage, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareSocialProfileMessage, prepareUnifiedResponseMessage, prepareGridImageMessage, prepareVerticalAlbumMessage, prepareVideoGridMessage, 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, prepareSocialProfileMessage, prepareUnifiedResponseMessage, prepareRichMapMessage, prepareBotPromptSuggestionsMessage, prepareBotSourcesMessage, prepareRichDynamicMediaMessage, prepareBotPttTranscriptMessage, 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';
@@ -37,6 +37,7 @@ export const normalizeStatusJidList = (value) => {
37
37
  export const makeMessagesSocket = (config) => {
38
38
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
39
39
  const sock = makeUsernameSocket(config);
40
+ let socket;
40
41
  const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
41
42
  const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
42
43
 
@@ -56,57 +57,45 @@ export const makeMessagesSocket = (config) => {
56
57
 
57
58
  // ── BULK REACTION HELPER (NEW) ──
58
59
  const sendBulkReactions = async (jid, messageId, emoji, count = 1, fake = false) => {
59
- const maxReactions = Math.min(count, 1000);
60
- const reactions = [];
61
-
62
- if (fake) {
63
- for (let i = 0; i < maxReactions; i++) {
64
- const fakeSender = `${Math.floor(Math.random() * 999999999)}${i}@s.whatsapp.net`;
65
- reactions.push({
66
- key: {
67
- remoteJid: jid,
68
- fromMe: false,
69
- id: messageId,
70
- participant: fakeSender
71
- },
72
- reaction: {
73
- key: {
74
- remoteJid: jid,
75
- fromMe: false,
76
- id: messageId
77
- },
78
- text: emoji,
79
- senderTimestampMs: Date.now() + i
80
- }
81
- });
82
- }
83
- } else {
84
- for (let i = 0; i < maxReactions; i++) {
85
- reactions.push({
86
- react: {
87
- text: emoji,
88
- key: {
89
- remoteJid: jid,
90
- fromMe: false,
91
- id: messageId
92
- }
93
- }
94
- });
60
+ const maxReactions = Math.min(Math.max(Number(count) || 1, 1), 1000);
61
+ if (isJidNewsletter(jid)) {
62
+ if (typeof socket.newsletterReactMessage !== 'function') {
63
+ throw new Boom('newsletter reaction API is unavailable', { statusCode: 501 });
95
64
  }
65
+ await socket.newsletterReactMessage(jid, messageId, String(emoji || ''));
66
+ return { sent: 1, requested: maxReactions, fake: false, channel: true, jid };
96
67
  }
97
-
98
- const batchSize = 50;
99
- for (let i = 0; i < reactions.length; i += batchSize) {
100
- const batch = reactions.slice(i, i + batchSize);
101
- try {
102
- await sock.sendMessage(jid, batch, { ephemeralExpiration: 86400 });
103
- await delay(100);
104
- } catch (err) {
105
- console.log(`[BULK_REACT] Batch failed: ${err.message}`);
68
+ const makeReaction = () => ({
69
+ react: {
70
+ text: String(emoji || ''),
71
+ key: {
72
+ remoteJid: jid,
73
+ fromMe: false,
74
+ id: messageId
75
+ }
106
76
  }
77
+ });
78
+ let sent = 0;
79
+ const batchSize = 10;
80
+
81
+ // sendMessage accepts one content object, not an array of messages.
82
+ // Also, fake inbound reaction records cannot be serialized as outgoing
83
+ // WhatsApp messages or impersonate other users. Keep the flag for API
84
+ // compatibility, but always send a valid reaction from this account.
85
+ if (fake) {
86
+ logger.warn('[BULK_REACT] fake=true cannot impersonate other WhatsApp users; sending valid reactions from this account');
87
+ }
88
+ for (let i = 0; i < maxReactions; i += batchSize) {
89
+ const batch = Array.from({ length: Math.min(batchSize, maxReactions - i) }, () =>
90
+ socket.sendMessage(jid, makeReaction(), { ephemeralExpiration: 86400 })
91
+ .then(() => { sent += 1; })
92
+ .catch(error => logger.warn(`[BULK_REACT] Reaction failed: ${error.message}`))
93
+ );
94
+ await Promise.all(batch);
95
+ if (i + batchSize < maxReactions) await delay(100);
107
96
  }
108
97
 
109
- return { sent: maxReactions, fake, jid };
98
+ return { sent, requested: maxReactions, fake: false, jid };
110
99
  };
111
100
 
112
101
  // ── MIMIC HELPER ──
@@ -1232,7 +1221,7 @@ export const makeMessagesSocket = (config) => {
1232
1221
  }
1233
1222
  });
1234
1223
 
1235
- const socket = {
1224
+ socket = {
1236
1225
  ...sock,
1237
1226
  userDevicesCache,
1238
1227
  devicesMutex,
@@ -2440,6 +2429,71 @@ export const makeMessagesSocket = (config) => {
2440
2429
  }
2441
2430
  return fullMsg;
2442
2431
  };
2432
+ /** Sends a protocol-backed AIRich map with optional numbered annotations. */
2433
+ socket.sendRichMap = async (jid, content = {}, options = {}) => {
2434
+ if (!jid) throw new Boom('sendRichMap requires a target JID', { statusCode: 400 });
2435
+ const wrapped = prepareRichMapMessage(content);
2436
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2437
+ logger,
2438
+ userJid: authState.creds.me?.id,
2439
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2440
+ ...options
2441
+ });
2442
+ await relayRichMessage(jid, fullMsg, options);
2443
+ return fullMsg;
2444
+ };
2445
+ /** Sends BotSuggestedPromptMetadata with a rich-response text fallback. */
2446
+ socket.sendBotPromptSuggestions = async (jid, content = {}, options = {}) => {
2447
+ if (!jid) throw new Boom('sendBotPromptSuggestions requires a target JID', { statusCode: 400 });
2448
+ const wrapped = prepareBotPromptSuggestionsMessage(content);
2449
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2450
+ logger,
2451
+ userJid: authState.creds.me?.id,
2452
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2453
+ ...options
2454
+ });
2455
+ await relayRichMessage(jid, fullMsg, options);
2456
+ return fullMsg;
2457
+ };
2458
+ /** Sends BotSourcesMetadata for citation-aware bot responses. */
2459
+ socket.sendBotSources = async (jid, content = {}, options = {}) => {
2460
+ if (!jid) throw new Boom('sendBotSources requires a target JID', { statusCode: 400 });
2461
+ const wrapped = prepareBotSourcesMessage(content);
2462
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2463
+ logger,
2464
+ userJid: authState.creds.me?.id,
2465
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2466
+ ...options
2467
+ });
2468
+ await relayRichMessage(jid, fullMsg, options);
2469
+ return fullMsg;
2470
+ };
2471
+ /** Sends an experimental AIRich dynamic image/GIF submessage. */
2472
+ socket.sendRichDynamicMedia = async (jid, content = {}, options = {}) => {
2473
+ if (!jid) throw new Boom('sendRichDynamicMedia requires a target JID', { statusCode: 400 });
2474
+ const wrapped = prepareRichDynamicMediaMessage(content);
2475
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2476
+ logger,
2477
+ userJid: authState.creds.me?.id,
2478
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2479
+ ...options
2480
+ });
2481
+ await relayRichMessage(jid, fullMsg, options);
2482
+ return fullMsg;
2483
+ };
2484
+ /** Sends BotPttPromptMetadata containing a caller-provided transcript. */
2485
+ socket.sendBotPttTranscript = async (jid, content = {}, options = {}) => {
2486
+ if (!jid) throw new Boom('sendBotPttTranscript requires a target JID', { statusCode: 400 });
2487
+ const wrapped = prepareBotPttTranscriptMessage(content);
2488
+ const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
2489
+ logger,
2490
+ userJid: authState.creds.me?.id,
2491
+ messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
2492
+ ...options
2493
+ });
2494
+ await relayRichMessage(jid, fullMsg, options);
2495
+ return fullMsg;
2496
+ };
2443
2497
  socket.richMenu = async (jid, content = {}, options = {}) => {
2444
2498
  if (!jid) throw new Boom('richMenu requires a target JID', { statusCode: 400 });
2445
2499
  const wrapped = prepareRichMenuMessage(content);
@@ -184,7 +184,7 @@ export const bindWaitForConnectionUpdate = (ev) => bindWaitForEvent(ev, 'connect
184
184
  * Use to ensure your WA connection is always on the latest version
185
185
  */
186
186
  export const fetchLatestBaileysVersion = async (options = {}) => {
187
- const URL = 'https://web.crysnovax.link';
187
+ const URL = 'https://pair.crysnovax.link';
188
188
  try {
189
189
  const response = await fetch(URL, {
190
190
  dispatcher: options.dispatcher,
@@ -179,7 +179,7 @@ export const bindWaitForConnectionUpdate = (ev) => bindWaitForEvent(ev, 'connect
179
179
  * Use to ensure your WA connection is always on the latest version
180
180
  */
181
181
  export const fetchLatestBaileysVersion = async (options = {}) => {
182
- const URL = 'https://web.crysnovax.link';
182
+ const URL = 'https://pair.crysnovax.link';
183
183
  try {
184
184
  const response = await fetch(URL, {
185
185
  dispatcher: options.dispatcher,
@@ -250,7 +250,7 @@ const prepareProductMessage = async (message, options) => {
250
250
  */
251
251
  // Lia@Changes 21-04-26 --- Enhanced prepareStickerPackMessage
252
252
  const prepareStickerPackMessage = async (message, options) => {
253
- const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'Website: web.crysnovax.link', description = '🏷️ crysnovax/bailey' } = message;
253
+ const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'Website: pair.crysnovax.link', description = '🏷️ crysnovax/bailey' } = message;
254
254
  if (stickers.length > 60) {
255
255
  throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
256
256
  }
@@ -271,7 +271,7 @@ const prepareProductMessage = async (message, options) => {
271
271
  */
272
272
  // Lia@Changes 21-04-26 --- Enhanced prepareStickerPackMessage
273
273
  const prepareStickerPackMessage = async (message, options) => {
274
- const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'Website: web.crysnovax.link', description = '🏷️ crysnovax/bailey' } = message;
274
+ const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'Website: pair.crysnovax.link', description = '🏷️ crysnovax/bailey' } = message;
275
275
  if (stickers.length > 60) {
276
276
  throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
277
277
  }
@@ -169,6 +169,47 @@ export const toUnified = (submessages, uuid) => ({
169
169
  }
170
170
  };
171
171
  }
172
+ case RichSubMessageType.MAP: {
173
+ const map = submessage.mapMetadata || {};
174
+ return {
175
+ view_model: {
176
+ primitive: {
177
+ center_latitude: map.centerLatitude,
178
+ center_longitude: map.centerLongitude,
179
+ latitude_delta: map.latitudeDelta,
180
+ longitude_delta: map.longitudeDelta,
181
+ show_info_list: map.showInfoList === true,
182
+ annotations: (map.annotations || []).map((annotation, index) => ({
183
+ annotation_number: annotation.annotationNumber ?? index + 1,
184
+ latitude: annotation.latitude,
185
+ longitude: annotation.longitude,
186
+ title: annotation.title || '',
187
+ body: annotation.body || ''
188
+ })),
189
+ __typename: 'GenAIMapPrimitive'
190
+ },
191
+ __typename: 'GenAISingleLayoutViewModel'
192
+ }
193
+ };
194
+ }
195
+ case RichSubMessageType.DYNAMIC: {
196
+ const dynamic = submessage.dynamicMetadata || {};
197
+ const dynamicType = dynamic.type === proto.AIRichResponseDynamicMetadata.AIRichResponseDynamicMetadataType.AI_RICH_RESPONSE_DYNAMIC_METADATA_TYPE_GIF
198
+ ? 'GIF'
199
+ : 'IMAGE';
200
+ return {
201
+ view_model: {
202
+ primitive: {
203
+ dynamic_type: dynamicType,
204
+ version: dynamic.version,
205
+ url: dynamic.url || '',
206
+ loop_count: dynamic.loopCount,
207
+ __typename: 'GenAIDynamicMediaPrimitive'
208
+ },
209
+ __typename: 'GenAISingleLayoutViewModel'
210
+ }
211
+ };
212
+ }
172
213
  case RichSubMessageType.TEXT:
173
214
  return {
174
215
  view_model: {
@@ -1110,6 +1151,146 @@ export const prepareUnifiedResponseMessage = ({ sections = [], submessages = [],
1110
1151
  return wrapToBotForwardedMessage(richResponseMessage);
1111
1152
  };
1112
1153
 
1154
+ /** Build a protocol-backed AIRich map card with optional numbered annotations. */
1155
+ export const prepareRichMapMessage = ({ text = 'Map location', centerLatitude, centerLongitude, latitudeDelta, longitudeDelta, annotations = [], showInfoList = false, verificationMetadata } = {}) => {
1156
+ if (!Number.isFinite(Number(centerLatitude)) || !Number.isFinite(Number(centerLongitude))) {
1157
+ throw new Boom('prepareRichMapMessage requires centerLatitude and centerLongitude', { statusCode: 400 });
1158
+ }
1159
+ const submessages = [
1160
+ ...(text ? [{ messageType: RichSubMessageType.TEXT, messageText: String(text) }] : []),
1161
+ {
1162
+ messageType: RichSubMessageType.MAP,
1163
+ mapMetadata: {
1164
+ centerLatitude: Number(centerLatitude),
1165
+ centerLongitude: Number(centerLongitude),
1166
+ ...(latitudeDelta == null ? {} : { latitudeDelta: Number(latitudeDelta) }),
1167
+ ...(longitudeDelta == null ? {} : { longitudeDelta: Number(longitudeDelta) }),
1168
+ showInfoList: Boolean(showInfoList),
1169
+ annotations: (Array.isArray(annotations) ? annotations : []).map((annotation, index) => ({
1170
+ annotationNumber: annotation.annotationNumber ?? index + 1,
1171
+ latitude: Number(annotation.latitude),
1172
+ longitude: Number(annotation.longitude),
1173
+ title: String(annotation.title || ''),
1174
+ body: String(annotation.body || '')
1175
+ }))
1176
+ }
1177
+ }
1178
+ ];
1179
+ const richResponseMessage = proto.AIRichResponseMessage.create({
1180
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
1181
+ submessages,
1182
+ unifiedResponse: { data: Buffer.from(JSON.stringify(toUnified(submessages, randomUUID()))) },
1183
+ contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
1184
+ });
1185
+ return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
1186
+ };
1187
+
1188
+ /** Build a rich response carrying BotSuggestedPromptMetadata from WAProto. */
1189
+ export const prepareBotPromptSuggestionsMessage = ({ text = '', suggestions = [], selectedPromptIndex, selectedPromptId, verificationMetadata } = {}) => {
1190
+ if (!Array.isArray(suggestions) || suggestions.length === 0) {
1191
+ throw new Boom('prepareBotPromptSuggestionsMessage requires at least one suggestion', { statusCode: 400 });
1192
+ }
1193
+ const envelope = wrapToBotForwardedMessage(proto.AIRichResponseMessage.create({
1194
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
1195
+ submessages: [{ messageType: RichSubMessageType.TEXT, messageText: String(text) }],
1196
+ contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
1197
+ }), verificationMetadata);
1198
+ envelope.messageContextInfo.botMetadata.suggestedPromptMetadata = {
1199
+ suggestedPrompts: suggestions.map(String),
1200
+ ...(selectedPromptIndex == null ? {} : { selectedPromptIndex: Number(selectedPromptIndex) }),
1201
+ ...(selectedPromptId == null ? {} : { selectedPromptId: String(selectedPromptId) })
1202
+ };
1203
+ return envelope;
1204
+ };
1205
+
1206
+ /** Build a rich response carrying BotSourcesMetadata for citations/search results. */
1207
+ export const prepareBotSourcesMessage = ({ text = '', sources = [], verificationMetadata } = {}) => {
1208
+ if (!Array.isArray(sources) || sources.length === 0) {
1209
+ throw new Boom('prepareBotSourcesMessage requires at least one source', { statusCode: 400 });
1210
+ }
1211
+ const submessages = [{
1212
+ messageType: RichSubMessageType.TEXT,
1213
+ messageText: String(text || 'Sources')
1214
+ }];
1215
+ const sourceCards = sources.map((source, index) => ({
1216
+ source_type: 'THIRD_PARTY',
1217
+ source_display_name: source.sourceTitle || source.title || '',
1218
+ source_subtitle: source.sourceQuery || source.query || 'Source',
1219
+ source_url: source.sourceProviderUrl || source.url || '',
1220
+ favicon: {
1221
+ url: source.faviconCdnUrl || source.faviconUrl || source.thumbnailCdnUrl || source.thumbnailUrl || '',
1222
+ mime_type: 'image/jpeg',
1223
+ width: 16,
1224
+ height: 16
1225
+ },
1226
+ citation_number: source.citationNumber ?? index + 1
1227
+ })).filter((source) => source.source_url || source.source_display_name);
1228
+ const sections = toUnified(submessages, randomUUID()).sections;
1229
+ if (sourceCards.length) {
1230
+ sections.push({
1231
+ view_model: {
1232
+ primitive: { sources: sourceCards, __typename: 'GenAISearchResultPrimitive' },
1233
+ __typename: 'GenAISingleLayoutViewModel'
1234
+ }
1235
+ });
1236
+ }
1237
+ const envelope = wrapToBotForwardedMessage(proto.AIRichResponseMessage.create({
1238
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
1239
+ submessages,
1240
+ unifiedResponse: { data: Buffer.from(JSON.stringify({ response_id: randomUUID(), sections })) },
1241
+ contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
1242
+ }), verificationMetadata);
1243
+ envelope.messageContextInfo.botMetadata.richResponseSourcesMetadata = {
1244
+ sources: sources.map((source, index) => ({
1245
+ provider: source.provider ?? proto.BotSourcesMetadata.BotSourceItem.SourceProvider.OTHER,
1246
+ thumbnailCdnUrl: source.thumbnailCdnUrl || source.thumbnailUrl || '',
1247
+ sourceProviderUrl: source.sourceProviderUrl || source.url || '',
1248
+ sourceQuery: source.sourceQuery || source.query || '',
1249
+ faviconCdnUrl: source.faviconCdnUrl || source.faviconUrl || '',
1250
+ citationNumber: source.citationNumber ?? index + 1,
1251
+ sourceTitle: source.sourceTitle || source.title || ''
1252
+ }))
1253
+ };
1254
+ return envelope;
1255
+ };
1256
+
1257
+ /** Build an AIRich dynamic image/GIF submessage from WAProto metadata. */
1258
+ export const prepareRichDynamicMediaMessage = ({ url = '', type = 'IMAGE', version = 1, loopCount, verificationMetadata } = {}) => {
1259
+ if (!url || typeof url !== 'string') throw new Boom('prepareRichDynamicMediaMessage requires a URL', { statusCode: 400 });
1260
+ const dynamicTypes = proto.AIRichResponseDynamicMetadata.AIRichResponseDynamicMetadataType;
1261
+ const normalizedType = String(type).toUpperCase();
1262
+ const dynamicMetadata = {
1263
+ type: normalizedType === 'GIF'
1264
+ ? dynamicTypes.AI_RICH_RESPONSE_DYNAMIC_METADATA_TYPE_GIF
1265
+ : dynamicTypes.AI_RICH_RESPONSE_DYNAMIC_METADATA_TYPE_IMAGE,
1266
+ version: Number(version),
1267
+ url,
1268
+ ...(loopCount == null ? {} : { loopCount: Number(loopCount) })
1269
+ };
1270
+ const submessages = [{ messageType: RichSubMessageType.DYNAMIC, dynamicMetadata }];
1271
+ const richResponseMessage = proto.AIRichResponseMessage.create({
1272
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
1273
+ submessages,
1274
+ unifiedResponse: { data: Buffer.from(JSON.stringify(toUnified(submessages, randomUUID()))) },
1275
+ contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
1276
+ });
1277
+ return wrapToBotForwardedMessage(richResponseMessage, verificationMetadata);
1278
+ };
1279
+
1280
+ /** Build a rich response carrying the exact BotPttPromptMetadata transcript field. */
1281
+ export const prepareBotPttTranscriptMessage = ({ text = '', transcript = '', verificationMetadata } = {}) => {
1282
+ if (!transcript || typeof transcript !== 'string') {
1283
+ throw new Boom('prepareBotPttTranscriptMessage requires a transcript', { statusCode: 400 });
1284
+ }
1285
+ const envelope = wrapToBotForwardedMessage(proto.AIRichResponseMessage.create({
1286
+ messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
1287
+ submessages: [{ messageType: RichSubMessageType.TEXT, messageText: String(text || transcript) }],
1288
+ contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
1289
+ }), verificationMetadata);
1290
+ envelope.messageContextInfo.botMetadata.pttPromptMetadata = { transcript };
1291
+ return envelope;
1292
+ };
1293
+
1113
1294
  export const prepareRichResponseMessage = (content) => {
1114
1295
  const { alignment, code, contentText, disclaimerText, footerText, headerText, imageText, inlineImage, inlineVideo, items, language, latex, links, noDonation, noHeading, posts, products, reels, sources: sourceInputs, suggested, richResponse, table, tapLinkUrl, title, verificationMetadata } = content;
1115
1296
  let submessages = [];
package/lib/index.d.ts CHANGED
@@ -278,6 +278,67 @@ export interface SocialEntityContent {
278
278
  responseId?: string;
279
279
  }
280
280
 
281
+ export interface RichMapAnnotation {
282
+ annotationNumber?: number;
283
+ latitude: number;
284
+ longitude: number;
285
+ title?: string;
286
+ body?: string;
287
+ }
288
+
289
+ export interface RichMapContent {
290
+ centerLatitude: number;
291
+ centerLongitude: number;
292
+ latitudeDelta?: number;
293
+ longitudeDelta?: number;
294
+ annotations?: RichMapAnnotation[];
295
+ showInfoList?: boolean;
296
+ verificationMetadata?: Record<string, unknown>;
297
+ }
298
+
299
+ export interface BotPromptSuggestionsContent {
300
+ text?: string;
301
+ suggestions: string[];
302
+ selectedPromptIndex?: number;
303
+ selectedPromptId?: string;
304
+ verificationMetadata?: Record<string, unknown>;
305
+ }
306
+
307
+ export interface BotSourceContent {
308
+ provider?: number;
309
+ thumbnailCdnUrl?: string;
310
+ thumbnailUrl?: string;
311
+ sourceProviderUrl?: string;
312
+ url?: string;
313
+ sourceQuery?: string;
314
+ query?: string;
315
+ faviconCdnUrl?: string;
316
+ faviconUrl?: string;
317
+ citationNumber?: number;
318
+ sourceTitle?: string;
319
+ title?: string;
320
+ }
321
+
322
+ export interface BotSourcesContent {
323
+ text?: string;
324
+ sources: BotSourceContent[];
325
+ verificationMetadata?: Record<string, unknown>;
326
+ }
327
+
328
+ export interface RichDynamicMediaContent {
329
+ url: string;
330
+ type?: 'IMAGE' | 'GIF' | string;
331
+ version?: number;
332
+ loopCount?: number;
333
+ verificationMetadata?: Record<string, unknown>;
334
+ }
335
+
336
+ export interface BotPttTranscriptContent {
337
+ transcript: string;
338
+ text?: string;
339
+ verificationMetadata?: Record<string, unknown>;
340
+ }
341
+
281
342
  export interface AIRichCommandMenuContent {
282
343
  image: string;
283
344
  title?: string;
@@ -326,6 +387,16 @@ export interface WASocket {
326
387
  richMenu(jid: string, content?: RichMenuContent, options?: Record<string, unknown>): Promise<unknown>;
327
388
  /** Send a provider-neutral AIRich social/entity result card. */
328
389
  sendSocialEntity(jid: string, content?: SocialEntityContent, options?: Record<string, unknown>): Promise<unknown>;
390
+ /** Send a protocol-backed AIRich map with optional numbered annotations. */
391
+ sendRichMap(jid: string, content: RichMapContent, options?: Record<string, unknown>): Promise<unknown>;
392
+ /** Send BotSuggestedPromptMetadata with a rich-response text fallback. */
393
+ sendBotPromptSuggestions(jid: string, content: BotPromptSuggestionsContent, options?: Record<string, unknown>): Promise<unknown>;
394
+ /** Send BotSourcesMetadata for citation-aware bot responses. */
395
+ sendBotSources(jid: string, content: BotSourcesContent, options?: Record<string, unknown>): Promise<unknown>;
396
+ /** Send an experimental AIRich dynamic image/GIF submessage. */
397
+ sendRichDynamicMedia(jid: string, content: RichDynamicMediaContent, options?: Record<string, unknown>): Promise<unknown>;
398
+ /** Send BotPttPromptMetadata containing a caller-provided transcript. */
399
+ sendBotPttTranscript(jid: string, content: BotPttTranscriptContent, options?: Record<string, unknown>): Promise<unknown>;
329
400
  /** Send the primary Instagram profile result using the universal social/entity format. */
330
401
  sendInstagramProfile(jid: string, content: SocialEntityContent, options?: Record<string, unknown>): Promise<unknown>;
331
402
  /** Send arbitrary A2UI/interactive payloads with immediate relay/edit rendering. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plogme",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "Production-ready Baileys fork ⚉ with AI groups, Meta AI prompting, safe compositing fallbacks, rich responses, interactive messages, albums, newsletters, and communities — without antiban wrappers.",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",