ishumdz-bail 1.0.18 → 1.0.19
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 +30 -13
- package/lib/Utils/messages.js +85 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -416,15 +416,18 @@ await sock.sendMessage(jid, {
|
|
|
416
416
|
|
|
417
417
|
## 🔘 Horizontal Buttons (Interactive Messages)
|
|
418
418
|
|
|
419
|
-
> ⚡ **
|
|
420
|
-
>
|
|
421
|
-
>
|
|
422
|
-
>
|
|
419
|
+
> ⚡ **How it renders:** the `buttons` API sends the **classic ButtonsMessage** — the
|
|
420
|
+
> only format WhatsApp renders as a true **horizontal button row**. Modern clients
|
|
421
|
+
> render it when the message has a **header** (image / video / GIF / location map-card).
|
|
422
|
+
> A **location header needs no media upload** — coordinates alone give the big map
|
|
423
|
+
> card + horizontal row, and a **custom image or GIF** works with one media upload.
|
|
424
|
+
> Set `{ nativeFlow: true }` for vertical native-flow quick_reply buttons instead.
|
|
423
425
|
|
|
424
|
-
### Text + Horizontal Buttons
|
|
426
|
+
### Text + Horizontal Buttons (location header = zero uploads)
|
|
425
427
|
|
|
426
428
|
```javascript
|
|
427
429
|
await sock.sendMessage(jid, {
|
|
430
|
+
location: { degreesLatitude: 9.9312, degreesLongitude: 76.2673 }, // simple map card
|
|
428
431
|
text: 'Pong 9 Ms ⚡\nDeveloper: ISHAN-X × LOVELY',
|
|
429
432
|
footer: 'ISHAN-X MD PRO',
|
|
430
433
|
buttons: [
|
|
@@ -434,10 +437,10 @@ await sock.sendMessage(jid, {
|
|
|
434
437
|
})
|
|
435
438
|
```
|
|
436
439
|
|
|
437
|
-
###
|
|
440
|
+
### Custom Pic / GIF + Horizontal Buttons
|
|
438
441
|
|
|
439
442
|
```javascript
|
|
440
|
-
// Image header
|
|
443
|
+
// Image header (one media upload, then horizontal row)
|
|
441
444
|
await sock.sendMessage(jid, {
|
|
442
445
|
image: { url: './banner.jpg' },
|
|
443
446
|
caption: 'Welcome!',
|
|
@@ -448,7 +451,7 @@ await sock.sendMessage(jid, {
|
|
|
448
451
|
]
|
|
449
452
|
})
|
|
450
453
|
|
|
451
|
-
// GIF header
|
|
454
|
+
// Animated GIF header
|
|
452
455
|
await sock.sendMessage(jid, {
|
|
453
456
|
gif: { url: './animation.gif' },
|
|
454
457
|
caption: 'Live preview!',
|
|
@@ -464,7 +467,7 @@ await sock.sendMessage(jid, {
|
|
|
464
467
|
})
|
|
465
468
|
```
|
|
466
469
|
|
|
467
|
-
### Location Map-Card + Buttons (
|
|
470
|
+
### Location Map-Card + Buttons (custom name/address)
|
|
468
471
|
|
|
469
472
|
```javascript
|
|
470
473
|
await sock.sendMessage(jid, {
|
|
@@ -476,10 +479,24 @@ await sock.sendMessage(jid, {
|
|
|
476
479
|
// jpegThumbnail: buffer — custom map image (optional)
|
|
477
480
|
},
|
|
478
481
|
text: 'Pong 9 Ms ⚡',
|
|
479
|
-
buttons: [
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
482
|
+
buttons: [ /* ... */ ]
|
|
483
|
+
})
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Text-only (TEXT header, best-effort) & native flow escape hatch
|
|
487
|
+
|
|
488
|
+
```javascript
|
|
489
|
+
// text-only: TEXT headerType — some clients still show the row vertically;
|
|
490
|
+
// for a guaranteed horizontal row attach image/gif/location as above.
|
|
491
|
+
await sock.sendMessage(jid, {
|
|
492
|
+
text: 'Pong 9 Ms ⚡', footer: 'ISHAN-X',
|
|
493
|
+
buttons: [ { buttonText: { displayText: 'menu' }, buttonId: 'menu', type: 1 } ]
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
// vertical native-flow quick_reply buttons (renders on all modern clients)
|
|
497
|
+
await sock.sendMessage(jid, {
|
|
498
|
+
text: 'Menu?', nativeFlow: true,
|
|
499
|
+
buttons: [ { buttonText: { displayText: 'menu' }, buttonId: 'menu', type: 1 } ]
|
|
483
500
|
})
|
|
484
501
|
```
|
|
485
502
|
|
package/lib/Utils/messages.js
CHANGED
|
@@ -517,6 +517,13 @@ const applyContextInfoAndMentions = (interactiveMessage, message) => {
|
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
|
+
// PATCH: 1x1 transparent PNG — text-only buttons attach this as an invisible
|
|
521
|
+
// IMAGE header so modern clients render the horizontal row (classic requires
|
|
522
|
+
// a header), with no map card and no "version doesn't support it" error.
|
|
523
|
+
const BUTTONS_INVISIBLE_PNG = () => Buffer.from(
|
|
524
|
+
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=',
|
|
525
|
+
'base64'
|
|
526
|
+
)
|
|
520
527
|
export const generateWAMessageContent = async (message, options) => {
|
|
521
528
|
var _a, _b;
|
|
522
529
|
let m = {};
|
|
@@ -589,45 +596,46 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
589
596
|
m = { interactiveMessage };
|
|
590
597
|
}
|
|
591
598
|
else if ('buttonsMessage' in message && !!message.buttonsMessage) {
|
|
592
|
-
// PATCH: raw buttonsMessage —
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
// support it"
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
const hasMediaHeader = !!(bMsg.imageMessage || bMsg.videoMessage || bMsg.documentMessage || bMsg.locationMessage);
|
|
599
|
-
if (hasMediaHeader) {
|
|
600
|
-
m = { buttonsMessage: proto.Message.ButtonsMessage.create(bMsg) };
|
|
601
|
-
} else {
|
|
602
|
-
const flowButtons = (bMsg.buttons || []).map(btn => ({
|
|
603
|
-
name: 'quick_reply',
|
|
604
|
-
buttonParamsJson: JSON.stringify({
|
|
605
|
-
display_text: btn.buttonText?.displayText || btn.buttonText || '',
|
|
606
|
-
id: btn.buttonId || btn.buttonText?.displayText || ''
|
|
607
|
-
})
|
|
608
|
-
}));
|
|
609
|
-
const interactive = {
|
|
610
|
-
nativeFlowMessage: {
|
|
611
|
-
buttons: flowButtons,
|
|
612
|
-
messageParamsJson: '',
|
|
613
|
-
messageVersion: 1
|
|
614
|
-
},
|
|
615
|
-
body: { text: bMsg.contentText || bMsg.text || '' },
|
|
616
|
-
footer: bMsg.footerText ? { text: bMsg.footerText } : undefined
|
|
617
|
-
};
|
|
618
|
-
applyContextInfoAndMentions(interactive, { contextInfo: bMsg.contextInfo });
|
|
619
|
-
m = { interactiveMessage: interactive };
|
|
620
|
-
}
|
|
599
|
+
// PATCH: raw buttonsMessage — always classic pass-through. Modern clients
|
|
600
|
+
// render the horizontal row when a header exists (image/video/document/
|
|
601
|
+
// location) or when a thumbnail header is attached; text-only without any
|
|
602
|
+
// header is the "version doesn't support it" case. Add a header in that
|
|
603
|
+
// situation, or use the `buttons` API which auto-adds one.
|
|
604
|
+
m = { buttonsMessage: proto.Message.ButtonsMessage.create(message.buttonsMessage) };
|
|
621
605
|
}
|
|
622
606
|
else if ('buttons' in message && !!message.buttons) {
|
|
623
|
-
// PATCH: `buttons` API —
|
|
624
|
-
//
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
607
|
+
// PATCH: `buttons` API — HORIZONTAL button row via classic ButtonsMessage
|
|
608
|
+
// with a header (modern clients only render classic horizontal buttons
|
|
609
|
+
// when a header exists: image/video/gif/location, or an embedded
|
|
610
|
+
// thumbnail for text-only). Set { nativeFlow: true } to opt out and get
|
|
611
|
+
// vertical native-flow quick_reply buttons instead.
|
|
612
|
+
if (message.nativeFlow === true) {
|
|
613
|
+
const flowButtons = message.buttons.map(b => ({
|
|
614
|
+
name: 'quick_reply',
|
|
615
|
+
buttonParamsJson: JSON.stringify({
|
|
616
|
+
display_text: b.buttonText?.displayText || b.displayText || b.text || '',
|
|
617
|
+
id: b.buttonId || b.id || b.buttonText?.displayText || ''
|
|
618
|
+
})
|
|
619
|
+
}));
|
|
620
|
+
const interactiveMessage = {
|
|
621
|
+
nativeFlowMessage: {
|
|
622
|
+
buttons: flowButtons,
|
|
623
|
+
messageParamsJson: '',
|
|
624
|
+
messageVersion: 1
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
if ('text' in message) {
|
|
628
|
+
interactiveMessage.body = { text: message.text };
|
|
629
|
+
} else if ('caption' in message) {
|
|
630
|
+
interactiveMessage.body = { text: message.caption };
|
|
631
|
+
}
|
|
632
|
+
if ('footer' in message && !!message.footer) {
|
|
633
|
+
interactiveMessage.footer = { text: message.footer };
|
|
634
|
+
}
|
|
635
|
+
applyContextInfoAndMentions(interactiveMessage, message);
|
|
636
|
+
m = { interactiveMessage };
|
|
637
|
+
}
|
|
638
|
+
else if ('location' in message && !!message.location) {
|
|
631
639
|
const loc = message.location;
|
|
632
640
|
const buttonsMessage = {
|
|
633
641
|
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE })),
|
|
@@ -653,62 +661,58 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
653
661
|
m = { buttonsMessage: proto.Message.ButtonsMessage.create(buttonsMessage) };
|
|
654
662
|
}
|
|
655
663
|
else {
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
buttonParamsJson: JSON.stringify({
|
|
659
|
-
display_text: b.buttonText?.displayText || b.displayText || b.text || '',
|
|
660
|
-
id: b.buttonId || b.id || b.buttonText?.displayText || ''
|
|
661
|
-
})
|
|
662
|
-
}));
|
|
663
|
-
const interactiveMessage = {
|
|
664
|
-
nativeFlowMessage: {
|
|
665
|
-
buttons: flowButtons,
|
|
666
|
-
messageParamsJson: '',
|
|
667
|
-
messageVersion: 1
|
|
668
|
-
}
|
|
664
|
+
const buttonsMessage = {
|
|
665
|
+
buttons: message.buttons.map(b => ({ ...b, type: proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
|
|
669
666
|
};
|
|
670
|
-
if ('
|
|
671
|
-
|
|
672
|
-
} else if ('caption' in message) {
|
|
673
|
-
interactiveMessage.body = { text: message.caption };
|
|
667
|
+
if ('footer' in message && !!message.footer) {
|
|
668
|
+
buttonsMessage.footerText = message.footer;
|
|
674
669
|
}
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
hasMediaAttachment: false
|
|
670
|
+
if (message.contextInfo || message.mentions) {
|
|
671
|
+
buttonsMessage.contextInfo = {
|
|
672
|
+
...(message.contextInfo || {}),
|
|
673
|
+
...(message.mentions ? { mentionedJid: message.mentions } : {})
|
|
680
674
|
};
|
|
681
675
|
}
|
|
682
676
|
if ('video' in message && !!message.video) {
|
|
683
677
|
const media = await prepareWAMessageMedia({ video: message.video }, options);
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
hasMediaAttachment: true
|
|
688
|
-
};
|
|
689
|
-
if (message.gifPlayback) interactiveMessage.header.videoMessage.gifPlayback = true;
|
|
678
|
+
buttonsMessage.videoMessage = media.videoMessage;
|
|
679
|
+
if (message.gifPlayback) buttonsMessage.videoMessage.gifPlayback = true;
|
|
680
|
+
buttonsMessage.headerType = ButtonType.VIDEO;
|
|
690
681
|
} else if ('gif' in message && !!message.gif) {
|
|
691
|
-
// PATCH: direct `gif` key — animated GIF header (video with gifPlayback)
|
|
692
682
|
const media = await prepareWAMessageMedia({ video: message.gif }, options);
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
hasMediaAttachment: true
|
|
697
|
-
};
|
|
698
|
-
interactiveMessage.header.videoMessage.gifPlayback = true;
|
|
683
|
+
buttonsMessage.videoMessage = media.videoMessage;
|
|
684
|
+
buttonsMessage.videoMessage.gifPlayback = true;
|
|
685
|
+
buttonsMessage.headerType = ButtonType.VIDEO;
|
|
699
686
|
} else if ('image' in message && !!message.image) {
|
|
700
687
|
const media = await prepareWAMessageMedia({ image: message.image }, options);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
688
|
+
buttonsMessage.imageMessage = media.imageMessage;
|
|
689
|
+
buttonsMessage.headerType = ButtonType.IMAGE;
|
|
690
|
+
} else {
|
|
691
|
+
if ('text' in message || 'caption' in message) {
|
|
692
|
+
const body = message.text ?? message.caption;
|
|
693
|
+
buttonsMessage.contentText = body;
|
|
694
|
+
// PATCH: text-only — invisible 1x1 PNG header so the horizontal
|
|
695
|
+
// row renders on modern clients without a map card and without
|
|
696
|
+
// the "doesn't support it" error. Upload failure falls back to
|
|
697
|
+
// the TEXT header (may render vertical, still no error).
|
|
698
|
+
try {
|
|
699
|
+
const media = await prepareWAMessageMedia({ image: BUTTONS_INVISIBLE_PNG() }, options);
|
|
700
|
+
buttonsMessage.imageMessage = media.imageMessage;
|
|
701
|
+
buttonsMessage.headerType = ButtonType.IMAGE;
|
|
702
|
+
} catch {
|
|
703
|
+
buttonsMessage.text = (message.title || String(body).split('\n')[0] || '').slice(0, 60);
|
|
704
|
+
buttonsMessage.headerType = ButtonType.TEXT;
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
buttonsMessage.headerType = ButtonType.EMPTY;
|
|
708
|
+
}
|
|
706
709
|
}
|
|
707
|
-
if ('
|
|
708
|
-
|
|
710
|
+
if ('text' in message && (buttonsMessage.videoMessage || buttonsMessage.imageMessage)) {
|
|
711
|
+
// media header already carries the caption
|
|
712
|
+
if (buttonsMessage.videoMessage && 'caption' in message) buttonsMessage.videoMessage.caption = message.caption;
|
|
713
|
+
if (buttonsMessage.imageMessage && 'caption' in message) buttonsMessage.imageMessage.caption = message.caption;
|
|
709
714
|
}
|
|
710
|
-
|
|
711
|
-
m = { interactiveMessage };
|
|
715
|
+
m = { buttonsMessage: proto.Message.ButtonsMessage.create(buttonsMessage) };
|
|
712
716
|
}
|
|
713
717
|
}
|
|
714
718
|
else if (hasNonNullishProperty(message, 'sections') || hasNonNullishProperty(message, 'productList')) {
|