f-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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 f-baileys
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # f-baileys ๐Ÿš€
2
+
3
+ > **Fast ยท Stable ยท Multi-Platform WhatsApp Bot Library**
4
+ > Pairing Code โœ“ | QR Code โœ“ | Auto-Install โœ“ | Termux/VPS/Panel โœ“
5
+
6
+ [![npm version](https://img.shields.io/npm/v/f-baileys.svg?style=flat-square)](https://npmjs.com/package/f-baileys)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](LICENSE)
8
+ [![Node](https://img.shields.io/badge/Node-%3E%3D16-brightgreen?style=flat-square)](https://nodejs.org)
9
+
10
+ ---
11
+
12
+ ## โœจ Features
13
+
14
+ | Feature | Status |
15
+ |---------|--------|
16
+ | Pairing Code login (100% masuk) | โœ… |
17
+ | QR Code login | โœ… |
18
+ | Auto-reconnect on disconnect | โœ… |
19
+ | Multi-platform support | โœ… |
20
+ | Auto-install missing dependencies | โœ… |
21
+ | Serialized message objects | โœ… |
22
+ | Send Text/Image/Video/Audio/Sticker/Document | โœ… |
23
+ | TypeScript definitions | โœ… |
24
+ | Pterodactyl Panel compatible | โœ… |
25
+ | Termux (F-Droid) compatible | โœ… |
26
+
27
+ ---
28
+
29
+ ## ๐Ÿ“ฆ Install
30
+
31
+ ```bash
32
+ npm install f-baileys
33
+ ```
34
+
35
+ > Saat install, f-baileys otomatis mendeteksi platform dan menginstall dependensi yang kurang.
36
+
37
+ ---
38
+
39
+ ## ๐Ÿš€ Quick Start
40
+
41
+ ### Mode QR Code
42
+
43
+ ```js
44
+ const FBaileys = require('f-baileys');
45
+
46
+ const bot = new FBaileys({
47
+ sessionPath: './session',
48
+ usePairingCode: false, // QR Code mode
49
+ printQR: true,
50
+ autoReconnect: true,
51
+ });
52
+
53
+ bot.on('open', () => console.log('โœ“ Connected!'));
54
+
55
+ bot.on('message', async (msg) => {
56
+ if (msg.fromMe) return;
57
+ if (msg.text === 'ping') {
58
+ await msg.reply('Pong! ๐Ÿ“');
59
+ }
60
+ });
61
+
62
+ bot.connect();
63
+ ```
64
+
65
+ ### Mode Pairing Code
66
+
67
+ ```js
68
+ const FBaileys = require('f-baileys');
69
+
70
+ const bot = new FBaileys({
71
+ sessionPath: './session',
72
+ usePairingCode: true, // โ† Pairing Code
73
+ phoneNumber: '628xxxxxxxxxx', // Nomor HP (tanpa + atau spasi)
74
+ autoReconnect: true,
75
+ });
76
+
77
+ bot.on('open', () => console.log('โœ“ Connected via Pairing Code!'));
78
+ bot.on('message', async (msg) => {
79
+ if (msg.text === 'halo') await msg.reply('Halo juga!');
80
+ });
81
+
82
+ bot.connect();
83
+ ```
84
+
85
+ ---
86
+
87
+ ## โš™๏ธ Options
88
+
89
+ | Option | Type | Default | Description |
90
+ |--------|------|---------|-------------|
91
+ | `sessionPath` | string | `'./f_session'` | Folder simpan session |
92
+ | `usePairingCode` | boolean | `false` | `true` = pairing code, `false` = QR |
93
+ | `phoneNumber` | string | `null` | Nomor HP untuk pairing code |
94
+ | `printQR` | boolean | `true` | Auto print QR ke terminal |
95
+ | `logLevel` | string | `'silent'` | `'silent'` / `'info'` / `'debug'` |
96
+ | `autoReconnect` | boolean | `true` | Auto reconnect saat putus |
97
+ | `reconnectDelay` | number | `3000` | Delay reconnect (ms) |
98
+ | `baileysOptions` | object | `{}` | Extra config ke makeWASocket |
99
+
100
+ ---
101
+
102
+ ## ๐Ÿ“จ Message Object
103
+
104
+ Setiap pesan yang diterima sudah diserialisasi:
105
+
106
+ ```js
107
+ bot.on('message', async (msg) => {
108
+ msg.id // ID pesan
109
+ msg.from // JID pengirim/grup
110
+ msg.sender // JID pengirim (di dalam grup)
111
+ msg.senderNum // Nomor pengirim (tanpa @s.whatsapp.net)
112
+ msg.fromMe // true jika pesan dari bot sendiri
113
+ msg.isGroup // true jika pesan dari grup
114
+ msg.text // Isi teks pesan
115
+ msg.type // Tipe pesan (imageMessage, videoMessage, dll)
116
+ msg.pushName // Nama pengirim
117
+ msg.timestamp // Unix timestamp
118
+
119
+ // Flags
120
+ msg.isText // Pesan teks
121
+ msg.isImage // Pesan gambar
122
+ msg.isVideo // Pesan video
123
+ msg.isAudio // Pesan audio
124
+ msg.isSticker // Pesan stiker
125
+ msg.isDocument // Pesan dokumen
126
+
127
+ // Quoted message
128
+ msg.quoted?.body // Teks quoted
129
+ msg.quoted?.type // Tipe quoted
130
+
131
+ // Helpers
132
+ await msg.reply('Teks') // Balas pesan
133
+ await msg.react('โœ…') // Reaksi emoji
134
+ });
135
+ ```
136
+
137
+ ---
138
+
139
+ ## ๐Ÿ“ค Send Methods
140
+
141
+ ```js
142
+ // Teks
143
+ await bot.sendText(jid, 'Hello!');
144
+ await bot.sendText(jid, 'Reply!', quotedMsg);
145
+
146
+ // Gambar
147
+ await bot.sendImage(jid, './gambar.jpg', 'Caption');
148
+ await bot.sendImage(jid, 'https://..../img.jpg', 'Caption');
149
+
150
+ // Video
151
+ await bot.sendVideo(jid, './video.mp4', 'Caption');
152
+
153
+ // Audio
154
+ await bot.sendAudio(jid, './audio.mp3');
155
+ await bot.sendAudio(jid, './voice.ogg', true); // ptt = voice note
156
+
157
+ // Sticker
158
+ await bot.sendSticker(jid, './sticker.webp');
159
+
160
+ // Dokumen
161
+ await bot.sendDocument(jid, './file.pdf', 'dokumen.pdf', 'application/pdf');
162
+
163
+ // Reaksi
164
+ await bot.sendReaction(jid, msg.key, '๐Ÿ”ฅ');
165
+
166
+ // Typing indicator
167
+ await bot.sendTyping(jid, 2000); // Typing selama 2 detik
168
+
169
+ // Download media dari pesan
170
+ const buffer = await bot.downloadMedia(msg);
171
+ ```
172
+
173
+ ---
174
+
175
+ ## ๐ŸŒ Platform Support
176
+
177
+ | Platform | Status |
178
+ |----------|--------|
179
+ | VPS Linux (Ubuntu/Debian/CentOS) | โœ… |
180
+ | Termux (Google Play) | โœ… |
181
+ | Termux (F-Droid) | โœ… |
182
+ | Pterodactyl Panel | โœ… |
183
+ | Docker | โœ… |
184
+ | Windows | โœ… |
185
+ | macOS | โœ… |
186
+
187
+ ---
188
+
189
+ ## ๐Ÿ“ฆ Dependencies (Auto-Install)
190
+
191
+ Saat `npm install f-baileys`, package berikut otomatis diinstall jika belum ada:
192
+
193
+ | Package | Fungsi |
194
+ |---------|--------|
195
+ | `@whiskeysockets/baileys` | Core WhatsApp MD |
196
+ | `sharp` | Image processing (WebP, resize, dll) |
197
+ | `jimp` | Pure JS image processing |
198
+ | `fluent-ffmpeg` | FFmpeg wrapper (convert audio/video) |
199
+ | `ffmpeg-static` | Binary FFmpeg |
200
+ | `ytdl-core` | YouTube downloader |
201
+ | `webp-converter` | Konversi ke WebP (sticker) |
202
+ | `color` | Manipulasi warna |
203
+ | `chalk` | Warna terminal |
204
+ | `qrcode-terminal` | Print QR di terminal |
205
+ | `axios` | HTTP requests |
206
+ | `pino` | Logger |
207
+
208
+ > Untuk Termux: f-baileys otomatis menjalankan `pkg install ffmpeg nodejs` juga.
209
+
210
+ ---
211
+
212
+ ## ๐Ÿ“ฑ Pairing Code Guide
213
+
214
+ 1. Jalankan bot dengan `usePairingCode: true`
215
+ 2. Kode 8 digit akan muncul di terminal (format: `XXXX-XXXX`)
216
+ 3. Buka WhatsApp di HP โ†’ **Perangkat Tertaut** โ†’ **Tautkan Perangkat**
217
+ 4. Pilih **Tautkan dengan Nomor Telepon**
218
+ 5. Masukkan kode yang tampil di terminal
219
+ 6. Selesai! โœ“
220
+
221
+ ---
222
+
223
+ ## ๐Ÿ”ง Events
224
+
225
+ ```js
226
+ bot.on('qr', (qr) => { ... }) // QR string
227
+ bot.on('open', (sock) => { ... }) // Connected
228
+ bot.on('close', ({ reason, code }) => {}) // Disconnected
229
+ bot.on('message', (msg) => { ... }) // Pesan masuk
230
+ bot.on('logout', () => { ... }) // Session logout
231
+ bot.on('group-update', (update) => { ... }) // Update grup
232
+ ```
233
+
234
+ ---
235
+
236
+ ## ๐Ÿ›  Utility
237
+
238
+ ```js
239
+ // Convert nomor ke JID
240
+ FBaileys.toJid('628123456789') // โ†’ 628123456789@s.whatsapp.net
241
+ FBaileys.toGroupJid('120363xxxxxx') // โ†’ 120363xxxxxx@g.us
242
+ ```
243
+
244
+ ---
245
+
246
+ ## ๐Ÿ“„ License
247
+
248
+ MIT ยฉ f-baileys
@@ -0,0 +1,38 @@
1
+ /**
2
+ * f-baileys โ€” Example: Pairing Code Login
3
+ * =========================================
4
+ * npm install f-baileys
5
+ * node examples/pairing-login.js
6
+ */
7
+
8
+ const FBaileys = require('f-baileys');
9
+
10
+ // Ganti dengan nomor kamu (tanpa + atau spasi)
11
+ const PHONE_NUMBER = '628xxxxxxxxxx';
12
+
13
+ const bot = new FBaileys({
14
+ sessionPath: './session_pair',
15
+ usePairingCode: true, // โ† Pairing Code Mode
16
+ phoneNumber: PHONE_NUMBER,
17
+ logLevel: 'info',
18
+ autoReconnect: true,
19
+ reconnectDelay: 3000,
20
+ });
21
+
22
+ bot.on('open', (sock) => {
23
+ console.log('โœ“ Connected via Pairing Code!');
24
+ });
25
+
26
+ bot.on('message', async (msg) => {
27
+ if (msg.fromMe) return;
28
+
29
+ if (msg.text?.toLowerCase() === 'ping') {
30
+ await msg.reply('๐Ÿ“ Pong!');
31
+ }
32
+ });
33
+
34
+ bot.on('logout', () => {
35
+ console.log('Session logged out โ€” re-run to pair again');
36
+ });
37
+
38
+ bot.connect();
@@ -0,0 +1,53 @@
1
+ /**
2
+ * f-baileys โ€” Example: QR Code Login
3
+ * ====================================
4
+ * npm install f-baileys
5
+ * node examples/qr-login.js
6
+ */
7
+
8
+ const FBaileys = require('f-baileys');
9
+
10
+ const bot = new FBaileys({
11
+ sessionPath: './session',
12
+ usePairingCode: false, // false = QR Code mode
13
+ printQR: true, // Print QR to terminal
14
+ logLevel: 'info',
15
+ autoReconnect: true,
16
+ reconnectDelay: 3000,
17
+ });
18
+
19
+ // โ”€โ”€ Event: QR Generated โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
+ bot.on('qr', (qr) => {
21
+ console.log('QR received โ€” scan with WhatsApp!');
22
+ });
23
+
24
+ // โ”€โ”€ Event: Connected โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
25
+ bot.on('open', (sock) => {
26
+ console.log('โœ“ Bot connected!');
27
+ });
28
+
29
+ // โ”€โ”€ Event: Message Received โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
30
+ bot.on('message', async (msg) => {
31
+ if (msg.fromMe) return; // Ignore own messages
32
+
33
+ console.log(`[${msg.senderNum}]: ${msg.text}`);
34
+
35
+ // Ping command
36
+ if (msg.text?.toLowerCase() === 'ping') {
37
+ await msg.reply('๐Ÿ“ Pong! f-baileys is running.');
38
+ await msg.react('โœ…');
39
+ }
40
+
41
+ // Info command
42
+ if (msg.text?.toLowerCase() === '!info') {
43
+ await msg.reply(`*f-baileys Bot*\nPlatform: ${bot.platform.name}\nNode: ${process.version}`);
44
+ }
45
+ });
46
+
47
+ // โ”€โ”€ Event: Disconnect โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
48
+ bot.on('close', ({ reason }) => {
49
+ console.log(`Disconnected: ${reason}`);
50
+ });
51
+
52
+ // โ”€โ”€ Start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
53
+ bot.connect();
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "f-baileys",
3
+ "version": "1.0.0",
4
+ "description": "๐Ÿš€ Fast & Stable WhatsApp Bot Library โ€” Pairing Code + QR Code Support, Multi-Platform (Termux/VPS/Panel), Full Dependency Auto-Installer",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "scripts": {
8
+ "start": "node src/index.js",
9
+ "example": "node examples/basic.js",
10
+ "postinstall": "node scripts/auto-install.js"
11
+ },
12
+ "keywords": [
13
+ "whatsapp",
14
+ "baileys",
15
+ "bot",
16
+ "pairing-code",
17
+ "qrcode",
18
+ "whatsapp-bot",
19
+ "termux",
20
+ "vps",
21
+ "multi-device",
22
+ "f-baileys"
23
+ ],
24
+ "author": "f-baileys",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/f-baileys/f-baileys"
29
+ },
30
+ "engines": {
31
+ "node": ">=16.0.0"
32
+ },
33
+ "peerDependencies": {
34
+ "@whiskeysockets/baileys": "^6.7.0"
35
+ },
36
+ "dependencies": {
37
+ "@whiskeysockets/baileys": "^6.7.0",
38
+ "pino": "^8.0.0",
39
+ "qrcode-terminal": "^0.12.0",
40
+ "chalk": "^4.1.2",
41
+ "node-fetch": "^2.7.0",
42
+ "axios": "^1.6.0",
43
+ "fs-extra": "^11.2.0",
44
+ "moment": "^2.30.1",
45
+ "ora": "^5.4.1",
46
+ "cli-table3": "^0.6.3"
47
+ },
48
+ "optionalDependencies": {
49
+ "sharp": "^0.33.0",
50
+ "jimp": "^0.22.12",
51
+ "fluent-ffmpeg": "^2.1.3",
52
+ "ffmpeg-static": "^5.2.0",
53
+ "ytdl-core": "^4.11.5",
54
+ "webp-converter": "^2.3.0",
55
+ "color": "^4.2.3",
56
+ "@ffmpeg/ffmpeg": "^0.12.10"
57
+ }
58
+ }
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env node
2
+ // f-baileys Auto Dependency Installer
3
+ // Detects platform: Termux (fdroid), VPS Linux, Windows, Panel (Pterodactyl)
4
+
5
+ const { execSync, spawnSync } = require('child_process');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+
10
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
+ // COLORS (tanpa chalk karena belum tentu ada)
12
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
+ const c = {
14
+ reset: '\x1b[0m',
15
+ green: '\x1b[32m',
16
+ yellow: '\x1b[33m',
17
+ cyan: '\x1b[36m',
18
+ red: '\x1b[31m',
19
+ bold: '\x1b[1m',
20
+ magenta: '\x1b[35m',
21
+ };
22
+ const log = {
23
+ info: (m) => console.log(`${c.cyan}[f-baileys]${c.reset} ${m}`),
24
+ success: (m) => console.log(`${c.green}[f-baileys] โœ“${c.reset} ${m}`),
25
+ warn: (m) => console.log(`${c.yellow}[f-baileys] โš ${c.reset} ${m}`),
26
+ error: (m) => console.log(`${c.red}[f-baileys] โœ—${c.reset} ${m}`),
27
+ banner: () => console.log(`
28
+ ${c.magenta}${c.bold}
29
+ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
30
+ โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ• โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ•šโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•
31
+ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
32
+ โ–ˆโ–ˆโ•”โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ• โ•šโ–ˆโ–ˆโ•”โ• โ•šโ•โ•โ•โ•โ–ˆโ–ˆโ•‘
33
+ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘
34
+ โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•โ•šโ•โ•โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ•โ•
35
+ ${c.reset}${c.cyan} Fast & Stable WhatsApp Bot Library v1.0.0${c.reset}
36
+ ${c.yellow}Pairing Code โœ“ | QR Code โœ“ | Multi-Platform โœ“ | Auto-Install โœ“${c.reset}
37
+ `),
38
+ };
39
+
40
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
41
+ // PLATFORM DETECTION
42
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
43
+ function detectPlatform() {
44
+ const platform = {
45
+ isTermux: false,
46
+ isVPS: false,
47
+ isPterodactyl: false,
48
+ isWindows: false,
49
+ isMac: false,
50
+ name: 'unknown',
51
+ pkgManager: 'npm',
52
+ };
53
+
54
+ // Windows
55
+ if (os.platform() === 'win32') {
56
+ platform.isWindows = true;
57
+ platform.name = 'Windows';
58
+ return platform;
59
+ }
60
+
61
+ // macOS
62
+ if (os.platform() === 'darwin') {
63
+ platform.isMac = true;
64
+ platform.name = 'macOS';
65
+ return platform;
66
+ }
67
+
68
+ // Termux detection
69
+ const termuxIndicators = [
70
+ process.env.TERMUX_VERSION,
71
+ process.env.PREFIX && process.env.PREFIX.includes('com.termux'),
72
+ fs.existsSync('/data/data/com.termux'),
73
+ fs.existsSync('/data/data/com.termux.fdroid'),
74
+ process.env.TERMUX_APP_PACKAGE_NAME,
75
+ ];
76
+ if (termuxIndicators.some(Boolean)) {
77
+ platform.isTermux = true;
78
+ platform.name = 'Termux';
79
+ platform.pkgManager = 'pkg';
80
+ return platform;
81
+ }
82
+
83
+ // Pterodactyl Panel detection
84
+ const pteroIndicators = [
85
+ process.env.P_SERVER_UUID,
86
+ process.env.P_SERVER_LOCATION,
87
+ fs.existsSync('/.dockerenv'),
88
+ process.env.PTERODACTYL,
89
+ process.env.SERVER_MEMORY,
90
+ ];
91
+ if (pteroIndicators.some(Boolean)) {
92
+ platform.isPterodactyl = true;
93
+ platform.name = 'Pterodactyl Panel';
94
+ return platform;
95
+ }
96
+
97
+ // VPS Linux
98
+ platform.isVPS = true;
99
+ platform.name = 'VPS/Linux';
100
+ return platform;
101
+ }
102
+
103
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
104
+ // OPTIONAL DEPENDENCIES TO TRY INSTALL
105
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
106
+ const OPTIONAL_DEPS = [
107
+ { name: 'sharp', version: '^0.33.0', desc: 'Image processing' },
108
+ { name: 'jimp', version: '^0.22.12', desc: 'Pure JS image processing' },
109
+ { name: 'fluent-ffmpeg', version: '^2.1.3', desc: 'FFmpeg wrapper' },
110
+ { name: 'ffmpeg-static', version: '^5.2.0', desc: 'Static FFmpeg binary' },
111
+ { name: 'ytdl-core', version: '^4.11.5', desc: 'YouTube downloader' },
112
+ { name: 'webp-converter', version: '^2.3.0', desc: 'WebP conversion' },
113
+ { name: 'color', version: '^4.2.3', desc: 'Color manipulation' },
114
+ ];
115
+
116
+ function isInstalled(pkg) {
117
+ try {
118
+ require.resolve(pkg);
119
+ return true;
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ function installPkg(pkg, platform) {
126
+ const cmd = `npm install ${pkg} --no-save --legacy-peer-deps 2>&1`;
127
+ try {
128
+ execSync(cmd, { stdio: 'pipe' });
129
+ return true;
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
136
+ // TERMUX SYSTEM PACKAGES
137
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
138
+ function installTermuxSystemDeps() {
139
+ const packages = ['ffmpeg', 'nodejs', 'python', 'libwebp'];
140
+ log.info('Installing Termux system packages...');
141
+ for (const pkg of packages) {
142
+ try {
143
+ execSync(`pkg install -y ${pkg} 2>&1`, { stdio: 'pipe' });
144
+ log.success(`Termux pkg installed: ${pkg}`);
145
+ } catch {
146
+ log.warn(`Skipped (already installed or unavailable): ${pkg}`);
147
+ }
148
+ }
149
+ }
150
+
151
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
152
+ // MAIN
153
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
154
+ async function main() {
155
+ log.banner();
156
+
157
+ const platform = detectPlatform();
158
+ log.info(`Platform detected: ${c.bold}${c.green}${platform.name}${c.reset}`);
159
+
160
+ // Termux: install system packages first
161
+ if (platform.isTermux) {
162
+ log.info('Termux detected โ€” installing system dependencies first...');
163
+ installTermuxSystemDeps();
164
+ }
165
+
166
+ // Install optional deps
167
+ log.info('Checking optional dependencies...');
168
+ let installed = 0, skipped = 0, failed = 0;
169
+
170
+ for (const dep of OPTIONAL_DEPS) {
171
+ if (isInstalled(dep.name)) {
172
+ log.success(`Already installed: ${dep.name}`);
173
+ skipped++;
174
+ continue;
175
+ }
176
+ process.stdout.write(`${c.cyan}[f-baileys]${c.reset} Installing ${dep.name} (${dep.desc})... `);
177
+ const ok = installPkg(`${dep.name}@${dep.version}`, platform);
178
+ if (ok) {
179
+ console.log(`${c.green}โœ“${c.reset}`);
180
+ installed++;
181
+ } else {
182
+ console.log(`${c.yellow}skipped${c.reset}`);
183
+ failed++;
184
+ }
185
+ }
186
+
187
+ console.log('');
188
+ log.success(`Setup complete! ${installed} installed, ${skipped} already present, ${failed} skipped`);
189
+ log.info(`Use: ${c.bold}${c.cyan}require('f-baileys')${c.reset}`);
190
+ console.log('');
191
+ }
192
+
193
+ main().catch((e) => {
194
+ log.error('Auto-install failed (non-critical): ' + e.message);
195
+ });
package/src/index.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { EventEmitter } from 'events';
2
+ import { WASocket } from '@whiskeysockets/baileys';
3
+
4
+ export interface FBaileysOptions {
5
+ sessionPath?: string;
6
+ usePairingCode?: boolean;
7
+ phoneNumber?: string;
8
+ printQR?: boolean;
9
+ logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug';
10
+ autoReconnect?: boolean;
11
+ reconnectDelay?: number;
12
+ baileysOptions?: Record<string, any>;
13
+ }
14
+
15
+ export interface PlatformInfo {
16
+ isTermux: boolean;
17
+ isVPS: boolean;
18
+ isPterodactyl: boolean;
19
+ isWindows: boolean;
20
+ isMac: boolean;
21
+ isDocker: boolean;
22
+ name: string;
23
+ arch: string;
24
+ nodeVersion: string;
25
+ }
26
+
27
+ export interface SerializedMessage {
28
+ id: string;
29
+ from: string;
30
+ fromMe: boolean;
31
+ pushName: string;
32
+ isGroup: boolean;
33
+ sender: string;
34
+ senderNum: string;
35
+ type: string;
36
+ body: string;
37
+ text: string;
38
+ timestamp: number;
39
+ isText: boolean;
40
+ isImage: boolean;
41
+ isVideo: boolean;
42
+ isAudio: boolean;
43
+ isSticker: boolean;
44
+ isDocument: boolean;
45
+ isPoll: boolean;
46
+ mentions: string[];
47
+ quoted: SerializedMessage | null;
48
+ reply: (text: string) => Promise<any>;
49
+ react: (emoji: string) => Promise<any>;
50
+ }
51
+
52
+ export declare class FBaileys extends EventEmitter {
53
+ constructor(options?: FBaileysOptions);
54
+
55
+ options: Required<FBaileysOptions>;
56
+ sock: WASocket | null;
57
+ isConnected: boolean;
58
+ platform: PlatformInfo;
59
+
60
+ connect(): Promise<WASocket>;
61
+ sendText(jid: string, text: string, quoted?: any): Promise<any>;
62
+ sendImage(jid: string, source: string | Buffer, caption?: string, quoted?: any): Promise<any>;
63
+ sendVideo(jid: string, source: string | Buffer, caption?: string, quoted?: any): Promise<any>;
64
+ sendAudio(jid: string, source: string | Buffer, ptt?: boolean, quoted?: any): Promise<any>;
65
+ sendSticker(jid: string, source: string | Buffer, quoted?: any): Promise<any>;
66
+ sendDocument(jid: string, source: string | Buffer, filename: string, mimetype?: string, quoted?: any): Promise<any>;
67
+ sendReaction(jid: string, key: any, emoji: string): Promise<any>;
68
+ sendTyping(jid: string, duration?: number): Promise<void>;
69
+ downloadMedia(message: any): Promise<Buffer>;
70
+
71
+ static toJid(number: string): string;
72
+ static toGroupJid(id: string): string;
73
+
74
+ on(event: 'qr', listener: (qr: string) => void): this;
75
+ on(event: 'open', listener: (sock: WASocket) => void): this;
76
+ on(event: 'close', listener: (info: { reason: string; code: number }) => void): this;
77
+ on(event: 'message', listener: (msg: SerializedMessage) => void): this;
78
+ on(event: 'logout', listener: () => void): this;
79
+ on(event: 'group-update', listener: (update: any) => void): this;
80
+ }
81
+
82
+ export declare function detectPlatform(): PlatformInfo;
83
+ export default FBaileys;
package/src/index.js ADDED
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
5
+ * โ•‘ f-baileys โ€” Main Entry โ•‘
6
+ * โ•‘ Fast ยท Stable ยท Multi-Platform WhatsApp Library โ•‘
7
+ * โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
8
+ */
9
+
10
+ const { makeWASocket, useMultiFileAuthState, makeCacheableSignalKeyStore,
11
+ DisconnectReason, fetchLatestBaileysVersion, proto,
12
+ downloadMediaMessage, getContentType } = require('@whiskeysockets/baileys');
13
+ const pino = require('pino');
14
+ const path = require('path');
15
+ const fs = require('fs');
16
+ const { EventEmitter } = require('events');
17
+
18
+ const FLogger = require('./logger');
19
+ const { detectPlatform } = require('./platform');
20
+ const { FQR } = require('./qr');
21
+ const { FPairing } = require('./pairing');
22
+ const FSerializer = require('./serializer');
23
+
24
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
25
+ class FBaileys extends EventEmitter {
26
+ /**
27
+ * @param {Object} options
28
+ * @param {string} [options.sessionPath] - Path to save session files
29
+ * @param {boolean} [options.usePairingCode] - true = pairing code, false = QR
30
+ * @param {string} [options.phoneNumber] - Phone number for pairing code (with country code, no +)
31
+ * @param {boolean} [options.printQR] - Auto-print QR to terminal
32
+ * @param {string} [options.logLevel] - 'silent'|'error'|'warn'|'info'|'debug'
33
+ * @param {boolean} [options.autoReconnect] - Auto reconnect on disconnect (default: true)
34
+ * @param {number} [options.reconnectDelay] - Delay ms before reconnect (default: 3000)
35
+ * @param {Object} [options.baileysOptions] - Extra options passed to makeWASocket
36
+ */
37
+ constructor(options = {}) {
38
+ super();
39
+ this.options = {
40
+ sessionPath: options.sessionPath ?? './f_session',
41
+ usePairingCode: options.usePairingCode ?? false,
42
+ phoneNumber: options.phoneNumber ?? null,
43
+ printQR: options.printQR ?? true,
44
+ logLevel: options.logLevel ?? 'silent',
45
+ autoReconnect: options.autoReconnect ?? true,
46
+ reconnectDelay: options.reconnectDelay ?? 3000,
47
+ baileysOptions: options.baileysOptions ?? {},
48
+ };
49
+
50
+ this.sock = null;
51
+ this.isConnected = false;
52
+ this.platform = detectPlatform();
53
+ this.logger = new FLogger(this.options.logLevel);
54
+ this._reconnectTimer = null;
55
+ }
56
+
57
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
58
+ // CONNECT
59
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
60
+ async connect() {
61
+ this.logger.banner();
62
+ this.logger.info(`Platform: ${this.platform.name}`);
63
+
64
+ // Ensure session dir exists
65
+ if (!fs.existsSync(this.options.sessionPath)) {
66
+ fs.mkdirSync(this.options.sessionPath, { recursive: true });
67
+ }
68
+
69
+ const { state, saveCreds } = await useMultiFileAuthState(this.options.sessionPath);
70
+ const { version, isLatest } = await fetchLatestBaileysVersion();
71
+ this.logger.info(`Baileys version: ${version.join('.')} ${isLatest ? '(latest)' : '(update available)'}`);
72
+
73
+ const pinoLogger = pino({ level: this.options.logLevel });
74
+
75
+ const socketConfig = {
76
+ version,
77
+ logger: pinoLogger,
78
+ printQRInTerminal: false, // We handle QR ourselves
79
+ auth: {
80
+ creds: state.creds,
81
+ keys: makeCacheableSignalKeyStore(state.keys, pinoLogger),
82
+ },
83
+ browser: ['f-baileys', 'Chrome', '120.0.0'],
84
+ connectTimeoutMs: 60_000,
85
+ defaultQueryTimeoutMs: 30_000,
86
+ keepAliveIntervalMs: 15_000,
87
+ retryRequestDelayMs: 500,
88
+ markOnlineOnConnect: true,
89
+ syncFullHistory: false,
90
+ ...this.options.baileysOptions,
91
+ };
92
+
93
+ this.sock = makeWASocket(socketConfig);
94
+
95
+ // โ”€โ”€ Handle pairing code โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
96
+ if (this.options.usePairingCode && !state.creds.registered) {
97
+ await FPairing.request(this.sock, this.options.phoneNumber, this.logger);
98
+ }
99
+
100
+ // โ”€โ”€ Connection updates โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
101
+ this.sock.ev.on('connection.update', async (update) => {
102
+ const { connection, lastDisconnect, qr, isNewLogin } = update;
103
+
104
+ if (qr && !this.options.usePairingCode) {
105
+ if (this.options.printQR) {
106
+ await FQR.print(qr, this.logger);
107
+ }
108
+ this.emit('qr', qr);
109
+ }
110
+
111
+ if (connection === 'open') {
112
+ this.isConnected = true;
113
+ clearTimeout(this._reconnectTimer);
114
+ this.logger.success('Connected to WhatsApp โœ“');
115
+ this.emit('open', this.sock);
116
+ }
117
+
118
+ if (connection === 'close') {
119
+ this.isConnected = false;
120
+ const code = lastDisconnect?.error?.output?.statusCode;
121
+ const reason = Object.keys(DisconnectReason).find(
122
+ (k) => DisconnectReason[k] === code
123
+ ) ?? 'Unknown';
124
+
125
+ this.logger.warn(`Disconnected โ€” reason: ${reason} (${code})`);
126
+ this.emit('close', { reason, code });
127
+
128
+ // Auto reconnect unless logged out
129
+ if (code !== DisconnectReason.loggedOut && this.options.autoReconnect) {
130
+ this.logger.info(`Reconnecting in ${this.options.reconnectDelay}ms...`);
131
+ this._reconnectTimer = setTimeout(() => this.connect(), this.options.reconnectDelay);
132
+ } else if (code === DisconnectReason.loggedOut) {
133
+ this.logger.error('Session logged out โ€” deleting session files');
134
+ this._clearSession();
135
+ this.emit('logout');
136
+ }
137
+ }
138
+ });
139
+
140
+ // โ”€โ”€ Save credentials โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
141
+ this.sock.ev.on('creds.update', saveCreds);
142
+
143
+ // โ”€โ”€ Messages โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
144
+ this.sock.ev.on('messages.upsert', async ({ messages, type }) => {
145
+ if (type !== 'notify') return;
146
+ for (const raw of messages) {
147
+ const msg = FSerializer.serialize(raw, this.sock);
148
+ this.emit('message', msg);
149
+ }
150
+ });
151
+
152
+ // โ”€โ”€ Group updates โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
153
+ this.sock.ev.on('group-participants.update', (update) => {
154
+ this.emit('group-update', update);
155
+ });
156
+
157
+ return this.sock;
158
+ }
159
+
160
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
161
+ // SEND HELPERS
162
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
163
+ /** Send a text message */
164
+ async sendText(jid, text, quoted = null) {
165
+ const content = { text };
166
+ const opts = quoted ? { quoted } : {};
167
+ return this.sock.sendMessage(jid, content, opts);
168
+ }
169
+
170
+ /** Send an image */
171
+ async sendImage(jid, source, caption = '', quoted = null) {
172
+ const image = typeof source === 'string' && fs.existsSync(source)
173
+ ? { image: fs.readFileSync(source) }
174
+ : { image: { url: source } };
175
+ return this.sock.sendMessage(jid, { ...image, caption }, quoted ? { quoted } : {});
176
+ }
177
+
178
+ /** Send a video */
179
+ async sendVideo(jid, source, caption = '', quoted = null) {
180
+ const video = typeof source === 'string' && fs.existsSync(source)
181
+ ? { video: fs.readFileSync(source) }
182
+ : { video: { url: source } };
183
+ return this.sock.sendMessage(jid, { ...video, caption }, quoted ? { quoted } : {});
184
+ }
185
+
186
+ /** Send audio */
187
+ async sendAudio(jid, source, ptt = false, quoted = null) {
188
+ const audio = typeof source === 'string' && fs.existsSync(source)
189
+ ? { audio: fs.readFileSync(source) }
190
+ : { audio: { url: source } };
191
+ return this.sock.sendMessage(jid, { ...audio, ptt, mimetype: 'audio/mpeg' }, quoted ? { quoted } : {});
192
+ }
193
+
194
+ /** Send a sticker */
195
+ async sendSticker(jid, source, quoted = null) {
196
+ const sticker = typeof source === 'string' && fs.existsSync(source)
197
+ ? { sticker: fs.readFileSync(source) }
198
+ : { sticker: { url: source } };
199
+ return this.sock.sendMessage(jid, sticker, quoted ? { quoted } : {});
200
+ }
201
+
202
+ /** Send a document */
203
+ async sendDocument(jid, source, filename, mimetype = 'application/octet-stream', quoted = null) {
204
+ const document = typeof source === 'string' && fs.existsSync(source)
205
+ ? { document: fs.readFileSync(source) }
206
+ : { document: { url: source } };
207
+ return this.sock.sendMessage(jid, { ...document, fileName: filename, mimetype }, quoted ? { quoted } : {});
208
+ }
209
+
210
+ /** Send a reaction */
211
+ async sendReaction(jid, key, emoji) {
212
+ return this.sock.sendMessage(jid, { react: { text: emoji, key } });
213
+ }
214
+
215
+ /** Send typing status */
216
+ async sendTyping(jid, duration = 2000) {
217
+ await this.sock.sendPresenceUpdate('composing', jid);
218
+ await new Promise((r) => setTimeout(r, duration));
219
+ await this.sock.sendPresenceUpdate('paused', jid);
220
+ }
221
+
222
+ /** Download media from a message */
223
+ async downloadMedia(message) {
224
+ return downloadMediaMessage(message, 'buffer', {}, {
225
+ logger: pino({ level: 'silent' }),
226
+ reuploadRequest: this.sock.updateMediaMessage,
227
+ });
228
+ }
229
+
230
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
231
+ // UTILITY
232
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
233
+ /** Format phone number to JID */
234
+ static toJid(number) {
235
+ return number.replace(/[^0-9]/g, '') + '@s.whatsapp.net';
236
+ }
237
+
238
+ /** Format group JID */
239
+ static toGroupJid(id) {
240
+ return id.includes('@g.us') ? id : id + '@g.us';
241
+ }
242
+
243
+ _clearSession() {
244
+ try {
245
+ fs.rmSync(this.options.sessionPath, { recursive: true, force: true });
246
+ } catch { /* ignore */ }
247
+ }
248
+ }
249
+
250
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
251
+ // EXPORTS
252
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
253
+ module.exports = FBaileys;
254
+ module.exports.FBaileys = FBaileys;
255
+ module.exports.FLogger = FLogger;
256
+ module.exports.FQR = FQR;
257
+ module.exports.FPairing = FPairing;
258
+ module.exports.FSerializer = FSerializer;
259
+ module.exports.detectPlatform = detectPlatform;
260
+
261
+ // Re-export useful baileys helpers
262
+ module.exports.proto = proto;
263
+ module.exports.getContentType = getContentType;
264
+ module.exports.DisconnectReason = DisconnectReason;
package/src/logger.js ADDED
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const c = {
4
+ reset: '\x1b[0m',
5
+ green: '\x1b[32m',
6
+ yellow: '\x1b[33m',
7
+ cyan: '\x1b[36m',
8
+ red: '\x1b[31m',
9
+ bold: '\x1b[1m',
10
+ magenta: '\x1b[35m',
11
+ grey: '\x1b[90m',
12
+ };
13
+
14
+ class FLogger {
15
+ constructor(level = 'info') {
16
+ this.level = level;
17
+ }
18
+
19
+ _ts() {
20
+ return c.grey + new Date().toLocaleTimeString() + c.reset;
21
+ }
22
+
23
+ banner() {
24
+ if (this.level === 'silent') return;
25
+ console.log(`
26
+ ${c.magenta}${c.bold} โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
27
+ โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ• โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ•šโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•
28
+ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
29
+ โ–ˆโ–ˆโ•”โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ• โ•šโ–ˆโ–ˆโ•”โ• โ•šโ•โ•โ•โ•โ–ˆโ–ˆโ•‘
30
+ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘
31
+ โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•โ•šโ•โ•โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ•โ•${c.reset}
32
+ ${c.cyan} v1.0.0 โ€” Fast & Stable WhatsApp Library${c.reset}
33
+ `);
34
+ }
35
+
36
+ info(msg) { if (this.level !== 'silent') console.log(`${this._ts()} ${c.cyan}[INFO]${c.reset} ${msg}`); }
37
+ success(msg) { if (this.level !== 'silent') console.log(`${this._ts()} ${c.green}[OK]${c.reset} ${msg}`); }
38
+ warn(msg) { if (!['silent', 'error'].includes(this.level)) console.log(`${this._ts()} ${c.yellow}[WARN]${c.reset} ${msg}`); }
39
+ error(msg) { if (this.level !== 'silent') console.log(`${this._ts()} ${c.red}[ERROR]${c.reset} ${msg}`); }
40
+ debug(msg) { if (this.level === 'debug') console.log(`${this._ts()} ${c.grey}[DEBUG]${c.reset} ${msg}`); }
41
+ }
42
+
43
+ module.exports = FLogger;
package/src/pairing.js ADDED
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * f-baileys โ€” Pairing Code Handler
5
+ * 100% reliable pairing code delivery
6
+ */
7
+
8
+ class FPairing {
9
+ /**
10
+ * Request a pairing code from WhatsApp
11
+ * @param {Object} sock - Baileys socket
12
+ * @param {string} phoneNumber - Phone number with country code (no + or spaces)
13
+ * @param {Object} logger - FLogger instance
14
+ */
15
+ static async request(sock, phoneNumber, logger) {
16
+ if (!phoneNumber) {
17
+ logger.error('Phone number required for pairing code!');
18
+ logger.info('Set option: { usePairingCode: true, phoneNumber: "628xxxxxxxxxx" }');
19
+ process.exit(1);
20
+ }
21
+
22
+ // Clean phone number
23
+ const cleaned = FPairing.cleanNumber(phoneNumber);
24
+
25
+ // Wait for socket to be ready
26
+ await FPairing._waitReady(sock);
27
+
28
+ try {
29
+ logger.info(`Requesting pairing code for: ${cleaned}`);
30
+
31
+ const code = await sock.requestPairingCode(cleaned);
32
+ const formatted = FPairing.formatCode(code);
33
+
34
+ FPairing._printCode(formatted, cleaned, logger);
35
+ return formatted;
36
+ } catch (err) {
37
+ logger.error(`Pairing code request failed: ${err.message}`);
38
+ logger.warn('Retrying in 3 seconds...');
39
+ await new Promise((r) => setTimeout(r, 3000));
40
+ return FPairing.request(sock, phoneNumber, logger);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Format raw pairing code into readable format (XXXX-XXXX)
46
+ */
47
+ static formatCode(code) {
48
+ const raw = code.replace(/[^A-Z0-9]/gi, '').toUpperCase();
49
+ if (raw.length === 8) {
50
+ return `${raw.slice(0, 4)}-${raw.slice(4, 8)}`;
51
+ }
52
+ return raw;
53
+ }
54
+
55
+ /**
56
+ * Clean phone number: remove spaces, dashes, +
57
+ */
58
+ static cleanNumber(number) {
59
+ return String(number).replace(/[^0-9]/g, '');
60
+ }
61
+
62
+ /**
63
+ * Wait for socket to initialize before requesting pairing
64
+ */
65
+ static _waitReady(sock, timeout = 10000) {
66
+ return new Promise((resolve) => {
67
+ // Give socket time to establish websocket connection
68
+ const t = setTimeout(resolve, 2000);
69
+ sock.ev.once('connection.update', () => {
70
+ clearTimeout(t);
71
+ resolve();
72
+ });
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Print pairing code to terminal in a styled box
78
+ */
79
+ static _printCode(code, phone, logger) {
80
+ const line = 'โ”€'.repeat(36);
81
+ console.log('');
82
+ console.log(`\x1b[36mโ”Œ${line}โ”\x1b[0m`);
83
+ console.log(`\x1b[36mโ”‚\x1b[0m \x1b[1m\x1b[33m๐Ÿ“ฑ WhatsApp Pairing Code\x1b[0m \x1b[36mโ”‚\x1b[0m`);
84
+ console.log(`\x1b[36mโ”œ${line}โ”ค\x1b[0m`);
85
+ console.log(`\x1b[36mโ”‚\x1b[0m Phone : \x1b[32m+${phone}\x1b[0m${' '.repeat(Math.max(0, 22 - phone.length))}\x1b[36mโ”‚\x1b[0m`);
86
+ console.log(`\x1b[36mโ”‚\x1b[0m Code : \x1b[1m\x1b[35m${code}\x1b[0m${' '.repeat(Math.max(0, 25 - code.length))}\x1b[36mโ”‚\x1b[0m`);
87
+ console.log(`\x1b[36mโ”œ${line}โ”ค\x1b[0m`);
88
+ console.log(`\x1b[36mโ”‚\x1b[0m \x1b[90mSteps: WA โ†’ Linked Devices โ†’ Link Device\x1b[0m \x1b[36mโ”‚\x1b[0m`);
89
+ console.log(`\x1b[36mโ”‚\x1b[0m \x1b[90mEnter code above โ€” expires in 60s\x1b[0m \x1b[36mโ”‚\x1b[0m`);
90
+ console.log(`\x1b[36mโ””${line}โ”˜\x1b[0m`);
91
+ console.log('');
92
+ }
93
+ }
94
+
95
+ module.exports = { FPairing };
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const fs = require('fs');
5
+
6
+ function detectPlatform() {
7
+ const p = {
8
+ isTermux: false,
9
+ isVPS: false,
10
+ isPterodactyl: false,
11
+ isWindows: false,
12
+ isMac: false,
13
+ isDocker: false,
14
+ name: 'Unknown',
15
+ arch: os.arch(),
16
+ nodeVersion: process.version,
17
+ };
18
+
19
+ if (os.platform() === 'win32') {
20
+ p.isWindows = true;
21
+ p.name = 'Windows';
22
+ return p;
23
+ }
24
+
25
+ if (os.platform() === 'darwin') {
26
+ p.isMac = true;
27
+ p.name = 'macOS';
28
+ return p;
29
+ }
30
+
31
+ // Termux
32
+ if (
33
+ process.env.TERMUX_VERSION ||
34
+ (process.env.PREFIX && process.env.PREFIX.includes('com.termux')) ||
35
+ fs.existsSync('/data/data/com.termux') ||
36
+ fs.existsSync('/data/data/com.termux.fdroid') ||
37
+ process.env.TERMUX_APP_PACKAGE_NAME
38
+ ) {
39
+ p.isTermux = true;
40
+ p.name = process.env.PREFIX?.includes('fdroid')
41
+ ? 'Termux (F-Droid)'
42
+ : 'Termux';
43
+ return p;
44
+ }
45
+
46
+ // Docker / Pterodactyl
47
+ if (fs.existsSync('/.dockerenv')) {
48
+ p.isDocker = true;
49
+ if (
50
+ process.env.P_SERVER_UUID ||
51
+ process.env.P_SERVER_LOCATION ||
52
+ process.env.PTERODACTYL ||
53
+ process.env.SERVER_MEMORY
54
+ ) {
55
+ p.isPterodactyl = true;
56
+ p.name = 'Pterodactyl Panel';
57
+ } else {
58
+ p.name = 'Docker';
59
+ }
60
+ return p;
61
+ }
62
+
63
+ // Default: VPS/Linux
64
+ p.isVPS = true;
65
+ p.name = `VPS/Linux (${os.release()})`;
66
+ return p;
67
+ }
68
+
69
+ module.exports = { detectPlatform };
package/src/qr.js ADDED
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * f-baileys โ€” QR Code Handler
5
+ * Prints QR to terminal and emits QR string
6
+ */
7
+
8
+ class FQR {
9
+ /**
10
+ * Print QR to terminal
11
+ * @param {string} qr - QR string from baileys
12
+ * @param {Object} logger - FLogger instance
13
+ */
14
+ static async print(qr, logger) {
15
+ try {
16
+ const qrTerminal = require('qrcode-terminal');
17
+ console.log('');
18
+ logger.info('Scan QR Code below with WhatsApp:');
19
+ logger.info('WA โ†’ Linked Devices โ†’ Link a Device โ†’ Scan QR');
20
+ console.log('');
21
+ qrTerminal.generate(qr, { small: true });
22
+ console.log('');
23
+ } catch {
24
+ logger.warn('qrcode-terminal not available โ€” QR string emitted as event only');
25
+ logger.info('Install: npm install qrcode-terminal');
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Convert QR string to image buffer (requires 'qrcode' package)
31
+ * @param {string} qr
32
+ * @returns {Promise<Buffer>}
33
+ */
34
+ static async toBuffer(qr) {
35
+ try {
36
+ const QRCode = require('qrcode');
37
+ return await QRCode.toBuffer(qr, {
38
+ type: 'png',
39
+ width: 512,
40
+ margin: 2,
41
+ color: { dark: '#128C7E', light: '#FFFFFF' },
42
+ });
43
+ } catch {
44
+ throw new Error('Package "qrcode" required: npm install qrcode');
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Convert QR string to base64 data URL
50
+ * @param {string} qr
51
+ * @returns {Promise<string>}
52
+ */
53
+ static async toDataURL(qr) {
54
+ try {
55
+ const QRCode = require('qrcode');
56
+ return await QRCode.toDataURL(qr, {
57
+ width: 512,
58
+ margin: 2,
59
+ color: { dark: '#128C7E', light: '#FFFFFF' },
60
+ });
61
+ } catch {
62
+ throw new Error('Package "qrcode" required: npm install qrcode');
63
+ }
64
+ }
65
+ }
66
+
67
+ module.exports = { FQR };
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * f-baileys โ€” Message Serializer
5
+ * Makes msg.text, msg.from, msg.isGroup, etc. easy to access
6
+ */
7
+
8
+ const { getContentType, proto } = require('@whiskeysockets/baileys');
9
+
10
+ class FSerializer {
11
+ /**
12
+ * Serialize a raw Baileys message into an easy-to-use object
13
+ * @param {Object} raw - Raw message from Baileys
14
+ * @param {Object} sock - Baileys socket instance
15
+ * @returns {Object} Serialized message
16
+ */
17
+ static serialize(raw, sock) {
18
+ const msg = { ...raw };
19
+
20
+ // โ”€โ”€ Basic info โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
21
+ msg.key = raw.key;
22
+ msg.id = raw.key.id;
23
+ msg.from = raw.key.remoteJid;
24
+ msg.fromMe = raw.key.fromMe;
25
+ msg.pushName = raw.pushName || '';
26
+ msg.isGroup = msg.from?.endsWith('@g.us') ?? false;
27
+ msg.sender = msg.isGroup
28
+ ? (raw.key.participant || raw.participant || '')
29
+ : (msg.fromMe ? (sock.user?.id ?? '') : msg.from);
30
+ msg.senderNum = msg.sender.replace(/@.+/g, '');
31
+ msg.timestamp = raw.messageTimestamp
32
+ ? (typeof raw.messageTimestamp === 'number'
33
+ ? raw.messageTimestamp
34
+ : raw.messageTimestamp.low ?? raw.messageTimestamp)
35
+ : Date.now() / 1000;
36
+
37
+ // โ”€โ”€ Content type โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
38
+ const content = raw.message;
39
+ msg.type = getContentType(content) || 'unknown';
40
+ msg.body = content?.conversation
41
+ || content?.extendedTextMessage?.text
42
+ || content?.imageMessage?.caption
43
+ || content?.videoMessage?.caption
44
+ || content?.documentMessage?.caption
45
+ || '';
46
+ msg.text = msg.body; // alias
47
+
48
+ // โ”€โ”€ Media flags โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
49
+ msg.isText = msg.type === 'conversation' || msg.type === 'extendedTextMessage';
50
+ msg.isImage = msg.type === 'imageMessage';
51
+ msg.isVideo = msg.type === 'videoMessage';
52
+ msg.isAudio = msg.type === 'audioMessage';
53
+ msg.isSticker = msg.type === 'stickerMessage';
54
+ msg.isDocument = msg.type === 'documentMessage';
55
+ msg.isContact = msg.type === 'contactMessage';
56
+ msg.isLocation = msg.type === 'locationMessage';
57
+ msg.isPoll = msg.type === 'pollCreationMessage';
58
+
59
+ // โ”€โ”€ Quoted message โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
60
+ const quoted = content?.extendedTextMessage?.contextInfo?.quotedMessage;
61
+ if (quoted) {
62
+ const qType = getContentType(quoted) || 'unknown';
63
+ msg.quoted = {
64
+ type: qType,
65
+ message: quoted,
66
+ body: quoted?.conversation
67
+ || quoted?.extendedTextMessage?.text
68
+ || quoted?.imageMessage?.caption
69
+ || quoted?.videoMessage?.caption
70
+ || '',
71
+ key: {
72
+ id: content.extendedTextMessage.contextInfo.stanzaId,
73
+ remoteJid: msg.from,
74
+ fromMe: content.extendedTextMessage.contextInfo.participant === sock.user?.id,
75
+ participant: content.extendedTextMessage.contextInfo.participant,
76
+ },
77
+ };
78
+ } else {
79
+ msg.quoted = null;
80
+ }
81
+
82
+ // โ”€โ”€ Mentions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
83
+ msg.mentions = content?.extendedTextMessage?.contextInfo?.mentionedJid ?? [];
84
+
85
+ // โ”€โ”€ Reply helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
86
+ msg.reply = (text) => sock.sendMessage(msg.from, { text }, { quoted: raw });
87
+
88
+ // โ”€โ”€ React helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
89
+ msg.react = (emoji) => sock.sendMessage(msg.from, { react: { text: emoji, key: raw.key } });
90
+
91
+ return msg;
92
+ }
93
+ }
94
+
95
+ module.exports = FSerializer;