plogme 2.0.4 → 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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Plogme
1
+ # Plogme ⚉
2
2
 
3
3
  **Plogme** is a Node.js WhatsApp Web client built on the Baileys protocol implementation. It provides authentication, messaging, media uploads, groups, status updates, interactive messages, albums, rich responses, Meta AI-style envelopes, HTML screens, and selected WhatsApp Web protocol features.
4
4
 
@@ -234,6 +234,44 @@ await sock.updateProfileName('Cody AI')
234
234
  await sock.updateProfileStatus('Building helpful WhatsApp tools')
235
235
  ```
236
236
 
237
+ ### Events and polls
238
+
239
+ ```js
240
+ await sock.sendMessage(jid, {
241
+ event: {
242
+ name: 'Meet and Mingle',
243
+ description: 'A scheduled community event.',
244
+ call: 'audio',
245
+ startDate: new Date(Date.now() + 3600000),
246
+ endDate: new Date(Date.now() + 28800000),
247
+ isCancelled: false,
248
+ isScheduleCall: false,
249
+ extraGuestsAllowed: false,
250
+ location: { name: 'Lagos', degreesLatitude: 6.5244, degreesLongitude: 3.3792 }
251
+ }
252
+ })
253
+
254
+ await sock.sendMessage(jid, {
255
+ poll: {
256
+ name: 'Release preference',
257
+ values: ['Stable', 'Beta'],
258
+ selectableCount: 1,
259
+ endDate: new Date(Date.now() + 28800000),
260
+ hideVoter: false,
261
+ canAddOption: false
262
+ }
263
+ })
264
+ ```
265
+
266
+ ### Forward and edit
267
+
268
+ ```js
269
+ await sock.sendMessage(jid, { forward: message, force: true })
270
+ await sock.sendMessage(jid, { delete: message.key })
271
+ await sock.sendMessage(jid, { text: 'Updated text', edit: message.key })
272
+ await sock.sendMessage(jid, { caption: 'Updated caption', edit: message.key })
273
+ ```
274
+
237
275
  ## Media and albums
238
276
 
239
277
  ### Images, video, audio, documents, and stickers
@@ -329,7 +367,7 @@ await sock.sendMessage(jid, {
329
367
  })
330
368
  ```
331
369
 
332
- `sendAlbumMessage()` accepts fetchable image and video URLs. Local `Buffer`/file inputs are not uploaded by this rich album API. The lower-level `prepareGridImageMessage()` helper remains available when a compact `GRID_IMAGE` layout is specifically required; `sendAlbumMessage()` itself uses the larger `GenAIVStackLayoutViewModel` presentation.
370
+ `sendAlbumMessage()` accepts fetchable image and video URLs. Local `Buffer`/file inputs are not uploaded by this rich album API. The lower-level `prepareGridImageMessage()` helper remains available when a compact `GRID_IMAGE` layout is specifically required; `sendAlbumMessage()` itself uses a full-width `GenAIVStackLayoutViewModel` presentation for both images and videos. Image primitives explicitly request full-width presentation and carry preview/full URL fallbacks.
333
371
 
334
372
  ### Video grids
335
373
 
@@ -384,6 +422,29 @@ await sock.sendMessage(jid, {
384
422
  })
385
423
  ```
386
424
 
425
+ Buttons can also carry media and a native-flow selector:
426
+
427
+ ```js
428
+ await sock.sendMessage(jid, {
429
+ image: { url: 'https://example.com/image.jpg' },
430
+ caption: 'Interactive buttons',
431
+ footer: 'CODY AI',
432
+ buttons: [
433
+ { text: 'Rating', id: '#Rating' },
434
+ {
435
+ text: 'Select',
436
+ sections: [{
437
+ title: 'Available options',
438
+ rows: [
439
+ { header: '', title: 'Secret Ingredient', description: '', id: '#SecretIngredient' },
440
+ { header: '', title: 'Coupon', description: '', id: '#CouponCode' }
441
+ ]
442
+ }]
443
+ }
444
+ ]
445
+ })
446
+ ```
447
+
387
448
  ### Native flow
388
449
 
389
450
  ```js
@@ -406,6 +467,89 @@ await sock.sendRichWebview(jid, {
406
467
  })
407
468
  ```
408
469
 
470
+ The lower-level `sendMessage` form also supports native-flow options, offer metadata, calls, copy actions, and webviews:
471
+
472
+ ```js
473
+ await sock.sendMessage(jid, {
474
+ image: { url: 'https://example.com/image.jpg' },
475
+ caption: 'Interactive options',
476
+ footer: 'CODY AI',
477
+ optionText: 'Select options',
478
+ optionTitle: 'Select options',
479
+ offerText: 'New coupon',
480
+ offerCode: 'SAVE20',
481
+ offerUrl: 'https://example.com/coupon',
482
+ offerExpiration: Date.now() + 3600000,
483
+ nativeFlow: [
484
+ { text: 'Greeting', id: '#Greeting', icon: 'review' },
485
+ { text: 'Call', call: '2348000000000' },
486
+ { text: 'Copy', copy: 'CODY AI' },
487
+ { text: 'Source', url: 'https://example.com', useWebview: true },
488
+ {
489
+ text: 'Select',
490
+ icon: 'default',
491
+ sections: [{
492
+ title: 'Available options',
493
+ rows: [{ header: '', title: 'Coupon', description: '', id: '#CouponCode' }]
494
+ }]
495
+ }
496
+ ],
497
+ interactiveAsTemplate: false
498
+ })
499
+ ```
500
+
501
+ Carousel cards and an audio footer are supported by the same `sendMessage` shape:
502
+
503
+ ```js
504
+ await sock.sendMessage(jid, {
505
+ text: 'Choose a service',
506
+ footer: 'CODY AI',
507
+ cards: [
508
+ {
509
+ image: { url: 'https://example.com/service-a.jpg' },
510
+ caption: 'Service A',
511
+ footer: 'CODY AI',
512
+ nativeFlow: [{ text: 'Open source', url: 'https://example.com/a', useWebview: true }]
513
+ },
514
+ {
515
+ image: { url: 'https://example.com/service-b.jpg' },
516
+ caption: 'Service B',
517
+ footer: 'CODY AI',
518
+ offerText: 'New coupon',
519
+ offerCode: 'SAVE20',
520
+ offerUrl: 'https://example.com/coupon',
521
+ offerExpiration: Date.now() + 3600000,
522
+ nativeFlow: [{ text: 'Open source', url: 'https://example.com/b' }]
523
+ }
524
+ ]
525
+ })
526
+
527
+ await sock.sendMessage(jid, {
528
+ text: 'Music in footer',
529
+ audioFooter: { url: 'https://example.com/footer.mp3' },
530
+ nativeFlow: [
531
+ { text: 'Next', id: '#Next', icon: 'review' },
532
+ { text: 'Skip', id: '#Skip', icon: 'default' }
533
+ ]
534
+ })
535
+ ```
536
+
537
+ ### Hydrated templates
538
+
539
+ ```js
540
+ await sock.sendMessage(jid, {
541
+ title: 'CODY AI',
542
+ image: { url: 'https://example.com/image.jpg' },
543
+ caption: 'Choose an action',
544
+ footer: 'Plogme',
545
+ templateButtons: [
546
+ { text: 'Open menu', id: '#Menu' },
547
+ { text: 'Open source', url: 'https://example.com' },
548
+ { text: 'Call support', call: '2348000000000' }
549
+ ]
550
+ })
551
+ ```
552
+
409
553
  ### Interactive table
410
554
 
411
555
  ```js
