ishumdz-bail 1.0.26 → 1.0.29

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,114 @@ 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 — and renders as a **horizontal one-line row** just like those
702
+ bases. Three shapes are accepted:
703
+
704
+ **1) Raw `buttonsMessage` classic menu card — mixed extras are flattened.** A raw
705
+ classic message that mixes `nativeFlowInfo` (single_select sheet) or `cta_*` buttons
706
+ with plain buttons **stays classic horizontal**: the header (location/image/video)
707
+ is preserved, every extra becomes a plain button with its original `buttonId`, and
708
+ its click arrives as a normal `buttonsResponseMessage` — so your bot can send the
709
+ list sheet / URL action itself (e.g. a separate `nativeFlow: true` message):
710
+
711
+ ```javascript
712
+ // Case X / cantarella style — works as-is:
713
+ await sock.sendMessage(jid, {
714
+ buttonsMessage: {
715
+ locationMessage: { degreesLatitude: 0, degreesLongitude: 0, name: 'Youhu base', address: '📍Today' },
716
+ contentText: menuText, footerText: footer, headerType: 6,
717
+ buttons: [
718
+ { buttonId: 'menu', buttonText: { displayText: '☰ menu' }, type: 1,
719
+ nativeFlowInfo: { name: 'single_select', paramsJson: JSON.stringify({
720
+ title: 'Pilih Menu',
721
+ sections: [{ title: 'base', highlight_label: '🔥', rows: [{ title: 'ping', description: 'live', id: '/ping' }] }]
722
+ }) } },
723
+ { buttonId: 'sc', buttonText: { displayText: '⌕ script' }, type: 1 }
724
+ ]
725
+ }
726
+ })
727
+ ```
728
+
729
+ **2) Raw `interactiveMessage` native-flow card — straight through `sendMessage`.**
730
+ No `generateWAMessageFromContent` + `relayMessage` dance needed (though that still
731
+ works too). `messageVersion` defaults to 1:
732
+
733
+ ```javascript
734
+ await sock.sendMessage(jid, {
735
+ interactiveMessage: {
736
+ body: { text: 'Pairing code: 1234-5678' },
737
+ footer: { text: 'Tap to copy' },
738
+ header: { hasMediaAttachment: false },
739
+ nativeFlowMessage: {
740
+ buttons: [{ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', copy_code: '12345678' }) }]
741
+ }
742
+ }
743
+ })
744
+
745
+ // short form also accepted:
746
+ await sock.sendMessage(jid, { interactiveMessage: { buttons: [{ name: 'quick_reply', buttonParamsJson: '{"display_text":"hi","id":"hi"}' }] } })
747
+ ```
748
+
749
+ **3) Plain raw `buttonsMessage`** (no native-flow extras) stays classic and gets the
750
+ automatic invisible-PNG header when you omit one. Flow params are accepted as
751
+ `paramsJson` or `buttonParamsJson`.
752
+
753
+ > 💡 **Want the tap-to-open sheet too?** Send it as a second message when the button
754
+ > is clicked (see `examples/menu-buttons.js` — the `allmenus` case). Mixing a sheet
755
+ > into the same classic row is what produced the "doesn't support it" bubble; the
756
+ > library keeps the row safe by flattening extras, and the two-message pattern gives
757
+ > you both layouts.
758
+
759
+ ### One-Tap Horizontal Button → Automatic List Sheet — v1.0.28+
760
+
761
+ `attachSheetAutoReply` does the two-message pattern **for you**: register it once,
762
+ and when the user taps a horizontal button, the list sheet is sent automatically —
763
+ no extra code in your `messages.upsert` handler:
764
+
765
+ ```javascript
766
+ const { makeWASocket, attachSheetAutoReply } = require('ishumdz-bail')
767
+
768
+ const sock = makeWASocket({ auth: state, ... })
769
+
770
+ // register ONCE, right after creating the socket:
771
+ attachSheetAutoReply(sock, {
772
+ // click id → full sheet (auto-sent, click never reaches your parser)
773
+ allmenus: {
774
+ text: '📂 *ALL MENUS* — tap a section 👇',
775
+ footer: 'ISHAN-X MD PRO',
776
+ buttonText: '📂 open menu',
777
+ sectionTitle: 'All Menus',
778
+ highlightLabel: '⭐ MAIN',
779
+ rows: [
780
+ { title: '📥 Download Menu', description: 'Downloader commands', rowId: '.downloadmenu' },
781
+ { title: '✨ AI Menu', description: 'AI commands', rowId: '.aimenu' },
782
+ { title: '🎮 Game Menu', description: 'Fun & games', rowId: '.gamemenu' }
783
+ ]
784
+ },
785
+ // shorthand: click id → rewrite into a normal command for your parser
786
+ support: '.owner'
787
+ })
788
+
789
+ // then your horizontal row stays 100% plain → guaranteed one-line render:
790
+ await sock.sendMessage(jid, {
791
+ location: { degreesLatitude: 0, degreesLongitude: 0,
792
+ name: 'ISHAN-X MD PRO', address: '📂 Select Menu 👇' },
793
+ text: menuText, footer,
794
+ buttons: [
795
+ { buttonText: { displayText: '📂 allmenus' }, buttonId: 'allmenus', type: 1 },
796
+ { buttonText: { displayText: '👤 owner' }, buttonId: 'owner', type: 1 },
797
+ { buttonText: { displayText: '⚡ ping' }, buttonId: 'ping', type: 1 }
798
+ ]
799
+ }, { quoted: msg })
800
+ // user taps "allmenus" → sheet opens instantly; taps "owner" → your parser gets '.owner'
801
+ ```
802
+
803
+ Also exported: `sendSheet(sock, jid, entry, quoted)` (send a sheet manually) and
804
+ `decodeButtonClick(msg)` (the click bridge, shipped with the library).
805
+
698
806
  ---
