ishumdz-bail 1.0.25 → 1.0.27

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
@@ -651,7 +651,7 @@ function resolveButtonResponse(msg) {
651
651
  const nf = msg.message?.interactiveResponseMessage?.nativeFlowResponseMessage
652
652
  if (nf?.paramsJson) {
653
653
  try {
654
- const p = JSON.parse(native.paramsJson)
654
+ const p = JSON.parse(nf.paramsJson)
655
655
  return p.id || p.selectedId || p.selected_row_id || null
656
656
  } catch { /* ignore */ }
657
657
  }
@@ -695,6 +695,59 @@ Action buttons (`url` / `copy` / `call`) never send a reply — nothing to handl
695
695
  > (`.menu` → custom image/GIF + buttons + highlighted menu sheet), plus a visual
696
696
  > preview in [`banner-preview.html`](banner-preview.html).
697
697
 
698
+ ### Base Compatibility — cantarella / Case X Style Code (v1.0.27+)
699
+
700
+ Code copied from other Baileys bases (cantarella-baileys, Case X Plugin YOUHU MD, etc.)
701
+ runs without changes. Three shapes are accepted:
702
+
703
+ **1) Raw `buttonsMessage` classic menu card — even the risky mix.** A raw classic
704
+ message that mixes `nativeFlowInfo` (single_select sheet) or `cta_*` buttons with
705
+ plain buttons is **auto-upgraded to a native-flow interactive card**, so it can never
706
+ render the "version doesn't support it" bubble:
707
+
708
+ ```javascript
709
+ // Case X / cantarella style — works as-is:
710
+ await sock.sendMessage(jid, {
711
+ buttonsMessage: {
712
+ locationMessage: { degreesLatitude: 0, degreesLongitude: 0, name: 'Youhu base', address: '📍Today' },
713
+ contentText: menuText, footerText: footer, headerType: 6,
714
+ buttons: [
715
+ { buttonId: 'menu', buttonText: { displayText: '☰ menu' }, type: 1,
716
+ nativeFlowInfo: { name: 'single_select', paramsJson: JSON.stringify({
717
+ title: 'Pilih Menu',
718
+ sections: [{ title: 'base', highlight_label: '🔥', rows: [{ title: 'ping', description: 'live', id: '/ping' }] }]
719
+ }) } },
720
+ { buttonId: 'sc', buttonText: { displayText: '⌕ script' }, type: 1 }
721
+ ]
722
+ }
723
+ })
724
+ ```
725
+
726
+ **2) Raw `interactiveMessage` native-flow card — straight through `sendMessage`.**
727
+ No `generateWAMessageFromContent` + `relayMessage` dance needed (though that still
728
+ works too). `messageVersion` defaults to 1:
729
+
730
+ ```javascript
731
+ await sock.sendMessage(jid, {
732
+ interactiveMessage: {
733
+ body: { text: 'Pairing code: 1234-5678' },
734
+ footer: { text: 'Tap to copy' },
735
+ header: { hasMediaAttachment: false },
736
+ nativeFlowMessage: {
737
+ buttons: [{ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', copy_code: '12345678' }) }]
738
+ }
739
+ }
740
+ })
741
+
742
+ // short form also accepted:
743
+ await sock.sendMessage(jid, { interactiveMessage: { buttons: [{ name: 'quick_reply', buttonParamsJson: '{"display_text":"hi","id":"hi"}' }] } })
744
+ ```
745
+
746
+ **3) Plain raw `buttonsMessage`** (no native-flow extras) stays classic and gets the
747
+ automatic invisible-PNG header when you omit one. Flow params are accepted as
748
+ `paramsJson` or `buttonParamsJson`, and `single_select` / `cta_url` / `cta_copy` /
749
+ `cta_call` / `quick_reply` names are preserved as-is.
750
+
698
751
  ---
699
752
 
700
753
  ## 🚫 Ban Checker
