@nozez-lab/baileys 1.0.0

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.
Files changed (3) hide show
  1. package/index.js +285 -0
  2. package/package.json +24 -0
  3. package/test.js +122 -0
package/index.js ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * NozezBaileys - Custom WhatsApp Baileys Library Wrapper
3
+ * Acts as a drop-in replacement for '@whiskeysockets/baileys' with native button, image headers, list badges & pairing code.
4
+ */
5
+
6
+ const Baileys = require('@whiskeysockets/baileys');
7
+ const { getContentType, prepareWAMessageMedia } = require('@whiskeysockets/baileys');
8
+ const chalk = require('chalk');
9
+ const pino = require('pino');
10
+
11
+ /**
12
+ * Custom WASocket wrapper that injects premium features
13
+ */
14
+ function makeWASocket(config = {}) {
15
+ // 1. Create the original socket connection
16
+ const sock = Baileys.default(config);
17
+
18
+ // 2. Add native Button helper on the socket object
19
+ sock.sendButtons = async (jid, header, text, footer, buttons, quoted = null, mediaOptions = {}) => {
20
+ const formattedButtons = buttons.map(btn => {
21
+ if (btn.type === 'reply') {
22
+ return {
23
+ name: "quick_reply",
24
+ buttonParamsJson: JSON.stringify({
25
+ display_text: btn.displayText,
26
+ id: btn.id
27
+ })
28
+ };
29
+ } else if (btn.type === 'url') {
30
+ return {
31
+ name: "cta_url",
32
+ buttonParamsJson: JSON.stringify({
33
+ display_text: btn.displayText,
34
+ url: btn.url,
35
+ merchant_url: btn.url
36
+ })
37
+ };
38
+ } else if (btn.type === 'copy') {
39
+ return {
40
+ name: "cta_copy",
41
+ buttonParamsJson: JSON.stringify({
42
+ display_text: btn.displayText,
43
+ copy_code: btn.code
44
+ })
45
+ };
46
+ }
47
+ return btn;
48
+ });
49
+
50
+ let headerObject = {
51
+ title: header || "",
52
+ hasMediaAttachment: false
53
+ };
54
+
55
+ // If an image header is provided, upload it natively to WhatsApp
56
+ if (mediaOptions.image) {
57
+ try {
58
+ const media = await prepareWAMessageMedia(
59
+ { image: mediaOptions.image },
60
+ { upload: sock.waUploadToServer }
61
+ );
62
+ headerObject = {
63
+ hasMediaAttachment: true,
64
+ imageMessage: media.imageMessage
65
+ };
66
+ } catch (err) {
67
+ console.error("[ERROR] Failed to prepare image header for buttons:", err);
68
+ }
69
+ }
70
+
71
+ const msg = {
72
+ viewOnceMessage: {
73
+ message: {
74
+ interactiveMessage: {
75
+ header: headerObject,
76
+ body: {
77
+ text: text
78
+ },
79
+ footer: {
80
+ text: footer || ""
81
+ },
82
+ nativeFlowMessage: {
83
+ buttons: formattedButtons
84
+ }
85
+ }
86
+ }
87
+ }
88
+ };
89
+
90
+ return await sock.relayMessage(jid, msg, { quoted });
91
+ };
92
+
93
+ // 3. Add native List (single_select dropdown) helper on the socket object
94
+ sock.sendList = async (jid, header, text, footer, buttonText, sections, quoted = null, mediaOptions = {}) => {
95
+ let headerObject = {
96
+ title: header || "",
97
+ hasMediaAttachment: false
98
+ };
99
+
100
+ // If an image header is provided, upload it natively to WhatsApp
101
+ if (mediaOptions.image) {
102
+ try {
103
+ const media = await prepareWAMessageMedia(
104
+ { image: mediaOptions.image },
105
+ { upload: sock.waUploadToServer }
106
+ );
107
+ headerObject = {
108
+ hasMediaAttachment: true,
109
+ imageMessage: media.imageMessage
110
+ };
111
+ } catch (err) {
112
+ console.error("[ERROR] Failed to prepare image header for list:", err);
113
+ }
114
+ }
115
+
116
+ const msg = {
117
+ viewOnceMessage: {
118
+ message: {
119
+ interactiveMessage: {
120
+ header: headerObject,
121
+ body: {
122
+ text: text
123
+ },
124
+ footer: {
125
+ text: footer || ""
126
+ },
127
+ nativeFlowMessage: {
128
+ buttons: [
129
+ {
130
+ name: "single_select",
131
+ buttonParamsJson: JSON.stringify({
132
+ title: buttonText,
133
+ sections: sections
134
+ })
135
+ }
136
+ ]
137
+ }
138
+ }
139
+ }
140
+ }
141
+ };
142
+
143
+ return await sock.relayMessage(jid, msg, { quoted });
144
+ };
145
+
146
+ // 4. Intercept the standard sendMessage function to support native buttons & lists!
147
+ const originalSendMessage = sock.sendMessage;
148
+ sock.sendMessage = async (jid, content, options = {}) => {
149
+ // Intercept native buttons
150
+ if (content.buttons && Array.isArray(content.buttons)) {
151
+ return await sock.sendButtons(
152
+ jid,
153
+ content.header || "",
154
+ content.text || content.caption || "",
155
+ content.footer || "",
156
+ content.buttons,
157
+ options.quoted,
158
+ { image: content.image } // Pass the image header parameter
159
+ );
160
+ }
161
+
162
+ // Intercept native lists
163
+ if (content.sections && Array.isArray(content.sections)) {
164
+ return await sock.sendList(
165
+ jid,
166
+ content.header || "",
167
+ content.text || content.caption || "",
168
+ content.footer || "",
169
+ content.buttonText || "Choose Option",
170
+ content.sections,
171
+ options.quoted,
172
+ { image: content.image } // Pass the image header parameter
173
+ );
174
+ }
175
+
176
+ // Pass through to original sendMessage for standard texts, images, etc.
177
+ return await originalSendMessage.call(sock, jid, content, options);
178
+ };
179
+
180
+ // 5. Built-in message serializer directly on the socket
181
+ sock.serializeMessage = (m) => {
182
+ if (!m) return m;
183
+
184
+ if (m.key) {
185
+ m.id = m.key.id;
186
+ m.isBot = m.id.startsWith('BAE5') || m.id.startsWith('HSK');
187
+ m.chat = m.key.remoteJid;
188
+ m.isGroup = m.chat.endsWith('@g.us');
189
+ m.sender = m.isGroup ? (m.key.participant || m.chat) : m.chat;
190
+ m.fromMe = m.key.fromMe;
191
+ }
192
+
193
+ if (m.message) {
194
+ m.type = getContentType(m.message);
195
+
196
+ // Core body text extraction (including button responses)
197
+ m.body = (m.type === 'conversation') ? m.message.conversation :
198
+ (m.type === 'extendedTextMessage') ? m.message.extendedTextMessage.text :
199
+ (m.type === 'imageMessage') ? m.message.imageMessage.caption :
200
+ (m.type === 'videoMessage') ? m.message.videoMessage.caption :
201
+ (m.type === 'templateButtonReplyMessage') ? m.message.templateButtonReplyMessage.selectedId :
202
+ (m.type === 'buttonsResponseMessage') ? m.message.buttonsResponseMessage.selectedButtonId :
203
+ (m.type === 'interactiveResponseMessage') ? JSON.parse(m.message.interactiveResponseMessage.nativeFlowResponseMessage.paramsJson).id :
204
+ "";
205
+
206
+ // Handle quoting
207
+ const quotedMsg = m.message[m.type]?.contextInfo?.quotedMessage;
208
+ if (quotedMsg) {
209
+ m.quoted = {
210
+ id: m.message[m.type].contextInfo.stanzaId,
211
+ sender: m.message[m.type].contextInfo.participant,
212
+ message: quotedMsg,
213
+ type: getContentType(quotedMsg),
214
+ body: (quotedMsg.conversation) ? quotedMsg.conversation :
215
+ (quotedMsg.extendedTextMessage?.text) ? quotedMsg.extendedTextMessage.text : ""
216
+ };
217
+ } else {
218
+ m.quoted = null;
219
+ }
220
+
221
+ m.mentionedJid = m.message[m.type]?.contextInfo?.mentionedJid || [];
222
+ }
223
+
224
+ // Attach simple reply helpers
225
+ m.reply = async (text, options = {}) => {
226
+ return await sock.sendMessage(m.chat, { text }, { quoted: m, ...options });
227
+ };
228
+
229
+ m.replyButtons = async (header, text, footer, buttons) => {
230
+ return await sock.sendButtons(m.chat, header, text, footer, buttons, m);
231
+ };
232
+
233
+ m.replyList = async (header, text, footer, buttonText, sections) => {
234
+ return await sock.sendList(m.chat, header, text, footer, buttonText, sections, m);
235
+ };
236
+
237
+ return m;
238
+ };
239
+
240
+ // 6. Built-in, high-performance automated Pairing Code requester
241
+ sock.pairWithPhoneNumber = async (phoneNumber, loggerCallback = null) => {
242
+ if (sock.authState.creds.registered) {
243
+ return null; // Already connected
244
+ }
245
+
246
+ const cleanNumber = phoneNumber.replace(/[^0-9]/g, '');
247
+ if (!cleanNumber) {
248
+ throw new Error("Invalid phone number format. Must contain digits only.");
249
+ }
250
+
251
+ // Set up callback or default console printer
252
+ const printCode = loggerCallback || ((code) => {
253
+ console.log(chalk.black.bgGreen(`\n==============================================`));
254
+ console.log(chalk.black.bgGreen(` 🔑 WHATSAPP PAIRING CODE: ${code} `));
255
+ console.log(chalk.black.bgGreen(`==============================================\n`));
256
+ console.log(chalk.cyan(`How to pair:`));
257
+ console.log(chalk.gray(`1. Open WhatsApp on your phone.`));
258
+ console.log(chalk.gray(`2. Go to: Linked Devices -> Link a Device -> Link with phone number.`));
259
+ console.log(chalk.gray(`3. Enter the 8-character pairing code: `) + chalk.green.bold(code) + `\n`);
260
+ });
261
+
262
+ // Delay execution by 3.5 seconds to ensure the WebSocket connection is open and ready
263
+ return new Promise((resolve, reject) => {
264
+ setTimeout(async () => {
265
+ try {
266
+ let code = await sock.requestPairingCode(cleanNumber);
267
+ code = code?.match(/.{1,4}/g)?.join("-") || code;
268
+ printCode(code);
269
+ resolve(code);
270
+ } catch (err) {
271
+ reject(err);
272
+ }
273
+ }, 3500);
274
+ });
275
+ };
276
+
277
+ return sock;
278
+ }
279
+
280
+ // Export everything from WhiskeySockets Baileys so this works as a drop-in replacement!
281
+ module.exports = {
282
+ ...Baileys,
283
+ default: makeWASocket,
284
+ makeWASocket
285
+ };
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@nozez-lab/baileys",
3
+ "version": "1.0.0",
4
+ "description": "Customized WhatsApp Baileys library supporting native buttons, single select submenus, and simplified pairing code connections",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node test.js"
8
+ },
9
+ "keywords": [
10
+ "whatsapp",
11
+ "baileys",
12
+ "buttons",
13
+ "pairing-code",
14
+ "nozezbaileys"
15
+ ],
16
+ "author": "Nozez",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@whiskeysockets/baileys": "^6.7.7",
20
+ "chalk": "^4.1.2",
21
+ "pino": "^9.1.0",
22
+ "qrcode-terminal": "^0.12.0"
23
+ }
24
+ }
package/test.js ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * test.js - Demo / Testing Script for NozezBaileys Custom Library
3
+ * Shows how simple it is to build a bot using your own custom Baileys!
4
+ */
5
+
6
+ // 1. Import your own customized Baileys!
7
+ const { default: makeWASocket, useMultiFileAuthState, DisconnectReason } = require('./index');
8
+ const pino = require('pino');
9
+ const path = require('path');
10
+
11
+ async function startBot() {
12
+ console.log("🚀 Starting connection using custom NozezBaileys library...\n");
13
+
14
+ // 2. Setup Multi-File Authentication State
15
+ const { state, saveCreds } = await useMultiFileAuthState('nozez_session');
16
+
17
+ // 3. Connect using custom makeWASocket
18
+ const sock = makeWASocket({
19
+ auth: state,
20
+ logger: pino({ level: 'silent' }), // High performance quiet logging
21
+ printQRInTerminal: true // Prints QR automatically if not pairing
22
+ });
23
+
24
+ // Save credentials when updated
25
+ sock.ev.on('creds.update', saveCreds);
26
+
27
+ // 4. SUPPORTS PAIRING CODE NATIVELY WITH 1-LINE CALL!
28
+ const usePairingCode = false; // Set to true to link using pairing code instead of scanning QR
29
+ const phoneNumber = "6282211559988"; // Replace with your target WhatsApp number
30
+
31
+ if (usePairingCode && !sock.authState.creds.registered) {
32
+ console.log(`[INFO] Pairing active. Requesting pairing code for +${phoneNumber}...`);
33
+ await sock.pairWithPhoneNumber(phoneNumber);
34
+ }
35
+
36
+ // 5. Connection events handling
37
+ sock.ev.on('connection.update', (update) => {
38
+ const { connection, lastDisconnect } = update;
39
+ if (connection === 'close') {
40
+ const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
41
+ console.log(`[CONN] Connection closed. Reconnecting: ${shouldReconnect}`);
42
+ if (shouldReconnect) startBot();
43
+ } else if (connection === 'open') {
44
+ console.log('\n======================================================');
45
+ console.log('🎉 SUCCESS! Connected to WhatsApp using NozezBaileys! 🎉');
46
+ console.log('======================================================\n');
47
+ console.log('Ketik perintah berikut di chat WhatsApp bot Anda:');
48
+ console.log('👉 .testbutton (Untuk menguji tombol interaktif)');
49
+ console.log('👉 .testlist (Untuk menguji dropdown sub-menu)\n');
50
+ }
51
+ });
52
+
53
+ // 6. Messages upsert listener
54
+ sock.ev.on('messages.upsert', async (chatUpdate) => {
55
+ const m = chatUpdate.messages[0];
56
+ if (!m.message) return;
57
+
58
+ // SUPPORTS BUILT-IN SERIALIZATION DIRECTLY ON THE SOCKET!
59
+ const parsed = sock.serializeMessage(m);
60
+ if (parsed.isBot) return; // Prevent loops
61
+
62
+ const body = parsed.body.trim().toLowerCase();
63
+
64
+ // 7. SUPPORTS SENDING NATIVE BUTTONS DIRECTLY VIA standard 'sendMessage'!
65
+ if (body === '.testbutton') {
66
+ await sock.sendMessage(parsed.chat, {
67
+ header: "✨ NozezBaileys Custom Buttons ✨",
68
+ text: "Halo! Ini adalah tombol interaktif native flow yang dikirim menggunakan standard `sendMessage` pada library buatan Anda sendiri!",
69
+ footer: "Mudah, ringkas, dan sangat optimal.",
70
+ buttons: [
71
+ { type: 'reply', displayText: '🎯 Pilihan Pertama', id: 'pilihan_1' },
72
+ { type: 'url', displayText: '🌐 Kunjungi Website', url: 'https://github.com' },
73
+ { type: 'copy', displayText: '📋 Salin Kode Promo', code: 'NOZEZ2026' }
74
+ ]
75
+ }, { quoted: m });
76
+ }
77
+
78
+ // 8. SUPPORTS SENDING DROPDOWN SUB-MENUS DIRECTLY VIA standard 'sendMessage'!
79
+ if (body === '.testlist') {
80
+ await sock.sendMessage(parsed.chat, {
81
+ // To send an image header, simply pass a valid image buffer, stream, or URL!
82
+ image: { url: "https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/assets/logo.png" },
83
+ header: "🖼️ Buttons dengan Gambar",
84
+ text: "Pilih aksi atau buka daftar pilihan:\n\n_Contoh buttons dengan header image & native flow list_",
85
+ footer: "CHATBOT ASSISTANCE v1 BY NOZEZ ⚡",
86
+ buttonText: "📋 Pilih Menu",
87
+ sections: [
88
+ {
89
+ title: "🌐 General",
90
+ rows: [
91
+ { title: "🔍 Ping Bot", description: "Cek status dan latensi bot", id: "ping" },
92
+ { title: "📋 Menu Lengkap", description: "Lihat semua perintah yang tersedia", id: "menu" }
93
+ ]
94
+ },
95
+ {
96
+ title: "👑 Owner",
97
+ rows: [
98
+ { title: "📡 Broadcast", description: "Kirim pesan ke semua user", id: "bc", highlight_label: "🔥 Eksklusif" }
99
+ ]
100
+ }
101
+ ]
102
+ }, { quoted: m });
103
+ }
104
+
105
+ // 9. SUPPORTS INTERCEPTING BUTTON & SUBMENU DROPDOWN CLICKS NATIVELY!
106
+ if (body === 'pilihan_1') {
107
+ await parsed.reply("✅ Anda berhasil mengklik *Pilihan Pertama*!");
108
+ }
109
+ if (body === 'ping') {
110
+ await parsed.reply("⚡ *Ping Bot Terpilih!* Status bot Anda aktif dan sangat responsif.");
111
+ }
112
+ if (body === 'menu') {
113
+ await parsed.reply("📋 *Menu Lengkap Terpilih!* Membuka seluruh daftar fitur bot.");
114
+ }
115
+ if (body === 'bc') {
116
+ await parsed.reply("📡 *Broadcast Terpilih!* Fitur eksklusif ini dipicu karena Anda mengklik tombol berlabel lencana.");
117
+ }
118
+ });
119
+ }
120
+
121
+ // Start execution
122
+ startBot().catch(console.error);