@@ -624,6 +768,63 @@ await sock.sendPlogmeMessage(jid, {
624
768
 
625
769
  The compatibility edit can be disabled for a specific send with `{ renderRichResponse: false }`. This sends the generated rich envelope once without the follow-up edit.
626
770
 
771
+ ### Rich response arrays
772
+
773
+ For mixed rich content, `richResponse` accepts text, code, and table items in one generated response:
774
+
775
+ ```js
776
+ await sock.sendMessage(jid, {
777
+ disclaimerText: 'CODY AI result',
778
+ richResponse: [
779
+ { text: 'Build result' },
780
+ { language: 'javascript', code: [{ highlightType: 0, codeContent: 'console.log("online")' }] },
781
+ { text: 'Runtime comparison:' },
782
+ {
783
+ title: 'Runtime',
784
+ table: [
785
+ { isHeading: true, items: ['', 'Node.js', 'Bun'] },
786
+ { isHeading: false, items: ['State', 'Online', 'Online'] }
787
+ ]
788
+ }
789
+ ]
790
+ })
791
+ ```
792
+
793
+ ### Rich URL previews and verified media
794
+
795
+ ```js
796
+ await sock.sendMessage(jid, {
797
+ text: 'https://example.com/docs',
798
+ richPreview: true
799
+ })
800
+
801
+ await sock.sendMessage(jid, {
802
+ text: 'https://example.com/docs',
803
+ richPreview: true,
804
+ previewTitle: 'CODY AI documentation',
805
+ previewDescription: 'Open the verified documentation'
806
+ })
807
+
808
+ await sock.sendMessage(jid, {
809
+ image: { url: 'https://example.com/verified-image.jpg' },
810
+ caption: 'Verified image',
811
+ verifiedMe: true
812
+ })
813
+ ```
814
+
815
+ `verifiedMe` applies to image and video messages. Rich previews require a `text` field containing the URL; preview rendering remains client-dependent.
816
+
817
+ ### Secured Meta service label
818
+
819
+ To request the supported secured Meta service label on an outgoing message:
820
+
821
+ ```js
822
+ await sock.sendMessage(jid, {
823
+ text: 'CODY AI secured response',
824
+ secureMetaServiceLabel: true
825
+ })
826
+ ```
827
+
627
828
  ## Rich menus, reels, and grids
628
829
 
629
830
  ### Original Gen4 rich menu
@@ -633,13 +834,19 @@ Use `sock.richMenu` for the original Gen4-style menu. This is the primary rich-m
633
834
  ```js
634
835
  await sock.richMenu(jid, {
635
836
  header: {
636
- title: 'Deployment tools'
837
+ title: 'Deployment tools',
838
+ image: {
839
+ url: 'https://example.com/cover.jpg',
840
+ mimeType: 'image/jpeg',
841
+ width: 1200,
842
+ height: 800
843
+ }
637
844
  },
638
845
  body: {
639
846
  title: 'Choose an action',
640
847
  buttons: [
641
- { id: 'deploy', text: 'Deploy' },
642
- { id: 'logs', text: 'View logs' }
848
+ { id: 'deploy', text: 'Deploy', toast: 'Deploy selected' },
849
+ { id: 'logs', text: 'View logs', toast: 'Logs selected' }
643
850
  ]
644
851
  },
645
852
  footer: {
@@ -649,6 +856,33 @@ await sock.richMenu(jid, {
649
856
  })
650
857
  ```
651
858
 
859
+ ### Universal A2UI message
860
+
861
+ `sendA2UIMessage` is the generic sender. It accepts arbitrary A2UI components, Bloks widget data, native-flow buttons, and optional media; it is not restricted to the command-table template. It also uses the immediate relay/edit compatibility path.
862
+
863
+ ```js
864
+ await sock.sendA2UIMessage(jid, {
865
+ bloksWidget: {
866
+ type: 'im_a2ui',
867
+ data: {
868
+ version: 'v0.9',
869
+ createSurface: {
870
+ surfaceId: 'custom-card',
871
+ catalogId: 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json',
872
+ components: [
873
+ { id: 'root', component: 'Column', children: ['title'] },
874
+ { id: 'title', component: 'Text', text: 'My custom A2UI card', variant: 'h2' }
875
+ ]
876
+ }
877
+ },
878
+ fallback: 'My custom A2UI card'
879
+ },
880
+ nativeFlowMessage: {
881
+ buttons: [{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ id: 'open', display_text: 'Open' }) }]
882
+ }
883
+ })
884
+ ```
885
+
652
886
  ### Large A2UI command card
653
887
 
654
888
  Use `sendA2UICommandMenu` for the full-size card layout with an uploaded image header, a command/function table, URL buttons, and a native-flow command selector. This is an `interactiveMessage`/A2UI payload, not an AIRich `unifiedResponse`, so it is intentionally separate from `sendPlogmeMessage` and `richMenu`.
@@ -720,6 +954,41 @@ await sock.richMenu(jid, {
720
954
 
721
955
  Validate button IDs on the server. A button ID is an event value, not an authorization grant.
722
956
 
957
+ ### Universal social/entity cards
958
+
959
+ The leaked profile-result format is exposed through a provider-neutral API. It supports Instagram and other providers without hard-coding the Instagram wire format. Every send uses the same immediate AIRich relay/edit path as the other rich messages.
960
+
961
+ ```js
962
+ await sock.sendSocialEntity(jid, {
963
+ platform: 'YOUTUBE',
964
+ username: 'crysnovax',
965
+ title: 'CODY AI',
966
+ imageUrl: 'https://example.com/avatar.png',
967
+ entityUrl: 'https://www.youtube.com/@crysnovax',
968
+ fullName: 'CRYSNOVAX',
969
+ isVerified: true,
970
+ resultText: 'See results'
971
+ })
972
+ ```
973
+
974
+ ### Instagram profile card
975
+
976
+ Instagram is the primary dedicated convenience API. It emits the `IG_PROFILE` social entity metadata while sharing the universal implementation.
977
+
978
+ ```js
979
+ await sock.sendInstagramProfile(jid, {
980
+ username: 'crysnovax',
981
+ title: 'CODY AI',
982
+ subtitle: '⚉',
983
+ imageUrl: 'https://example.com/avatar.png',
984
+ entityId: '123456',
985
+ entityUrl: 'https://www.instagram.com/crysnovax',
986
+ fullName: 'CRYSNOVAX',
987
+ isVerified: true,
988
+ resultText: 'See results'
989
+ })
990
+ ```
991
+
723
992
  ### Reels and rich media
724
993
 
725
994
  `sendReels` accepts an array of reel records. The main rich-media path is used for reels, sources, links, tables, and future rich payloads.
@@ -948,6 +1217,25 @@ Flow identifiers and tokens must belong to a valid business configuration.
948
1217
 
949
1218
  ## View-once utilities
950
1219
 
1220
+ Text view-once messages use the client-compatible inner `ExtendedTextMessage.viewOnce` flag:
1221
+
1222
+ ```js
1223
+ await sock.sendMessage(jid, {
1224
+ text: 'viewOnceMessage',
1225
+ viewOnce: true
1226
+ })
1227
+ ```
1228
+
1229
+ Media view-once messages continue to use WhatsApp's standard outer `viewOnceMessage` envelope:
1230
+
1231
+ ```js
1232
+ await sock.sendMessage(jid, {
1233
+ image: { url: 'https://example.com/secret.jpg' },
1234
+ caption: 'Open once',
1235
+ viewOnce: true
1236
+ })
1237
+ ```
1238
+
951
1239
  ```js
952
1240
  const info = sock.getViewOnceInfo(message)
953
1241
  console.log(info)
@@ -1013,3 +1301,1121 @@ For implementation questions, include the Node.js version, Plogme version, opera
1013
1301
  [1]: https://github.com/crysnovax/baileys "Plogme source repository"
1014
1302
  [2]: https://www.npmjs.com/package/plogme "Plogme on npm"
1015
1303
  [3]: https://github.com/WhiskeySockets/Baileys "Baileys protocol implementation"
1304
+
1305
+ ---
1306
+
1307
+ # Additional APIs from the Bailey2 compatibility surface
1308
+
1309
+ The following reference sections document supported APIs that are not repeated in the core CODY/Plogme guide above. They are retained from the `crysnovax/baileys2` compatibility documentation and exclude package-specific ban-checker functionality.
1310
+
1311
+ ## Welcome Flow
1312
+
1313
+ Auto-greet new contacts with an interactive FAQ menu. Fires once per contact, never repeats.
1314
+
1315
+ ### Basic Setup
1316
+
1317
+ ```javascript
1318
+ import { createWelcomeFlow } from '@crysnovax/baileys'
1319
+
1320
+ const welcome = createWelcomeFlow(sock, {
1321
+ greeting: 'Welcome! How can I help you today?',
1322
+ footer: 'Powered by MyBot',
1323
+ buttonText: 'Choose an option',
1324
+ sectionTitle: 'How can we help?',
1325
+ faqs: [
1326
+ { id: 'order', title: 'Track my order', description: 'Check order status' },
1327
+ { id: 'billing', title: 'Billing & payments', description: 'Payment issues' },
1328
+ { id: 'support', title: 'Technical support', description: 'Get help' },
1329
+ { id: 'human', title: 'Talk to a human', description: 'Connect with staff' }
1330
+ ]
1331
+ })
1332
+
1333
+ welcome.listen() // start
1334
+ ```
1335
+
1336
+ ### With Callbacks
1337
+
1338
+ ```javascript
1339
+ const welcome = createWelcomeFlow(sock, {
1340
+ greeting: 'Hi there! What brings you here today?',
1341
+ faqs: [
1342
+ { id: 'pricing', title: 'Pricing', description: 'Plans and costs' },
1343
+ { id: 'demo', title: 'Request demo', description: 'See it in action' },
1344
+ { id: 'support', title: 'Support', description: 'Get help' }
1345
+ ],
1346
+
1347
+ onGreet: async (jid, message) => {
1348
+ console.log(`Greeted new contact: ${jid}`)
1349
+ },
1350
+
1351
+ onFaqReply: async (jid, faqId, message) => {
1352
+ switch (faqId) {
1353
+ case 'pricing':
1354
+ await sock.sendMessage(jid, { text: 'Our plans start at $9/month…' })
1355
+ break
1356
+ case 'demo':
1357
+ await sock.sendMessage(jid, { text: 'Book a demo here: https://…' })
1358
+ break
1359
+ case 'support':
1360
+ await sock.sendMessage(jid, { text: 'Describe your issue and we will help!' })
1361
+ break
1362
+ }
1363
+ }
1364
+ })
1365
+
1366
+ welcome.listen()
1367
+ ```
1368
+
1369
+ ### Persist Across Restarts
1370
+
1371
+ ```javascript
1372
+ const welcome = createWelcomeFlow(sock, {
1373
+ greeting: 'Welcome!',
1374
+ faqs: [...],
1375
+ persistPath: './data/greeted-contacts.json'
1376
+ })
1377
+ ```
1378
+
1379
+ ### Control Methods
1380
+
1381
+ ```javascript
1382
+ welcome.listen() // start listening
1383
+ welcome.stop() // stop listening
1384
+ welcome.reset(jid) // force re-greet one contact
1385
+ welcome.resetAll() // clear all seen contacts
1386
+ welcome.hasGreeted(jid) // check if greeted → boolean
1387
+ ```
1388
+
1389
+ ### Config Reference
1390
+
1391
+ | Option | Type | Default | Description |
1392
+ |--------|------|---------|-------------|
1393
+ | greeting | string | 'Welcome!…' | Body text of welcome message |
1394
+ | footer | string | 'Powered by @crysnovax/baileys' | Footer text |
1395
+ | buttonText | string | 'Choose an option' | List button label |
1396
+ | sectionTitle | string | 'How can we help?' | Section header |
1397
+ | faqs | Array | 4 defaults | { id, title, description } |
1398
+ | typingDelayMs | number | 1200 | Typing indicator duration |
1399
+ | persistPath | string\|null | null | JSON file to persist JIDs |
1400
+ | ignoreGroups | boolean | true | Skip group chats |
1401
+ | ignoreNewsletter | boolean | true | Skip newsletters |
1402
+ | ignoreBroadcast | boolean | true | Skip broadcasts |
1403
+ | onGreet | async fn | null | Called after greeting sent |
1404
+ | onFaqReply | async fn | null | Called on FAQ selection |
1405
+
1406
+ ---
1407
+
1408
+ ## Payment Messages
1409
+
1410
+ ```javascript
1411
+ // Payment invite
1412
+ sock.sendMessage(jid, {
1413
+ paymentInviteServiceType: 3 // 1, 2, or 3
1414
+ })
1415
+
1416
+ // Invoice (not supported yet)
1417
+ sock.sendMessage(jid, {
1418
+ image: { url: './image.jpg' },
1419
+ invoiceNote: 'Invoice'
1420
+ })
1421
+
1422
+ // Order
1423
+ sock.sendMessage(jid, {
1424
+ orderText: 'Order',
1425
+ thumbnail: fs.readFileSync('./image.jpg')
1426
+ })
1427
+
1428
+ // Request payment
1429
+ sock.sendMessage(jid, {
1430
+ text: 'Request Payment',
1431
+ requestPaymentFrom: '0@s.whatsapp.net'
1432
+ })
1433
+ ```
1434
+
1435
+ ---
1436
+
1437
+ ## Message Options
1438
+
1439
+ ```javascript
1440
+ // AI icon (private chat only)
1441
+ sock.sendMessage(jid, {
1442
+ image: { url: './image.jpg' },
1443
+ caption: 'With AI icon!',
1444
+ ai: true
1445
+ })
1446
+
1447
+ // Ephemeral
1448
+ sock.sendMessage(jid, {
1449
+ image: { url: './image.jpg' },
1450
+ caption: 'Ephemeral',
1451
+ ephemeral: true
1452
+ })
1453
+
1454
+ // External ad reply
1455
+ sock.sendMessage(jid, {
1456
+ text: 'External Ad Reply',
1457
+ externalAdReply: {
1458
+ title: 'Did you know?',
1459
+ body: 'I dont know',
1460
+ thumbnail: fs.readFileSync('./image.jpg'),
1461
+ largeThumbnail: false,
1462
+ url: 'https://example.com'
1463
+ }
1464
+ })
1465
+
1466
+ // Group status (a valid group JID is required)
1467
+ const groupStatus = await sock.sendMessage(groupJid, {
1468
+ image: { url: './image.jpg' },
1469
+ caption: 'Group Status!',
1470
+ groupStatus: true
1471
+ })
1472
+
1473
+ // Text, video, audio, stickers, mentions, and rich previews use the same flag.
1474
+ await sock.sendMessage(groupJid, {
1475
+ text: 'Group update',
1476
+ mentions: ['628123456789@s.whatsapp.net'],
1477
+ groupStatus: true
1478
+ })
1479
+
1480
+ // Delete using the key returned by the original group-status send.
1481
+ // The group JID must match key.remoteJid when that field is present.
1482
+ await sock.deleteGroupStatus(groupJid, groupStatus.key)
1483
+
1484
+ // Report APIs submit the report first, then apply the requested safety action.
1485
+ // Contacts are blocked and groups are left only after WhatsApp accepts the report.
1486
+ await sock.reportContact('628123456789@s.whatsapp.net', [message.key])
1487
+ await sock.reportGroup(groupJid, [message.key])
1488
+
1489
+ // Lottie sticker
1490
+ sock.sendMessage(jid, {
1491
+ sticker: { url: './sticker.webp' },
1492
+ isLottie: true
1493
+ })
1494
+
1495
+ // likeThis — relay message exactly as-is, zero processing
1496
+ // Bypasses generateWAMessage entirely — no re-encoding, no normalization.
1497
+ // Useful for: forwarding with original quality, relaying captured messages verbatim,
1498
+ // re-sending albums/carousels without re-uploading, testing raw proto constructions.
1499
+ sock.sendMessage(jid, {
1500
+ likeThis: true,
1501
+ ...capturedMessage.message // spread any received message directly
1502
+ })
1503
+
1504
+ // Or with a manually built proto
1505
+ sock.sendMessage(jid, {
1506
+ likeThis: true,
1507
+ imageMessage: { ...rawProtoFields }
1508
+ })
1509
+
1510
+ // Or forwarding with original quality intact
1511
+ sock.sendMessage(jid, {
1512
+ likeThis: true,
1513
+ extendedTextMessage: {
1514
+ text: 'Built manually',
1515
+ contextInfo: {
1516
+ externalAdReply: {
1517
+ title: '@crysnovax/baileys',
1518
+ thumbnail: fs.readFileSync('./image.jpg'),
1519
+ sourceApp: 'whatsapp',
1520
+ showAdAttribution: true,
1521
+ mediaType: 1
1522
+ }
1523
+ }
1524
+ }
1525
+ })
1526
+
1527
+ // Secure Meta service label: adds actual_actors, host_storage, and
1528
+ // privacy_mode_ts to the outbound business node. The control flag itself
1529
+ // is consumed locally and is not encoded into the WhatsApp message.
1530
+ sock.sendMessage(jid, {
1531
+ text: 'Just a label!',
1532
+ secureMetaServiceLabel: true
1533
+ })
1534
+
1535
+ // Spoiler (compatible context flag; no unsupported future-proof wrapper)
1536
+ sock.sendMessage(jid, {
1537
+ image: { url: './image.jpg' },
1538
+ caption: 'Spoiler',
1539
+ spoiler: true
1540
+ })
1541
+
1542
+ // View once
1543
+ sock.sendMessage(jid, {
1544
+ image: { url: './image.jpg' },
1545
+ caption: 'View Once',
1546
+ viewOnce: true
1547
+ })
1548
+
1549
+ // View once v2
1550
+ sock.sendMessage(jid, {
1551
+ image: { url: './image.jpg' },
1552
+ caption: 'View Once V2',
1553
+ viewOnceV2: true
1554
+ })
1555
+
1556
+ // View once v2 extension
1557
+ sock.sendMessage(jid, {
1558
+ image: { url: './image.jpg' },
1559
+ caption: 'View Once V2 Extension',
1560
+ viewOnceV2Extension: true
1561
+ })
1562
+ ```
1563
+
1564
+ ---
1565
+
1566
+ ## Status & Stories
1567
+
1568
+ Statuses are sent to `status@broadcast` and require at least one viewer JID. Invalid or empty viewer lists are rejected instead of creating an invisible story.
1569
+
1570
+ ```javascript
1571
+ const viewers = [
1572
+ '628123456789@s.whatsapp.net',
1573
+ '628987654321@s.whatsapp.net'
1574
+ ]
1575
+
1576
+ // Standard API: styled text status. Fonts 0-9 are supported.
1577
+ await sock.sendMessage('status@broadcast', {
1578
+ text: 'Hello world!',
1579
+ backgroundColor: '#FF1FA15A',
1580
+ font: 0
1581
+ }, { statusJidList: viewers })
1582
+
1583
+ // Convenience API (equivalent to the standard form)
1584
+ await sock.sendMessage('status@broadcast', {
1585
+ status: true,
1586
+ text: 'Exclusive status!',
1587
+ statusJidList: viewers,
1588
+ backgroundColor: 'teal',
1589
+ font: 2
1590
+ })
1591
+
1592
+ // Image and video statuses support captions
1593
+ await sock.sendMessage('status@broadcast', {
1594
+ status: true,
1595
+ image: { url: './image.jpg' },
1596
+ caption: 'My status!',
1597
+ statusJidList: viewers
1598
+ })
1599
+ await sock.sendMessage('status@broadcast', {
1600
+ status: true,
1601
+ video: { url: './video.mp4' },
1602
+ caption: 'Watch this!',
1603
+ statusJidList: viewers
1604
+ })
1605
+
1606
+ // Audio or voice-note status, including waveform/background metadata
1607
+ await sock.sendMessage('status@broadcast', {
1608
+ status: true,
1609
+ audio: { url: './audio.ogg' },
1610
+ mimetype: 'audio/ogg; codecs=opus',
1611
+ ptt: true,
1612
+ waveform: new Uint8Array([0, 20, 45, 80, 45, 20, 0]),
1613
+ backgroundColor: '#FF0057FF',
1614
+ statusJidList: viewers
1615
+ })
1616
+ ```
1617
+
1618
+ Named, hex, and ARGB colors are accepted. Viewer JIDs are normalized and deduplicated.
1619
+
1620
+ ### Status Mentions
1621
+
1622
+ Passing an array as the destination posts a status and sends mention notifications to those contacts or groups. This is intentionally separate from `statusJidList`, which controls viewers without mentioning them.
1623
+
1624
+ ```javascript
1625
+ await sock.sendMessage([jidA, groupJid], { text: 'Hey, check my status!' })
1626
+ ```
1627
+
1628
+ ---
1629
+
1630
+ ## Favorites
1631
+
1632
+ Manage your WhatsApp Favorites list — add, remove, and sync across devices.
1633
+
1634
+ ### Add to Favorites
1635
+
1636
+ ```javascript
1637
+ // Add a single JID
1638
+ await sock.addToFavorites(['628123456789@s.whatsapp.net'])
1639
+
1640
+ // Add multiple JIDs (replaces entire list — merge handled by caller)
1641
+ await sock.addToFavorites([
1642
+ '628123456789@s.whatsapp.net',
1643
+ '628987654321@s.whatsapp.net',
1644
+ '1234@g.us'
1645
+ ])
1646
+ ```
1647
+
1648
+ ### Remove from Favorites
1649
+
1650
+ ```javascript
1651
+ // Remove specific JIDs (pass remaining list after filter)
1652
+ await sock.removeFromFavorites([
1653
+ '628123456789@s.whatsapp.net' // only this one remains
1654
+ ])
1655
+ ```
1656
+
1657
+ ### Full Example with Local Persistence
1658
+
1659
+ ```javascript
1660
+ import fs from 'fs'
1661
+ import path from 'path'
1662
+
1663
+ const FAV_FILE = path.join(process.cwd(), 'database', 'favorites.json')
1664
+
1665
+ const loadFavs = () => {
1666
+ try { return JSON.parse(fs.readFileSync(FAV_FILE, 'utf8')) }
1667
+ catch { return [] }
1668
+ }
1669
+
1670
+ const saveFavs = (favs) => {
1671
+ fs.mkdirSync(path.dirname(FAV_FILE), { recursive: true })
1672
+ fs.writeFileSync(FAV_FILE, JSON.stringify(favs, null, 2))
1673
+ }
1674
+
1675
+ // Add
1676
+ const existing = loadFavs()
1677
+ const target = '628123456789@s.whatsapp.net'
1678
+ if (!existing.includes(target)) {
1679
+ const merged = [...existing, target]
1680
+ await sock.addToFavorites(merged)
1681
+ saveFavs(merged)
1682
+ }
1683
+
1684
+ // Remove
1685
+ const toRemove = '628123456789@s.whatsapp.net'
1686
+ const remaining = existing.filter(jid => jid !== toRemove)
1687
+ await sock.addToFavorites(remaining)
1688
+ saveFavs(remaining)
1689
+ ```
1690
+
1691
+ **Note:** WhatsApp favorites use a single app state record. Each SET replaces the entire list. Always pass the complete desired array.
1692
+
1693
+ ---
1694
+
1695
+ ## Newsletter Management
1696
+
1697
+ ```javascript
1698
+ // Create
1699
+ sock.newsletterCreate('@crysnovax/baileys', 'Fresh updates weekly')
1700
+
1701
+ // Metadata
1702
+ const metadata = await sock.newsletterMetadata('1231111111111@newsletter')
1703
+
1704
+ // Subscribers count
1705
+ const subscribers = await sock.newsletterSubscribers('1231111111111@newsletter')
1706
+
1707
+ // Follow / Unfollow
1708
+ sock.newsletterFollow('1231111111111@newsletter')
1709
+ sock.newsletterUnfollow('1231111111111@newsletter')
1710
+
1711
+ // Mute / Unmute
1712
+ sock.newsletterMute('1231111111111@newsletter')
1713
+ sock.newsletterUnmute('1231111111111@newsletter')
1714
+
1715
+ // Admin management
1716
+ sock.newsletterDemote('1231111111111@newsletter', '6281111111111@s.whatsapp.net')
1717
+ sock.newsletterChangeOwner('1231111111111@newsletter', '6281111111111@s.whatsapp.net')
1718
+
1719
+ // Update
1720
+ sock.newsletterUpdate('1231111111111@newsletter', { name: '@crysnovax/baileys' })
1721
+ sock.newsletterUpdateName('1231111111111@newsletter', '@crysnovax/baileys')
1722
+ sock.newsletterUpdateDescription('1231111111111@newsletter', 'Fresh updates weekly')
1723
+ sock.newsletterUpdatePicture('1231111111111@newsletter', { url: 'path/to/image.jpg' })
1724
+ sock.newsletterRemovePicture('1231111111111@newsletter')
1725
+
1726
+ // React to message
1727
+ sock.newsletterReactMessage('1231111111111@newsletter', '100', '💛')
1728
+
1729
+ // Admin count
1730
+ const count = await sock.newsletterAdminCount('1231111111111@newsletter')
1731
+
1732
+ // Get all subscribed
1733
+ const newsletters = await sock.newsletterSubscribed()
1734
+
1735
+ // Fetch messages
1736
+ const messages = sock.newsletterFetchMessages('jid', '1231111111111@newsletter', 50, 0, 0)
1737
+
1738
+ // Delete
1739
+ sock.newsletterDelete('1231111111111@newsletter')
1740
+ ```
1741
+
1742
+ ---
1743
+
1744
+ ## Group Management
1745
+
1746
+ ```javascript
1747
+ // Create
1748
+ const group = await sock.groupCreate('@crysnovax/baileys', ['628123456789@s.whatsapp.net'])
1749
+
1750
+ // Metadata
1751
+ const metadata = await sock.groupMetadata(jid)
1752
+
1753
+ // Invite
1754
+ const inviteCode = await sock.groupInviteCode(jid)
1755
+ sock.groupRevokeInvite(jid)
1756
+ sock.groupAcceptInvite(inviteCode)
1757
+
1758
+ // Leave
1759
+ sock.groupLeave(jid)
1760
+
1761
+ // Participants
1762
+ sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'add')
1763
+ sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'remove')
1764
+ sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'promote')
1765
+ sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'demote')
1766
+
1767
+ // Join requests
1768
+ sock.groupRequestParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'approve')
1769
+
1770
+ // Info updates
1771
+ sock.groupUpdateSubject(jid, '@crysnovax/baileys')
1772
+ sock.groupUpdateDescription(jid, 'Updated description')
1773
+ sock.updateProfilePicture(jid, { url: 'path/to/image.jpg' })
1774
+ sock.removeProfilePicture(jid)
1775
+
1776
+ // Settings
1777
+ sock.groupSettingUpdate(jid, 'announcement') // admin only chat
1778
+ sock.groupSettingUpdate(jid, 'not_announcement') // open chat
1779
+ sock.groupSettingUpdate(jid, 'locked') // admin only edit info
1780
+ sock.groupSettingUpdate(jid, 'unlocked') // all edit info
1781
+
1782
+ // Add mode
1783
+ sock.groupMemberAddMode(jid, 'admin_add')
1784
+ sock.groupMemberAddMode(jid, 'all_member_add')
1785
+
1786
+ // Ephemeral
1787
+ sock.groupToggleEphemeral(jid, 86400) // enable
1788
+ sock.groupToggleEphemeral(jid, 0) // disable
1789
+
1790
+ // Approval mode
1791
+ sock.groupJoinApprovalMode(jid, 'on')
1792
+ sock.groupJoinApprovalMode(jid, 'off')
1793
+
1794
+ // Fetch all
1795
+ const groups = await sock.groupFetchAllParticipating()
1796
+
1797
+ // Pending requests
1798
+ const requests = await sock.groupRequestParticipantsList(jid)
1799
+
1800
+ // Invite info
1801
+ const group = await sock.groupGetInviteInfo('ABC123456789')
1802
+
1803
+ // Member label
1804
+ sock.updateMemberLabel(jid, '@crysnovax/baileys')
1805
+ ```
1806
+
1807
+ ---
1808
+
1809
+ ## Community Management
1810
+
1811
+ ```javascript
1812
+ // Create
1813
+ const community = await sock.communityCreate('@crysnovax/baileys', 'Fresh updates weekly')
1814
+
1815
+ // Create subgroup
1816
+ const group = await sock.communityCreateGroup(
1817
+ 'Announcements',
1818
+ ['628123456789@s.whatsapp.net'],
1819
+ communityJid
1820
+ )
1821
+
1822
+ // Link / Unlink
1823
+ sock.communityLinkGroup(groupJid, communityJid)
1824
+ sock.communityUnlinkGroup(groupJid, communityJid)
1825
+
1826
+ // Metadata
1827
+ const metadata = await sock.communityMetadata(jid)
1828
+
1829
+ // Invite
1830
+ const inviteCode = await sock.communityInviteCode(jid)
1831
+ sock.communityRevokeInvite(jid)
1832
+ sock.communityAcceptInvite(inviteCode)
1833
+
1834
+ // Leave
1835
+ sock.communityLeave(jid)
1836
+
1837
+ // Join requests
1838
+ sock.communityRequestParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'approve')
1839
+
1840
+ // Updates
1841
+ sock.communityUpdateSubject(jid, '@crysnovax/baileys')
1842
+ sock.communityUpdateDescription(jid, 'Updated description')
1843
+
1844
+ // Settings
1845
+ sock.communitySettingUpdate(jid, 'announcement')
1846
+ sock.communitySettingUpdate(jid, 'not_announcement')
1847
+ sock.communitySettingUpdate(jid, 'locked')
1848
+ sock.communitySettingUpdate(jid, 'unlocked')
1849
+
1850
+ // Add mode
1851
+ sock.communityMemberAddMode(jid, 'admin_add')
1852
+ sock.communityMemberAddMode(jid, 'all_member_add')
1853
+
1854
+ // Ephemeral
1855
+ sock.communityToggleEphemeral(jid, 86400)
1856
+ sock.communityToggleEphemeral(jid, 0)
1857
+
1858
+ // Approval mode
1859
+ sock.communityJoinApprovalMode(jid, 'on')
1860
+ sock.communityJoinApprovalMode(jid, 'off')
1861
+
1862
+ // Fetch all
1863
+ const communities = await sock.communityFetchAllParticipating()
1864
+
1865
+ // Linked groups
1866
+ const linked = await sock.communityFetchLinkedGroups(jid)
1867
+
1868
+ // Pending requests
1869
+ const requests = await sock.communityRequestParticipantsList(jid)
1870
+
1871
+ // Invite info
1872
+ const community = await sock.communityGetInviteInfo('ABC123456789')
1873
+ ```
1874
+
1875
+ ---
1876
+
1877
+ ## Profile & Business
1878
+
1879
+ ```javascript
1880
+ // Profile picture
1881
+ const url = await sock.profilePictureUrl(jid, 'image')
1882
+
1883
+ // Standard — auto crop + resize to 720×720
1884
+ sock.updateProfilePicture(jid, buffer)
1885
+ sock.updateProfilePicture(jid, { url })
1886
+
1887
+ // Standard with custom dimensions
1888
+ sock.updateProfilePicture(jid, { url }, { width: 640, height: 640 })
1889
+
1890
+ // HD — preserves original aspect ratio, no crop, no padding
1891
+ // Images under 720px pass through unchanged.
1892
+ // Larger images are scaled down proportionally to fit within 720px.
1893
+ sock.updateProfilePicture(jid, buffer, { hd: true })
1894
+ sock.updateProfilePicture(jid, { url }, { hd: true })
1895
+
1896
+ sock.removeProfilePicture(jid)
1897
+
1898
+ // Profile info (name and personal About/bio)
1899
+ await sock.updateProfileName('My Name')
1900
+ await sock.updateProfileStatus('Available')
1901
+ // Business descriptions are separate: updateBusinessProfile({ description })
1902
+
1903
+ // Presence
1904
+ sock.sendPresenceUpdate('available', jid)
1905
+ sock.presenceSubscribe(jid)
1906
+
1907
+ // Read receipts
1908
+ sock.readMessages([message.key])
1909
+ sock.sendReceipt(jid, participant, [messageId], 'read')
1910
+
1911
+ // Block / Unblock
1912
+ sock.updateBlockStatus(jid, 'block')
1913
+ sock.updateBlockStatus(jid, 'unblock')
1914
+
1915
+ // Blocklist
1916
+ const blocked = await sock.fetchBlocklist()
1917
+
1918
+ // Chat modify
1919
+ sock.chatModify({
1920
+ archive: true,
1921
+ lastMessageOrig: message,
1922
+ lastMessage: message
1923
+ }, jid)
1924
+
1925
+ // Star
1926
+ sock.star(jid, [{ id: messageId, fromMe: true }], true)
1927
+
1928
+ // Contact
1929
+ sock.addOrEditContact(jid, { displayName: 'Name' })
1930
+ sock.removeContact(jid)
1931
+
1932
+ // Labels
1933
+ sock.addChatLabel(jid, labelId)
1934
+ sock.removeChatLabel(jid, labelId)
1935
+ sock.addMessageLabel(jid, messageId, labelId)
1936
+
1937
+ // App state sync
1938
+ sock.resyncAppState(['regular', 'critical_block'], true)
1939
+
1940
+ // Business profile
1941
+ const profile = await sock.getBusinessProfile(jid)
1942
+
1943
+ // Product create
1944
+ const product = await sock.productCreate({
1945
+ name: 'Product',
1946
+ description: 'Description',
1947
+ price: 100000,
1948
+ currency: 'IDR',
1949
+ originCountryCode: 'ID',
1950
+ images: [buffer, { url: './image.jpg' }]
1951
+ })
1952
+
1953
+ // Product update
1954
+ await sock.productUpdate(productId, {
1955
+ name: 'Product',
1956
+ description: 'Updated',
1957
+ price: 75000,
1958
+ currency: 'IDR',
1959
+ images: [{ url: './image.jpg' }]
1960
+ })
1961
+
1962
+ // Product delete
1963
+ sock.productDelete([productId])
1964
+
1965
+ // Catalog
1966
+ const { products, nextPageCursor } = await sock.getCatalog({
1967
+ jid: '628123456789@s.whatsapp.net',
1968
+ limit: 10
1969
+ })
1970
+
1971
+ // Collections
1972
+ const collections = await sock.getCollections('628123456789@s.whatsapp.net', 10)
1973
+
1974
+ // Order details
1975
+ const order = await sock.getOrderDetails(orderId, tokenBase64)
1976
+
1977
+ // Business profile update
1978
+ await sock.updateBusinessProfile({
1979
+ address: 'Jakarta, Indonesia',
1980
+ description: 'Official Store',
1981
+ websites: ['https://example.com'],
1982
+ email: 'email@example.com',
1983
+ hours: {
1984
+ timezone: 'Asia/Jakarta',
1985
+ days: [{ day: 'mon', mode: 'open_24h' }]
1986
+ }
1987
+ })
1988
+
1989
+ // Use empty strings or an empty websites array to clear profile fields.
1990
+ await sock.updateBusinessProfile({ description: '', websites: [] })
1991
+
1992
+ // Cover photo: updateCoverPhoto returns the uploaded cover ID and always
1993
+ // removes its local temporary upload file, including after failures.
1994
+ const coverId = await sock.updateCoverPhoto({ url: './image.jpg' })
1995
+ await sock.removeCoverPhoto(coverId)
1996
+
1997
+ // Quick replies
1998
+ sock.addOrEditQuickReply({
1999
+ shortcut: 'hello',
2000
+ message: 'Hello from business account'
2001
+ })
2002
+ sock.removeQuickReply(timestamp)
2003
+ ```
2004
+
2005
+ ---
2006
+
2007
+ ## Privacy & Security
2008
+
2009
+ ```javascript
2010
+ // Last seen
2011
+ sock.updateLastSeenPrivacy('all')
2012
+ sock.updateLastSeenPrivacy('contacts')
2013
+ sock.updateLastSeenPrivacy('contact_blacklist')
2014
+ sock.updateLastSeenPrivacy('nobody')
2015
+
2016
+ // Online
2017
+ sock.updateOnlinePrivacy('all')
2018
+ sock.updateOnlinePrivacy('match_last_seen')
2019
+
2020
+ // Profile picture
2021
+ sock.updateProfilePicturePrivacy('contacts')
2022
+
2023
+ // Status
2024
+ sock.updateStatusPrivacy('contacts')
2025
+
2026
+ // Read receipts
2027
+ sock.updateReadReceiptsPrivacy('all')
2028
+ sock.updateReadReceiptsPrivacy('none')
2029
+
2030
+ // Groups add
2031
+ sock.updateGroupsAddPrivacy('all')
2032
+ sock.updateGroupsAddPrivacy('contacts')
2033
+
2034
+ // Messages
2035
+ sock.updateMessagesPrivacy('all')
2036
+ sock.updateMessagesPrivacy('contacts')
2037
+ sock.updateMessagesPrivacy('nobody')
2038
+
2039
+ // Call
2040
+ sock.updateCallPrivacy('everyone')
2041
+
2042
+ // Default disappearing mode
2043
+ sock.updateDefaultDisappearingMode(86400)
2044
+
2045
+ // Link previews
2046
+ sock.updateDisableLinkPreviewsPrivacy(true)
2047
+ ```
2048
+
2049
+ ---
2050
+
2051
+ ## Utility Methods
2052
+
2053
+ ### LID / Phone Number Resolution
2054
+
2055
+ ```javascript
2056
+ // Resolve LID ↔ PN cross-mappings
2057
+ const userId = await sock.findUserId('628123456789@s.whatsapp.net')
2058
+ console.log(userId)
2059
+ // { lid: '1234567890@lid', phoneNumber: '628123456789@s.whatsapp.net' }
2060
+
2061
+ const userId2 = await sock.findUserId('1234567890@lid')
2062
+ console.log(userId2)
2063
+ // { lid: '1234567890@lid', phoneNumber: '628123456789@s.whatsapp.net' }
2064
+ ```
2065
+
2066
+ LID-to-phone resolution uses mappings learned from trusted WhatsApp envelopes, history, or PN-to-LID USync. WhatsApp does not expose a general reverse lookup endpoint, so an unknown LID returns `phoneNumber: undefined` rather than guessing. Primary devices resolve to canonical `number@s.whatsapp.net` JIDs; linked-device suffixes and hosted domains are preserved.
2067
+
2068
+ ### Fetch User Status
2069
+
2070
+ ```javascript
2071
+ // Fetch status metadata for one or more users
2072
+ const statusList = await sock.fetchStatus('628123456789@s.whatsapp.net', '628987654321@s.whatsapp.net')
2073
+ ```
2074
+
2075
+ ### Fetch Disappearing Duration
2076
+
2077
+ ```javascript
2078
+ // Get disappearing message duration for contacts
2079
+ const durations = await sock.fetchDisappearingDuration('628123456789@s.whatsapp.net')
2080
+ ```
2081
+
2082
+ ### Bot List
2083
+
2084
+ ```javascript
2085
+ // Fetch official bot list (v2)
2086
+ const bots = await sock.getBotListV2()
2087
+ // [{ jid: '...', personaId: '...' }]
2088
+ ```
2089
+
2090
+ ### Call Links
2091
+
2092
+ ```javascript
2093
+ // Create voice/video call link
2094
+ const token = await sock.createCallLink('audio') // voice call
2095
+ const token2 = await sock.createCallLink('video') // video call
2096
+ const token3 = await sock.createCallLink('video', { // with scheduled event
2097
+ startTime: Date.now() + 3600000
2098
+ })
2099
+ ```
2100
+
2101
+ ### Server Properties
2102
+
2103
+ ```javascript
2104
+ // Access server-assigned AB props
2105
+ console.log(sock.serverProps)
2106
+ // {
2107
+ // privacyTokenOn1to1: true, // tctoken on 1:1 messages
2108
+ // profilePicPrivacyToken: true, // tctoken on profile pic IQs
2109
+ // lidTrustedTokenIssueToLid: false // issue tctokens to LID
2110
+ // }
2111
+ ```
2112
+
2113
+ ### App State Sync
2114
+
2115
+ ```javascript
2116
+ // Force resync specific collections
2117
+ await sock.resyncAppState(['regular_high', 'regular_low'], false)
2118
+
2119
+ // Apply custom app patch
2120
+ await sock.appPatch({
2121
+ syncAction: { ... },
2122
+ index: ['...'],
2123
+ type: 'regular_low',
2124
+ apiVersion: 5,
2125
+ operation: proto.SyncdMutation.SyncdOperation.SET
2126
+ })
2127
+ ```
2128
+
2129
+ ### Clean Dirty Bits
2130
+
2131
+ ```javascript
2132
+ // Clear dirty bits for specific sync types
2133
+ await sock.cleanDirtyBits('account_sync', timestamp)
2134
+ ```
2135
+
2136
+ ---
2137
+
2138
+ ## Meta AI Features
2139
+
2140
+ Meta AI-style thinking indicators and live reasoning feeds. Works on all WhatsApp clients — no "Update WhatsApp" messages.
2141
+
2142
+ **Note:** These use plain text placeholders with typing indicators by default, avoiding unsupported-message bubbles. Final code blocks, tables, links, and rich responses are unchanged. Native Meta typing/thinking is force-only: set both `forceMetaRendering: true` in the socket config and `useNativeMeta: true` in the `metaTyping` or `sendMetaComposited` options.
2143
+
2144
+ ### Meta Typing Indicator
2145
+
2146
+ Show a live thinking indicator that you control. Delete it manually when ready — no "edited" badge ever appears.
2147
+
2148
+ ```javascript
2149
+ import { metaTyping, buildSteps } from '@crysnovax/baileys'
2150
+
2151
+ const placeholder = await metaTyping(sock, jid, {
2152
+ description: 'Thinking…',
2153
+ steps: buildSteps(['Reading your message…', 'Writing response…'])
2154
+ })
2155
+
2156
+ // Do your work here…
2157
+
2158
+ // Delete the indicator cleanly
2159
+ await sock.sendMessage(jid, { delete: placeholder.key })
2160
+
2161
+ // Send the real message
2162
+ await sock.sendMessage(jid, { text: 'Here is your answer!' })
2163
+ ```
2164
+
2165
+ What users see:
2166
+
2167
+ ```
2168
+ [typing… indicator]
2169
+
2170
+ _Thinking…_
2171
+ ○ Reading your message…
2172
+ ○ Writing response…
2173
+
2174
+ [auto-deletes]
2175
+
2176
+ Here is your answer!
2177
+ ```
2178
+
2179
+ ### Meta Compositing
2180
+
2181
+ Full flow: indicator shows → auto-deletes → clean final message lands. Works with every rich content type.
2182
+
2183
+ ```javascript
2184
+ import { sendMetaComposited, PlanningStepStatus } from '@crysnovax/baileys'
2185
+
2186
+ // With code block
2187
+ await sendMetaComposited(
2188
+ sock, jid,
2189
+ { code: 'const x = 1 + 1', language: 'javascript' },
2190
+ {
2191
+ thinkingMs: 3000,
2192
+ description: 'Analyzing…',
2193
+ steps: [
2194
+ { title: 'Reading context…', status: PlanningStepStatus.DONE },
2195
+ { title: 'Writing code…', status: PlanningStepStatus.IN_PROGRESS }
2196
+ ]
2197
+ }
2198
+ )
2199
+
2200
+ // With table
2201
+ await sendMetaComposited(
2202
+ sock, jid,
2203
+ {
2204
+ title: 'Comparison',
2205
+ table: [
2206
+ ['Feature', 'Baileys', 'Crysnovax'],
2207
+ ['Rich Messages', '❌', '✅'],
2208
+ ['Meta Compositing', '❌', '✅']
2209
+ ]
2210
+ },
2211
+ { thinkingMs: 2500, description: 'Building table…' }
2212
+ )
2213
+
2214
+ // With rich response array
2215
+ await sendMetaComposited(
2216
+ sock, jid,
2217
+ {
2218
+ richResponse: [
2219
+ { text: 'Here is your result:' },
2220
+ { code: 'console.log("hello")', language: 'javascript' },
2221
+ { text: 'Run it with `node index.js`' }
2222
+ ]
2223
+ },
2224
+ { thinkingMs: 2000 }
2225
+ )
2226
+ ```
2227
+
2228
+ ### Replay Planning
2229
+
2230
+ Live Meta AI-style reasoning feed — each step visibly completes in real time, then the final rich message lands clean.
2231
+
2232
+ ```javascript
2233
+ import { replayPlanning, mixedSteps } from '@crysnovax/baileys'
2234
+
2235
+ await replayPlanning(
2236
+ sock, jid,
2237
+
2238
+ // Steps — status managed automatically
2239
+ mixedSteps([
2240
+ { title: 'Understanding your question…', type: 'reasoning' },
2241
+ { title: 'Searching for data…', type: 'search' },
2242
+ { title: 'Writing the answer…' }
2243
+ ]),
2244
+
2245
+ // Final rich message
2246
+ { code: 'const answer = 42', language: 'javascript' },
2247
+
2248
+ // Options
2249
+ {
2250
+ description: 'Thinking…',
2251
+ stepDelayMs: 900,
2252
+ finalPauseMs: 600
2253
+ }
2254
+ )
2255
+ ```
2256
+
2257
+ What users see:
2258
+
2259
+ ```
2260
+ _Thinking…_
2261
+ ○ Understanding your question…
2262
+ ○ Searching for data…
2263
+ ○ Writing the answer…
2264
+
2265
+ [step 1 completes]
2266
+ _Thinking…_
2267
+ ✓ Understanding your question…
2268
+ ○ Searching for data…
2269
+ ○ Writing the answer…
2270
+
2271
+ [step 2 completes]
2272
+ _Thinking…_
2273
+ ✓ Understanding your question…
2274
+ ✓ Searching for data…
2275
+ ○ Writing the answer…
2276
+
2277
+ [all done, deletes, then:]
2278
+
2279
+ const answer = 42
2280
+ ```
2281
+
2282
+ Step type helpers:
2283
+
2284
+ ```javascript
2285
+ import {
2286
+ buildReasoningSteps, // isReasoning: true
2287
+ buildSearchSteps, // isEnhancedSearch: true
2288
+ mixedSteps, // mix any combination
2289
+ buildSteps // plain steps
2290
+ } from '@crysnovax/baileys'
2291
+
2292
+ // All reasoning
2293
+ buildReasoningSteps(['Analyzing the problem…', 'Checking edge cases…'])
2294
+
2295
+ // All search
2296
+ buildSearchSteps(['Searching the web…', 'Reading top results…'])
2297
+
2298
+ // Mixed — most realistic Meta AI look
2299
+ mixedSteps([
2300
+ { title: 'Reading your message…', type: 'reasoning' },
2301
+ { title: 'Searching sources…', type: 'search' },
2302
+ { title: 'Composing response…' }
2303
+ ])
2304
+ ```
2305
+
2306
+ Replay planning only (no final message):
2307
+
2308
+ ```javascript
2309
+ import { replayPlanningOnly, buildSearchSteps } from '@crysnovax/baileys'
2310
+
2311
+ await replayPlanningOnly(
2312
+ sock, jid,
2313
+ buildSearchSteps(['Looking up prices…', 'Comparing results…']),
2314
+ { stepDelayMs: 1200 }
2315
+ )
2316
+
2317
+ // Send whatever you want after — no badge, no trace
2318
+ await sock.sendMessage(jid, { text: 'Here are the results!' })
2319
+ ```
2320
+
2321
+ Options reference:
2322
+
2323
+ | Option | Type | Default | Description |
2324
+ |--------|------|---------|-------------|
2325
+ | description | string | 'Thinking…' | Top label on the indicator bubble |
2326
+ | placeholderText | string | '' | Body text while steps run |
2327
+ | stepDelayMs | number | 900 | Ms between each step completing |
2328
+ | finalPauseMs | number | 600 | Ms to hold after all steps done |
2329
+ | abortOnDisconnect | boolean | true | Stops cleanly if socket closes |
2330
+ | sendOptions | object | {} | Extra options for final sendMessage |
2331
+
2332
+ ---
2333
+
2334
+ ## Events Reference
2335
+
2336
+ ```javascript
2337
+ sock.ev.on('connection.update', (update) => {})
2338
+ sock.ev.on('creds.update', (update) => {})
2339
+ sock.ev.on('messaging-history.set', (update) => {})
2340
+ sock.ev.on('messaging-history.status', (update) => {})
2341
+ sock.ev.on('chats.upsert', (update) => {})
2342
+ sock.ev.on('chats.update', (update) => {})
2343
+ sock.ev.on('chats.delete', (update) => {})
2344
+ sock.ev.on('chats.lock', (update) => {})
2345
+ sock.ev.on('lid-mapping.update', (update) => {})
2346
+ sock.ev.on('presence.update', (update) => {})
2347
+ sock.ev.on('contacts.upsert', (update) => {})
2348
+ sock.ev.on('contacts.update', (update) => {})
2349
+ sock.ev.on('messages.delete', (update) => {})
2350
+ sock.ev.on('messages.update', (update) => {})
2351
+ sock.ev.on('messages.media-update', (update) => {})
2352
+ sock.ev.on('messages.upsert', (update) => {})
2353
+ sock.ev.on('messages.reaction', (update) => {})
2354
+ sock.ev.on('message-receipt.update', (update) => {})
2355
+ sock.ev.on('groups.upsert', (update) => {})
2356
+ sock.ev.on('groups.update', (update) => {})
2357
+ sock.ev.on('group-participants.update', (update) => {})
2358
+ sock.ev.on('group.join-request', (update) => {})
2359
+ sock.ev.on('group.member-tag.update', (update) => {})
2360
+ sock.ev.on('blocklist.set', (update) => {})
2361
+ sock.ev.on('blocklist.update', (update) => {})
2362
+ sock.ev.on('call', (update) => {})
2363
+ sock.ev.on('labels.edit', (update) => {})
2364
+ sock.ev.on('labels.association', (update) => {})
2365
+ sock.ev.on('newsletter.reaction', (update) => {})
2366
+ sock.ev.on('newsletter.view', (update) => {})
2367
+ sock.ev.on('newsletter-participants.update', (update) => {})
2368
+ sock.ev.on('newsletter-settings.update', (update) => {})
2369
+ sock.ev.on('settings.update', (update) => {})
2370
+ ```
2371
+
2372
+ ---
2373
+
2374
+ ## Image Processing
2375
+
2376
+ Auto-detects available libraries: sharp, @napi-rs/image, or jimp.
2377
+
2378
+ ```javascript
2379
+ import { getImageProcessingLibrary } from '@crysnovax/baileys'
2380
+ import { readFile } from 'fs/promises'
2381
+
2382
+ const lib = await getImageProcessingLibrary()
2383
+ const bufferOrFilePath = './image.jpg'
2384
+ const width = 512
2385
+ let output
2386
+
2387
+ // Sharp
2388
+ if (lib.sharp?.default) {
2389
+ const img = lib.sharp.default(bufferOrFilePath)
2390
+ output = await img.resize(width).jpeg({ quality: 80 }).toBuffer()
2391
+ }
2392
+
2393
+ // NAPI-RS Image
2394
+ else if (lib.image?.Transformer) {
2395
+ const inputBuffer = Buffer.isBuffer(bufferOrFilePath)
2396
+ ? bufferOrFilePath
2397
+ : await readFile(bufferOrFilePath)
2398
+ const img = new lib.image.Transformer(inputBuffer)
2399
+ output = await img.resize(width, undefined, 0).jpeg(50)
2400
+ }
2401
+
2402
+ // Jimp
2403
+ else if (lib.jimp?.Jimp) {
2404
+ const img = await lib.jimp.Jimp.read(bufferOrFilePath)
2405
+ output = await img
2406
+ .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
2407
+ .getBuffer('image/jpeg', { quality: 50 })
2408
+ }
2409
+
2410
+ else {
2411
+ throw new Error('No image processing library available')
2412
+ }
2413
+ ```
2414
+
2415
+ ---
2416
+
2417
+ ## Credits and attribution
2418
+
2419
+ Plogme/CODY is a downstream fork and enhancement of the Baileys protocol implementation by [WhiskeySockets/Baileys](https://github.com/WhiskeySockets/Baileys). The underlying WhatsApp Web protocol work, structure, and many foundational APIs come from WhiskeySockets. Additional changes in this fork include CODY/Plogme rich-message composition, immediate AIRich rendering relay, A2UI and Gen4 menu helpers, albums, video grids, and related documentation.
2420
+
2421
+ This README also incorporates compatible API examples from [crysnovax/baileys2](https://github.com/crysnovax/baileys2). Custom package-specific ban-checker APIs are intentionally not documented here.