699
807
 
700
808
  ## 🚫 Ban Checker
@@ -0,0 +1,179 @@
1
+ /**
2
+ * sheet-buttons.js — one-tap horizontal button → automatic list sheet (v1.0.28+)
3
+ *
4
+ * Classic horizontal rows cannot carry a native-flow sheet inside the SAME
5
+ * message on current clients (that mix renders as "version doesn't support
6
+ * it"). The working pattern is TWO messages: the horizontal row, and — when
7
+ * the user clicks the menu button — a native-flow list sheet sent
8
+ * automatically. This module does the second half for you:
9
+ *
10
+ * const { attachSheetAutoReply } = require('ishumdz-bail')
11
+ *
12
+ * attachSheetAutoReply(sock, {
13
+ * // click id → full sheet definition
14
+ * allmenus: {
15
+ * text: '📂 *ALL MENUS* — tap a section 👇',
16
+ * footer: 'ISHAN-X MD PRO',
17
+ * buttonText: '📂 open menu',
18
+ * highlightLabel: '⭐ MAIN',
19
+ * sectionTitle: 'All Menus',
20
+ * rows: [
21
+ * { title: '📥 Download Menu', description: 'Downloaders', rowId: '.downloadmenu' },
22
+ * { title: '✨ AI Menu', description: 'AI commands', rowId: '.aimenu' }
23
+ * ]
24
+ * },
25
+ * // or a shorthand: rewrite the click into another command
26
+ * support: '.owner'
27
+ * })
28
+ *
29
+ * Now a horizontal row can stay 100% plain (guaranteed one-line render):
30
+ *
31
+ * await sock.sendMessage(jid, {
32
+ * location: { degreesLatitude: 0, degreesLongitude: 0,
33
+ * name: 'ISHAN-X MD PRO', address: '📂 Select Menu 👇' },
34
+ * text: menuText, footer,
35
+ * buttons: [
36
+ * { buttonText: { displayText: '📂 allmenus' }, buttonId: 'allmenus', type: 1 },
37
+ * { buttonText: { displayText: '👤 owner' }, buttonId: 'owner', type: 1 }
38
+ * ]
39
+ * })
40
+ *
41
+ * User taps "allmenus" → the sheet opens instantly, and your own
42
+ * messages.upsert handler is NOT called for that click (consumed).
43
+ * A shorthand entry rewrites the click into the given command text and
44
+ * lets your normal command parser handle it.
45
+ */
46
+
47
+ // Decode any button / sheet-row / list-row click into its id (same bridge
48
+ // the examples use, now shipped with the library).
49
+ export function decodeButtonClick(msg) {
50
+ // classic horizontal quick-reply buttons
51
+ const classic = msg?.message?.buttonsResponseMessage?.selectedButtonId
52
+ if (classic) return classic
53
+
54
+ // native flow (sheet rows / card quick replies)
55
+ const nf = msg?.message?.interactiveResponseMessage?.nativeFlowResponseMessage
56
+ if (nf?.paramsJson) {
57
+ try {
58
+ const p = JSON.parse(nf.paramsJson)
59
+ return p.id || p.selectedId || p.selected_row_id || null
60
+ } catch { /* ignore */ }
61
+ }
62
+
63
+ // classic listMessage rows
64
+ const list = msg?.message?.listResponseMessage?.singleSelectReply?.selectedRowId
65
+ if (list) return list
66
+
67
+ return null
68
+ }
69
+
70
+ const normalizeRows = (rows) => (rows || []).map(r => ({
71
+ title: r.title,
72
+ ...(r.description ? { description: r.description } : {}),
73
+ id: r.rowId ?? r.id ?? r.title
74
+ }))
75
+
76
+ const buildSections = (entry) => {
77
+ if (Array.isArray(entry.sections) && entry.sections.length) {
78
+ return entry.sections.map(s => ({
79
+ ...(s.title ? { title: s.title } : {}),
80
+ ...(s.highlight_label || s.highlightLabel ? { highlight_label: s.highlight_label || s.highlightLabel } : {}),
81
+ rows: normalizeRows(s.rows)
82
+ }))
83
+ }
84
+ return [{
85
+ title: entry.sectionTitle || entry.buttonText || 'Menu',
86
+ ...(entry.highlightLabel ? { highlight_label: entry.highlightLabel } : {}),
87
+ rows: normalizeRows(entry.rows)
88
+ }]
89
+ }
90
+
91
+ const normalizeHandlers = (handlers) => {
92
+ const sheets = {}
93
+ const commands = {}
94
+ for (const [id, val] of Object.entries(handlers || {})) {
95
+ if (typeof val === 'string') {
96
+ commands[id] = val
97
+ } else if (val && (Array.isArray(val.rows) || Array.isArray(val.sections))) {
98
+ sheets[id] = val
99
+ }
100
+ }
101
+ return { sheets, commands }
102
+ }
103
+
104
+ /**
105
+ * Manually send a native-flow list sheet (the tap-to-open menu).
106
+ * attachSheetAutoReply calls this for you on a matching click.
107
+ */
108
+ export async function sendSheet(sock, jid, entry, quoted) {
109
+ const sections = buildSections(entry)
110
+ return sock.sendMessage(jid, {
111
+ text: entry.text || '📋 Select an option 👇',
112
+ ...(entry.footer ? { footer: entry.footer } : {}),
113
+ nativeFlow: true,
114
+ buttons: [{
115
+ buttonText: { displayText: entry.buttonText || '☰ menu' },
116
+ buttonId: entry.buttonId || 'sheet',
117
+ type: 1,
118
+ sections
119
+ }]
120
+ }, quoted ? { quoted } : {})
121
+ }
122
+
123
+ /**
124
+ * Register automatic sheet replies on a socket.
125
+ *
126
+ * @param {object} sock the makeWASocket() instance
127
+ * @param {object} handlers map of clickId → sheet definition { text?, footer?,
128
+ * buttonText?, sectionTitle?, highlightLabel?, rows|sections }
129
+ * OR clickId → '.command' string shorthand
130
+ * @returns {Function} uninstall — restores the original sock.ev.on
131
+ */
132
+ export function attachSheetAutoReply(sock, handlers) {
133
+ const { sheets, commands } = normalizeHandlers(handlers)
134
+
135
+ async function handleUpsert(payload) {
136
+ const { messages, type } = payload || {}
137
+ if (type !== 'notify' || !Array.isArray(messages)) return false
138
+ for (const msg of messages) {
139
+ if (!msg?.message || msg.key?.fromMe) continue
140
+ const clicked = decodeButtonClick(msg)
141
+ if (!clicked) continue
142
+
143
+ // 1) full sheet definition → send the sheet, consume the click
144
+ const entry = sheets[clicked]
145
+ if (entry) {
146
+ try {
147
+ await sendSheet(sock, msg.key.remoteJid, { ...entry, buttonId: clicked }, msg)
148
+ } catch (e) {
149
+ try { sock.logger?.error?.({ err: e }, 'sheet auto-reply failed') } catch { /* ignore */ }
150
+ }
151
+ return true
152
+ }
153
+
154
+ // 2) command shorthand → rewrite into normal command text for the parser
155
+ const cmd = commands[clicked]
156
+ if (cmd) {
157
+ msg.message = { conversation: cmd }
158
+ return false
159
+ }
160
+ }
161
+ return false
162
+ }
163
+
164
+ // Wrap the user's messages.upsert registration so the sheet fires first
165
+ // and consumed clicks never reach the bot's command parser.
166
+ const origOn = sock.ev.on.bind(sock.ev)
167
+ sock.ev.on = function (ev, handler) {
168
+ if (ev === 'messages.upsert') {
169
+ return origOn(ev, async (...args) => {
170
+ const consumed = await handleUpsert(args[0]).catch(() => false)
171
+ if (consumed) return true
172
+ return handler(...args)
173
+ })
174
+ }
175
+ return origOn(ev, handler)
176
+ }
177
+
178
+ return () => { sock.ev.on = origOn }
179
+ }
@@ -654,20 +654,68 @@ export const generateWAMessageContent = async (message, options) => {
654
654
  applyContextInfoAndMentions(interactiveMessage, message);
655
655
  m = { interactiveMessage };
656
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
+ }
657
677
  else if ('buttonsMessage' in message && !!message.buttonsMessage) {
658
678
  // PATCH: raw buttonsMessage — classic pass-through. Modern clients render
659
679
  // the horizontal row only when a header exists (image/video/document/
660
680
  // location). A text-only raw message WITHOUT any header is exactly the
661
681
  // "version doesn't support it" case — so when the caller passed none, we
662
682
  // attach the invisible 1x1 PNG image header automatically (same trick the
663
- // `buttons` API uses) and the row always renders. This is unconditional:
664
- // header-less raw messages can never produce the "not supported" bubble.
683
+ // `buttons` API uses) and the row always renders.
665
684
  const raw = proto.Message.ButtonsMessage.fromObject(message.buttonsMessage)
666
685
  // NOTE: fromObject turns missing fields into null (protobuf defaults), so
667
686
  // a nullish headerType (= caller never set it) counts as "no header" too.
668
687
  const hasHeader = !!(raw.imageMessage || raw.videoMessage || raw.documentMessage ||
669
688
  raw.locationMessage ||
670
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
+ // PATCH (1.0.28): a raw classic message that MIXES native-flow extras
703
+ // (single_select nativeFlowInfo, cta_* buttons) into a ButtonsMessage is
704
+ // the Case X / cantarella menu-card pattern — on current clients it
705
+ // renders as "version doesn't support it" (real-device evidence).
706
+ // Instead of upgrading to the vertical native-flow card, keep the
707
+ // HORIZONTAL classic layout: every extra becomes a plain RESPONSE
708
+ // button with its original buttonId, clicks arrive as
709
+ // buttonsResponseMessage, and the bot sends the list sheet / URL /
710
+ // copy action itself (e.g. a separate nativeFlow message). The header
711
+ // (location/image/video/document) is preserved untouched — that is the
712
+ // proven horizontal Case X / cantarella render.
713
+ raw.buttons = inputButtons.map(b => proto.Message.ButtonsMessage.Button.fromObject({
714
+ buttonId: b?.buttonId || b?.buttonText?.displayText || 'button',
715
+ buttonText: { displayText: b?.buttonText?.displayText || b?.displayText || 'Button' },
716
+ type: proto.Message.ButtonsMessage.Button.Type.RESPONSE
717
+ }))
718
+ }
671
719
  if (!hasHeader) {
672
720
  try {
673
721
  const media = await prepareWAMessageMedia({ image: BUTTONS_INVISIBLE_PNG() }, options)
package/lib/index.js CHANGED
@@ -2,6 +2,7 @@ import makeWASocket from './Socket/index.js';
2
2
  import chalk from "chalk";
3
3
  import { enableCallAutoAnswer, getActiveVoipClient, CallState } from './Caller/auto-answer.js';
4
4
  import { attachProMethods } from './Pro/index.js';
5
+ import { attachSheetAutoReply, sendSheet, decodeButtonClick } from './Pro/sheet-buttons.js';
5
6
  import * as PollsPro from './Pro/polls.js';
6
7
  import * as NewsletterPro from './Pro/newsletter.js';
7
8
  import * as StatusPro from './Pro/status.js';
@@ -91,5 +92,8 @@ export const removeProfilePicture = ProfilePro.removeProfilePicture;
91
92
  export const rejectCall = ProfilePro.rejectCall;
92
93
 
93
94
  export { enableCallAutoAnswer, getActiveVoipClient, CallState, attachProMethods, makeWASocket };
95
+
96
+ // One-tap horizontal button → automatic list sheet (v1.0.28+)
97
+ export { attachSheetAutoReply, sendSheet, decodeButtonClick };
94
98
  export default makeWASocket;
95
99
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ishumdz-bail",
3
- "version": "1.0.26",
3
+ "version": "1.0.29",
4
4
  "description": "WhatsApp API Modification By Lovely",
5
5
  "keywords": [
6
6
  "whatsapp",