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 +21 -0
- package/README.md +248 -0
- package/examples/pairing-login.js +38 -0
- package/examples/qr-login.js +53 -0
- package/package.json +58 -0
- package/scripts/auto-install.js +195 -0
- package/src/index.d.ts +83 -0
- package/src/index.js +264 -0
- package/src/logger.js +43 -0
- package/src/pairing.js +95 -0
- package/src/platform.js +69 -0
- package/src/qr.js +67 -0
- package/src/serializer.js +95 -0
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
|
+
[](https://npmjs.com/package/f-baileys)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](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 };
|
package/src/platform.js
ADDED
|
@@ -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;
|