@@ -586,7 +586,10 @@ export const generateWAMessageContent = async (message, options) => {
586
586
  if ('interactiveButtons' in message && !!message.interactiveButtons) {
587
587
  const nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
588
588
  buttons: message.interactiveButtons,
589
- messageParamsJson: message.messageParams ?? "{}"
589
+ messageParamsJson: message.messageParams ?? "{}",
590
+ // PATCH (1.0.26): default messageVersion to 1 — version 0 renders as
591
+ // greyed-out, untappable placeholder buttons on current clients.
592
+ messageVersion: message.messageVersion ?? 1
590
593
  });
591
594
  let interactiveMessage = { nativeFlowMessage: nativeFlow };
592
595
  if ('text' in message) {
@@ -651,20 +654,101 @@ export const generateWAMessageContent = async (message, options) => {
651
654
  applyContextInfoAndMentions(interactiveMessage, message);
652
655
  m = { interactiveMessage };
653
656
  }
657
+ else if ('interactiveMessage' in message && !!message.interactiveMessage) {
658
+ // PATCH (1.0.27): cantarella / Case X-style RAW interactiveMessage
659
+ // pass-through — send the card exactly like those bases do, but via a
660
+ // normal sendMessage call. Two accepted shapes:
661
+ // { interactiveMessage: { body, footer, header, nativeFlowMessage: { buttons } } }
662
+ // { interactiveMessage: { buttons } } (short form)
663
+ // messageVersion defaults to 1 (0 renders greyed-out placeholders).
664
+ let card = message.interactiveMessage;
665
+ if (card && !card.nativeFlowMessage && Array.isArray(card.buttons)) {
666
+ const { buttons, messageParamsJson, ...rest } = card;
667
+ card = { ...rest, nativeFlowMessage: { buttons, messageParamsJson: messageParamsJson || "" } };
668
+ }
669
+ if (card?.nativeFlowMessage && (card.nativeFlowMessage.messageVersion == null || card.nativeFlowMessage.messageVersion === 0)) {
670
+ card.nativeFlowMessage.messageVersion = 1;
671
+ }
672
+ if (card && !card.body && 'text' in message && !!message.text) {
673
+ card.body = { text: message.text };
674
+ }
675
+ m = { interactiveMessage: card };
676
+ }
654
677
  else if ('buttonsMessage' in message && !!message.buttonsMessage) {
655
678
  // PATCH: raw buttonsMessage — classic pass-through. Modern clients render
656
679
  // the horizontal row only when a header exists (image/video/document/
657
680
  // location). A text-only raw message WITHOUT any header is exactly the
658
681
  // "version doesn't support it" case — so when the caller passed none, we
659
682
  // attach the invisible 1x1 PNG image header automatically (same trick the
660
- // `buttons` API uses) and the row always renders. This is unconditional:
661
- // header-less raw messages can never produce the "not supported" bubble.
683
+ // `buttons` API uses) and the row always renders.
662
684
  const raw = proto.Message.ButtonsMessage.fromObject(message.buttonsMessage)
663
685
  // NOTE: fromObject turns missing fields into null (protobuf defaults), so
664
686
  // a nullish headerType (= caller never set it) counts as "no header" too.
665
687
  const hasHeader = !!(raw.imageMessage || raw.videoMessage || raw.documentMessage ||
666
688
  raw.locationMessage ||
667
689
  (raw.headerType != null && raw.headerType !== ButtonType.EMPTY && raw.headerType !== ButtonType.UNKNOWN))
690
+ // PATCH (1.0.27): a raw classic message that MIXES native-flow extras
691
+ // (single_select nativeFlowInfo, cta_* buttons) into a ButtonsMessage is
692
+ // the Case X / cantarella menu-card pattern — on current clients it
693
+ // renders as "version doesn't support it" (real-device evidence). Convert
694
+ // the whole message to a native-flow interactive card that renders fully.
695
+ // Read the caller's ORIGINAL button objects — proto fromObject drops
696
+ // unknown fields (e.g. nativeFlowInfo.buttonParamsJson), and some bases
697
+ // (cantarella) put params there.
698
+ const inputButtons = Array.isArray(message.buttonsMessage.buttons) ? message.buttonsMessage.buttons : raw.buttons
699
+ const rawExtras = (inputButtons || []).filter(b =>
700
+ b?.nativeFlowInfo?.name || Number(b?.type) === proto.Message.ButtonsMessage.Button.Type.NATIVE_FLOW)
701
+ if (rawExtras.length) {
702
+ const rawLoc = raw.locationMessage
703
+ const flowButtons = inputButtons.map(b => {
704
+ const name = b?.nativeFlowInfo?.name
705
+ if (name === 'single_select') {
706
+ let params = b.nativeFlowInfo.paramsJson
707
+ try { params = JSON.stringify(JSON.parse(params)) } catch { params = typeof params === 'string' ? params : '{}' }
708
+ return { name: 'single_select', buttonParamsJson: params }
709
+ }
710
+ if (name) {
711
+ return { name, buttonParamsJson: b.nativeFlowInfo.buttonParamsJson || b.nativeFlowInfo.paramsJson || '{}' }
712
+ }
713
+ return {
714
+ name: 'quick_reply',
715
+ buttonParamsJson: JSON.stringify({
716
+ display_text: b.buttonText?.displayText || '',
717
+ id: b.buttonId || b.buttonText?.displayText || ''
718
+ })
719
+ }
720
+ })
721
+ const interactiveMessage = {
722
+ nativeFlowMessage: { buttons: flowButtons, messageParamsJson: '', messageVersion: 1 }
723
+ }; const bodyText = raw.contentText || (raw.imageMessage?.caption ?? raw.videoMessage?.caption ?? '') || ''
724
+ if (bodyText) interactiveMessage.body = { text: bodyText };
725
+ if (raw.footerText) interactiveMessage.footer = { text: raw.footerText };
726
+ const header = { title: raw.text || (interactiveMessage.body.text ? String(interactiveMessage.body.text).split('\n')[0].slice(0, 42) : '') || 'Menu', subtitle: '', hasMediaAttachment: false };
727
+ if (rawLoc) {
728
+ header.locationMessage = {
729
+ degreesLatitude: rawLoc.degreesLatitude ?? 0,
730
+ degreesLongitude: rawLoc.degreesLongitude ?? 0,
731
+ name: rawLoc.name ?? null,
732
+ address: rawLoc.address ?? null,
733
+ ...(rawLoc.jpegThumbnail ? { jpegThumbnail: rawLoc.jpegThumbnail } : {})
734
+ };
735
+ header.hasMediaAttachment = true;
736
+ } else if (raw.imageMessage) {
737
+ header.imageMessage = raw.imageMessage;
738
+ header.hasMediaAttachment = true;
739
+ } else if (raw.videoMessage) {
740
+ header.videoMessage = raw.videoMessage;
741
+ header.hasMediaAttachment = true;
742
+ } else if (raw.documentMessage) {
743
+ header.documentMessage = raw.documentMessage;
744
+ header.hasMediaAttachment = true;
745
+ }
746
+ if (header.title || header.hasMediaAttachment) interactiveMessage.header = header;
747
+ if (raw.contextInfo?.mentionedJid?.length) {
748
+ interactiveMessage.contextInfo = { mentionedJid: raw.contextInfo.mentionedJid };
749
+ }
750
+ m = { interactiveMessage };
751
+ } else {
668
752
  if (!hasHeader) {
669
753
  try {
670
754
  const media = await prepareWAMessageMedia({ image: BUTTONS_INVISIBLE_PNG() }, options)
@@ -673,6 +757,7 @@ export const generateWAMessageContent = async (message, options) => {
673
757
  } catch { /* upload failed — send as-is */ }
674
758
  }
675
759
  m = { buttonsMessage: raw };
760
+ }
676
761
  }
677
762
  else if ('buttons' in message && !!message.buttons) {
678
763
  // PATCH: `buttons` API — HORIZONTAL button row via classic ButtonsMessage
@@ -722,6 +807,63 @@ export const generateWAMessageContent = async (message, options) => {
722
807
  applyContextInfoAndMentions(interactiveMessage, message);
723
808
  m = { interactiveMessage };
724
809
  }
810
+ else if (message.buttons.some(b => buildActionFlow(b) || buildButtonSections(b))) {
811
+ // PATCH (1.0.26): a classic ButtonsMessage that MIXES native-flow
812
+ // extras (rows/sections sheet, url/copy/call actions) with a media
813
+ // or location header renders as "version doesn't support it" on
814
+ // current clients (real-device evidence). So when any button
815
+ // carries an extra, the WHOLE message is upgraded to a native-flow
816
+ // interactive card — map/media header + body + footer + every
817
+ // button type (single_select, cta_url, quick_reply) — which modern
818
+ // clients render fully. Plain-only rows keep the classic
819
+ // horizontal ButtonsMessage (that path is proven to render).
820
+ const flowButtons = message.buttons.map(b => {
821
+ const action = buildActionFlow(b);
822
+ if (action) return { name: action.name, buttonParamsJson: JSON.stringify(action.params) };
823
+ const sections = buildButtonSections(b);
824
+ if (sections) return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: b.buttonText?.displayText || b.displayText || 'Menu', sections }) };
825
+ return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: b.buttonText?.displayText || b.displayText || b.text || '', id: b.buttonId || b.id || b.buttonText?.displayText || '' }) };
826
+ });
827
+ const interactiveMessage = {
828
+ nativeFlowMessage: { buttons: flowButtons, messageParamsJson: '', messageVersion: 1 }
829
+ };
830
+ if ('text' in message) interactiveMessage.body = { text: message.text };
831
+ else if ('caption' in message) interactiveMessage.body = { text: message.caption };
832
+ if ('footer' in message && !!message.footer) interactiveMessage.footer = { text: message.footer };
833
+ const header = {
834
+ title: message.title || (message.caption ? String(message.caption).split('\n')[0].slice(0, 42) : '') || 'Menu',
835
+ subtitle: message.subtitle || '',
836
+ hasMediaAttachment: false
837
+ };
838
+ if ('location' in message && !!message.location) {
839
+ const loc = message.location;
840
+ header.locationMessage = {
841
+ degreesLatitude: loc.degreesLatitude ?? loc.latitude ?? 0,
842
+ degreesLongitude: loc.degreesLongitude ?? loc.longitude ?? 0,
843
+ name: loc.name ?? null,
844
+ address: loc.address ?? null,
845
+ ...(loc.jpegThumbnail ? { jpegThumbnail: loc.jpegThumbnail } : {})
846
+ };
847
+ header.hasMediaAttachment = true;
848
+ } else if ('video' in message && !!message.video) {
849
+ const media = await prepareWAMessageMedia({ video: message.video }, options);
850
+ header.videoMessage = media.videoMessage;
851
+ if (message.gifPlayback) header.videoMessage.gifPlayback = true;
852
+ header.hasMediaAttachment = true;
853
+ } else if ('gif' in message && !!message.gif) {
854
+ const media = await prepareWAMessageMedia({ video: message.gif }, options);
855
+ header.videoMessage = media.videoMessage;
856
+ header.videoMessage.gifPlayback = true;
857
+ header.hasMediaAttachment = true;
858
+ } else if ('image' in message && !!message.image) {
859
+ const media = await prepareWAMessageMedia({ image: message.image }, options);
860
+ header.imageMessage = media.imageMessage;
861
+ header.hasMediaAttachment = true;
862
+ }
863
+ if (header.title || header.hasMediaAttachment) interactiveMessage.header = header;
864
+ applyContextInfoAndMentions(interactiveMessage, message);
865
+ m = { interactiveMessage };
866
+ }
725
867
  else if ('location' in message && !!message.location) {
726
868
  const loc = message.location;
727
869
  const buttonsMessage = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ishumdz-bail",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "WhatsApp API Modification By Lovely",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -43,7 +43,7 @@
43
43
  "prepare": "",
44
44
  "preinstall": "node ./engine-requirements.js",
45
45
  "release": "release-it",
46
- "test": "jest"
46
+ "test": "node --test \"test/**/*.test.mjs\""
47
47
  },
48
48
  "dependencies": {
49
49
  "@cacheable/node-cache": "^1.4.0",