@isaxn/bailyes 1.0.0 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,33 +1,281 @@
1
- # @isan/bailyes
1
+ # @isaxn/bailyes
2
2
 
3
- 🚀 **Modern WhatsApp Bot Framework** built on top of
4
- [`@whiskeysockets/baileys`](https://github.com/WhiskeySockets/Baileys)
3
+ Framework WhatsApp bot berbasis [`@whiskeysockets/baileys`](https://github.com/WhiskeySockets/Baileys), dengan:
5
4
 
6
- `@isan/bailyes` menyediakan **engine WhatsApp bot yang ringan, stabil, dan mudah dipakai**, cocok untuk:
7
- - Bot pribadi
8
- - Bot grup
9
- - Framework bot lanjutan
10
- - Project npm / open-source
5
+ - Login QR **dan** pairing code
6
+ - Auto-reconnect yang tidak kehilangan event setelah socket diganti
7
+ - `Store` bawaan (kontak, chat, pesan, metadata grup) — pengganti `makeInMemoryStore` yang sudah dihapus dari Baileys
8
+ - Message builder modular: `Button`, `ButtonV2`, `Carousel`, `AIRich`
9
+ - 100% bisa dipakai lewat `require(...)`, tidak perlu ESM
11
10
 
12
- ---
11
+ ## Instalasi
13
12
 
14
- ## ✨ Features
13
+ ```bash
14
+ npm install @isaxn/bailyes
15
+ ```
16
+
17
+ Paket ini memuat `@whiskeysockets/baileys` secara dinamis di dalam (Baileys versi terbaru murni ESM), jadi kamu tetap bisa `require("@isaxn/bailyes")` seperti biasa walaupun dependensinya ESM.
18
+
19
+ ```js
20
+ const button = require("@isaxn/bailyes");
21
+ const { Bailyes, ButtonV2, Store } = button;
22
+ ```
23
+
24
+
25
+ ## Quick start — bot sederhana
26
+
27
+ ```js
28
+ const { Bailyes } = require("@isaxn/bailyes");
29
+
30
+ const bot = new Bailyes({
31
+ auth: "auth",
32
+ prefix: ["!", "."]
33
+ });
34
+
35
+ bot.command("ping", async (ctx) => {
36
+ await ctx.reply("pong");
37
+ });
38
+
39
+ bot.onFramework("ready", () => console.log("Bot connected"));
40
+
41
+ bot.start();
42
+ ```
43
+
44
+ Menjalankan `node bot.js` akan menampilkan QR code di terminal secara default.
45
+
46
+ ## Login dengan pairing code
47
+
48
+ ```js
49
+ const bot = new Bailyes({
50
+ auth: "auth",
51
+ pairingCode: true,
52
+ phoneNumber: "6281234567890"
53
+ });
54
+
55
+ bot.onFramework("pairing-code", (code) => {
56
+ console.log("Masukkan kode ini di WhatsApp:", code);
57
+ });
58
+
59
+ bot.start();
60
+ ```
61
+
62
+ `phoneNumber` wajib diisi format internasional tanpa `+` atau spasi (`62...`). Kalau `pairingCode: true` tapi sesi sudah pernah login sebelumnya (`creds.registered`), kode tidak akan diminta lagi.
63
+
64
+ ## Reconnect
65
+
66
+ `WAClient` menyimpan status koneksi lewat `EventEmitter` sendiri (`open`, `close`, `qr`, `pairing-code`, `logged-out`, `error`), bukan langsung dari `sock.ev`. Jadi walaupun socket internal diganti saat reconnect, listener command/hear kamu **tidak hilang** — beda dari versi sebelumnya yang mendaftarkan `messages.upsert` langsung ke `sock.ev` sekali saja di awal.
67
+
68
+ Reconnect otomatis dengan backoff (`reconnectDelay * attempt`, dibatasi `maxReconnectDelay`), berhenti otomatis kalau `DisconnectReason` adalah `loggedOut`.
69
+
70
+ ```js
71
+ const bot = new Bailyes({
72
+ reconnectDelay: 3000,
73
+ maxReconnectDelay: 30000,
74
+ maxReconnectAttempts: 0 // 0 = tidak dibatasi
75
+ });
76
+ ```
77
+
78
+ ## Store — memory kontak, chat, dan pesan
79
+
80
+ Baileys versi baru tidak lagi menyediakan `makeInMemoryStore` bawaan, jadi harus dibuat manual. `Store` di paket ini otomatis mendengarkan event `contacts.*`, `chats.*`, `messages.*`, `groups.*`, `group-participants.update`.
81
+
82
+ ```js
83
+ const { Store } = require("@isaxn/bailyes");
84
+
85
+ const store = new Store({
86
+ file: "store.json", // opsional: simpan/baca otomatis dari file
87
+ autosaveInterval: 15000, // interval autosave (ms)
88
+ maxMessagesPerChat: 200 // batas pesan yang disimpan per chat
89
+ });
90
+ ```
91
+
92
+ Dipakai lewat opsi `store` saat membuat `Bailyes`/`WAClient` (otomatis dibind ke `sock.ev`):
93
+
94
+ ```js
95
+ const bot = new Bailyes({
96
+ store: { file: "store.json", autosaveInterval: 15000 }
97
+ });
98
+ ```
99
+
100
+ Atau matikan store sama sekali dengan `store: false`.
101
+
102
+ API `Store`:
103
+
104
+ - `store.getName(jid)` — nama kontak (fallback ke nomor)
105
+ - `store.getContact(jid)`, `store.getChat(jid)`, `store.getGroupMetadata(jid)`
106
+ - `store.loadMessage(jid, id)` — ambil pesan lama dari memory (berguna untuk quoted/anti-delete)
107
+ - `store.writeToFile(path)` / `store.readFromFile(path)` — simpan/baca manual
108
+ - `store.toJSON()` / `store.fromJSON(data)`
109
+
110
+ Di dalam `ctx` (Context pesan masuk), tinggal panggil `ctx.getName()` — otomatis pakai store yang sama.
111
+
112
+ ## Message builder
113
+
114
+ ### ButtonV2 (buttonsMessage)
115
+
116
+ ```js
117
+ const { ButtonV2 } = require("@isaxn/bailyes");
118
+
119
+ const msg = new ButtonV2(sock)
120
+ .setBody("Pilih salah satu")
121
+ .setFooter("Bailyes Bot")
122
+ .addButton("Menu 1", "menu1")
123
+ .addButton("Menu 2", "menu2");
124
+
125
+ await msg.send(jid, { quoted: ctx.msg });
126
+ ```
127
+
128
+ `ButtonV2` **wajib** minimal satu `.addButton()` sebelum `.send()` — kalau tidak, akan langsung melempar `Error`, bukan diam-diam fallback jadi teks biasa seperti sebelumnya.
15
129
 
16
- - Built on **Baileys (Multi-Device)**
17
- - 🔐 **QR Login only** (aman & stabil)
18
- - ⚡ Fast & lightweight
19
- - 🧩 Easy to integrate into any bot
20
- - 📦 Ready for **ESM & CommonJS**
21
- - 🔁 Auto reconnect
22
- - 🧠 Clean & minimal API
23
- - 🚫 No forced pairing / aggressive login
130
+ ### Button (interactiveMessage / native flow list)
24
131
 
25
- ---
132
+ ```js
133
+ const { Button } = require("@isaxn/bailyes");
26
134
 
27
- support in my channel:
28
- https://whatsapp.com/channel/0029Vb7M8VA05MUkehrqcl3J
135
+ const msg = new Button(sock)
136
+ .setTitle("Judul")
137
+ .setBody("Isi pesan")
138
+ .addSelection("Pilih menu")
139
+ .makeSection("Kategori A")
140
+ .makeRow("", "Item 1", "Deskripsi item 1", "item1")
141
+ .makeRow("", "Item 2", "Deskripsi item 2", "item2");
29
142
 
30
- ## 📦 Installation
143
+ await msg.send(jid);
144
+ ```
145
+
146
+ ### Carousel
147
+
148
+ ```js
149
+ const { Carousel, Button } = require("@isaxn/bailyes");
150
+
151
+ const card1 = await new Button(sock).setImage("https://...").setTitle("Kartu 1").toCard();
152
+ const card2 = await new Button(sock).setImage("https://...").setTitle("Kartu 2").toCard();
153
+
154
+ const carousel = new Carousel(sock).setBody("Lihat pilihan berikut").addCard([card1, card2]);
155
+
156
+ await carousel.send(jid);
157
+ ```
158
+
159
+ ### AIRich (rich response ala AI)
160
+
161
+ ```js
162
+ const { AIRich } = require("@isaxn/bailyes");
163
+
164
+ const rich = new AIRich(sock)
165
+ .setTitle("Asisten")
166
+ .addText("Ini contoh **teks** dengan [tautan](https://example.com)")
167
+ .addCode("javascript", "console.log('halo')")
168
+ .addTip("Ketik !menu untuk melihat semua perintah");
169
+
170
+ await rich.send(jid);
171
+ ```
172
+
173
+ ### Toolkit
174
+
175
+ Kumpulan util murni tanpa perlu instance socket kecuali fungsi yang upload media (`toUrl`, `resolveMedia`):
176
+
177
+ ```js
178
+ const { Toolkit } = require("@isaxn/bailyes");
179
+
180
+ const buffer = await Toolkit.fetchBuffer("https://example.com/gambar.jpg");
181
+ const resized = await Toolkit.resize(buffer, 300, 300);
182
+ ```
183
+
184
+ ## v2.1.0 — cara panggil baru: `bx`
185
+
186
+ Selain builder chain (`new ButtonV2(sock).setBody(...).addButton(...)`), sekarang ada `bx` — satu fungsi sekali panggil, tanpa chaining, untuk kasus-kasus umum:
187
+
188
+ ```js
189
+ const { bx } = require("@isaxn/bailyes");
190
+
191
+ await bx.button(sock, jid, {
192
+ body: "Pilih menu",
193
+ footer: "Bailyes Bot",
194
+ buttons: [
195
+ { text: "Menu 1", id: "menu1" },
196
+ { text: "Menu 2", id: "menu2" }
197
+ ]
198
+ });
199
+
200
+ await bx.list(sock, jid, {
201
+ title: "Daftar Produk",
202
+ sections: [
203
+ { title: "Kategori A", rows: [{ title: "Item 1", description: "Deskripsi", id: "item1" }] }
204
+ ]
205
+ });
206
+
207
+ await bx.card(sock, jid, {
208
+ body: "Lihat produk kami",
209
+ cards: [
210
+ { title: "Produk 1", image: "https://...", buttons: [{ type: "url", text: "Beli", value: "https://..." }] },
211
+ { title: "Produk 2", image: "https://...", buttons: [{ type: "reply", text: "Tanya", value: "tanya1" }] }
212
+ ]
213
+ });
214
+
215
+ await bx.rich(sock, jid, { title: "Asisten", text: "Halo!", tip: "Ketik !menu" });
216
+
217
+ await bx.linkPreview(sock, jid, { text: "Cek ini", link: "https://example.com", title: "Judul", description: "Deskripsi" });
218
+ ```
219
+
220
+ ### `bx.livePhoto` — kirim foto + video terpasang jadi satu (Live Photo)
221
+
222
+ Fitur baru: kirim gambar yang otomatis "hidup" jadi video singkat saat ditekan, mirip Live Photo di iPhone. Ini memakai `messageAssociation` bawaan WhatsApp (video di-relay terpisah tapi terikat ke `parentMessageKey` pesan gambar).
223
+
224
+ ```js
225
+ await bx.livePhoto(sock, jid, {
226
+ image: "https://example.com/foto.jpg", // url, path lokal, atau Buffer
227
+ video: "https://example.com/klip.mp4" // url, path lokal, atau Buffer
228
+ });
229
+ ```
230
+
231
+ ### `bx.liveThumbnail` — animasi thumbnail link preview
232
+
233
+ Edit pesan berulang kali dengan thumbnail link-preview yang berganti-ganti, jadi efek "gambar bergerak" di dalam satu bubble teks/link.
234
+
235
+ ```js
236
+ const { key } = await sock.sendMessage(jid, { text: "Menyiapkan..." });
237
+
238
+ await bx.liveThumbnail(sock, jid, {
239
+ key,
240
+ text: "Cek promo ini",
241
+ link: "https://example.com/promo",
242
+ title: "Promo",
243
+ description: "Terbatas hari ini",
244
+ images: ["https://.../1.jpg", "https://.../2.jpg", "https://.../3.jpg"],
245
+ interval: 1500,
246
+ loops: 2
247
+ });
248
+ ```
249
+
250
+ ## Perbaikan dari versi sebelumnya
251
+
252
+ - **`require()` sekarang benar-benar berfungsi** tanpa perlu build step terpisah (`dist/`), karena Baileys (ESM) di-load lewat `import()` dinamis yang di-cache, dipanggil dari dalam fungsi `async` — bukan `import` statis yang bikin `require()` gagal.
253
+ - **Pairing code** ditambahkan (`pairingCode: true` + `phoneNumber`), sebelumnya hanya QR.
254
+ - **Reconnect tidak lagi memutus listener command** — sebelumnya `sock.ev.on("messages.upsert", ...)` didaftarkan sekali di `start()`, jadi setelah reconnect (socket baru), listener lama menunjuk ke socket basi. Sekarang event diteruskan lewat `WAClient` (EventEmitter sendiri) yang stabil lintas reconnect.
255
+ - **`ButtonV2` tanpa `.addButton()` sekarang melempar error jelas**, bukan diam-diam fallback jadi pesan teks biasa.
256
+ - **Store/memory kontak dibuat manual** (`src/core/store.js`) karena Baileys tidak lagi menyediakan `makeInMemoryStore` bawaan.
257
+ - **`@lid` dan JID lain yang gagal diambil foto profilnya** tidak lagi melempar exception — `ctx.getProfilePicture()` mengembalikan `null` dengan aman lewat try/catch.
258
+ - **`@whiskeysockets/baileys` dinaikkan ke `^6.7.23`** (versi `6.7.9` yang dipakai sebelumnya kena advisory keamanan spoofing pesan).
259
+ - File tunggal 2600+ baris (`build-message.js`) dipecah jadi modul-modul kecil per tanggung jawab (`toolkit`, `base-builder`, `button`, `button-v2`, `carousel`, `ai-rich`, `tokenizer`, `table`, `inline-entities`, `native-flow`, `link-preview`) supaya gampang di-maintain dan tidak saling tabrak saat diedit.
260
+
261
+ ## Saran update selanjutnya
262
+
263
+ - **Plugin loader otomatis** — folder `plugins/` di-scan, tiap file otomatis jadi command (mirip pola yang sudah kamu pakai di bot lamamu), supaya `bot.command()` tidak ditulis manual satu-satu.
264
+ - **Anti-delete berbasis `Store`** — karena `store.loadMessage(jid, id)` sudah ada, tinggal dengarkan event `messages.delete` dan kirim ulang isi pesan lama sebelum dihapus.
265
+ - **Rate limiter per-command** — `cooldown` sekarang masih per-chat global; bisa dibuat per-command per-user biar lebih presisi (misal command berat dibatasi lebih ketat dari command ringan).
266
+ - **Poll message builder** — builder khusus untuk `pollCreationMessage`, senada gaya `bx` (`bx.poll(sock, jid, { question, options })`).
267
+ - **Session multi-akun** — satu proses Node bisa jalankan banyak `Bailyes` instance sekaligus (multi-nomor), tinggal kasih `auth` folder beda-beda; bisa dibungkus jadi `SessionManager` biar gampang.
268
+ - **Backend `Store` opsional ke SQLite** — saat ini `Store` murni in-memory + file JSON; untuk bot dengan banyak chat/pesan, opsi simpan ke SQLite (`better-sqlite3`) bakal lebih tahan crash dan tidak makan RAM.
269
+ - **Webhook/HTTP bridge** — endpoint HTTP kecil biar bot bisa dikontrol dari luar (kirim pesan lewat API, cek status koneksi, dsb), cocok buat kamu yang juga suka bikin REST API (NexRay API).
270
+
271
+ ## Testing
31
272
 
32
273
  ```bash
33
- npm install @isan/bailyes
274
+ npm test
275
+ ```
276
+
277
+ `test/basic.test.js` menguji Handler, Store, Toolkit, dan proses `build()` dari `ButtonV2`/`AIRich` tanpa perlu koneksi WhatsApp asli.
278
+
279
+ ## Lisensi
280
+
281
+ MIT — lihat `LICENSE`.
package/package.json CHANGED
@@ -1,33 +1,45 @@
1
1
  {
2
2
  "name": "@isaxn/bailyes",
3
- "version": "1.0.0",
4
- "description": "WhatsApp Bot Framework built on Baileys",
5
- "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./src/index.js",
8
- "exports": {
9
- "import": "./src/index.js",
10
- "require": "./dist/index.cjs"
3
+ "version": "2.1.3",
4
+ "description": "WhatsApp Bot Framework built on Baileys, with QR and pairing code login, a built-in contact/chat/message store, and a modular interactive message builder (Button, ButtonV2, Carousel, AIRich).",
5
+ "main": "./src/index.js",
6
+ "exports": "./src/index.js",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "scripts": {
11
+ "test": "node test/basic.test.js"
11
12
  },
12
13
  "files": [
13
14
  "src",
14
- "dist",
15
15
  "README.md",
16
16
  "LICENSE"
17
17
  ],
18
18
  "author": "isan",
19
19
  "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/USERNAME/bailyes.git"
23
+ },
24
+ "homepage": "https://github.com/USERNAME/bailyes#readme",
25
+ "bugs": {
26
+ "url": "https://github.com/USERNAME/bailyes/issues"
27
+ },
20
28
  "keywords": [
21
29
  "whatsapp",
22
30
  "bot",
23
31
  "baileys",
24
32
  "whatsapp-bot",
25
- "framework"
33
+ "framework",
34
+ "pairing-code",
35
+ "store"
26
36
  ],
27
37
  "dependencies": {
28
- "@whiskeysockets/baileys": "^6.7.0",
38
+ "@whiskeysockets/baileys": "^6.7.23",
29
39
  "pino": "^9.0.0",
30
- "qrcode-terminal": "^0.12.0"
40
+ "qrcode-terminal": "^0.12.0",
41
+ "sharp": "^0.33.0",
42
+ "fluent-ffmpeg": "^2.1.2"
31
43
  },
32
44
  "publishConfig": {
33
45
  "access": "public"
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ let pending = null;
4
+
5
+ function getBaileys() {
6
+ if (!pending) {
7
+ pending = import("@whiskeysockets/baileys").then((mod) => {
8
+ return mod && mod.default && !mod.makeWASocket
9
+ ? Object.assign({ makeWASocket: mod.default }, mod)
10
+ : mod;
11
+ });
12
+ }
13
+ return pending;
14
+ }
15
+
16
+ module.exports = { getBaileys };
@@ -1,55 +1,175 @@
1
- //isanamd code dont delete
2
- import makeWASocket, {
3
- useMultiFileAuthState,
4
- DisconnectReason,
5
- fetchLatestBaileysVersion
6
- } from "@whiskeysockets/baileys";
1
+ "use strict";
7
2
 
8
- import qrcode from "qrcode-terminal";
9
- import pino from "pino";
3
+ const { EventEmitter } = require("events");
4
+ const pino = require("pino");
5
+ const qrcode = require("qrcode-terminal");
6
+ const { getBaileys } = require("./baileys-loader.js");
7
+ const { Store } = require("./store.js");
10
8
 
11
- export class WAClient {
12
- async connect(authFolder = "./auth") {
13
- const { state, saveCreds } =
14
- await useMultiFileAuthState(authFolder);
9
+ const PASSTHROUGH_EVENTS = [
10
+ "messages.upsert",
11
+ "messages.update",
12
+ "messages.delete",
13
+ "messages.reaction",
14
+ "message-receipt.update",
15
+ "chats.upsert",
16
+ "chats.update",
17
+ "chats.delete",
18
+ "contacts.upsert",
19
+ "contacts.update",
20
+ "groups.upsert",
21
+ "groups.update",
22
+ "group-participants.update",
23
+ "presence.update",
24
+ "call",
25
+ "blocklist.set",
26
+ "blocklist.update"
27
+ ];
15
28
 
16
- const { version } =
17
- await fetchLatestBaileysVersion();
29
+ class WAClient extends EventEmitter {
30
+ constructor(options = {}) {
31
+ super();
32
+
33
+ this.authFolder = options.auth || "auth";
34
+ this.usePairingCode = !!options.pairingCode;
35
+ this.phoneNumber = options.phoneNumber || null;
36
+ this.browser = options.browser || ["Bailyes", "Chrome", "2.0.0"];
37
+ this.logger = options.logger || pino({ level: "silent" });
38
+ this.reconnectDelay = options.reconnectDelay ?? 3000;
39
+ this.maxReconnectDelay = options.maxReconnectDelay ?? 30000;
40
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? 0;
41
+ this.socketOptions = options.socketOptions || {};
42
+
43
+ this.store =
44
+ options.store === false
45
+ ? null
46
+ : options.store instanceof Store
47
+ ? options.store
48
+ : new Store(typeof options.store === "object" ? options.store : {});
49
+
50
+ this.sock = null;
51
+ this._reconnectAttempts = 0;
52
+ this._stopped = false;
53
+ this._pairingRequested = false;
54
+ }
55
+
56
+ async connect() {
57
+ this._stopped = false;
58
+
59
+ const baileys = await getBaileys();
60
+ const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion } = baileys;
61
+
62
+ const { state, saveCreds } = await useMultiFileAuthState(this.authFolder);
63
+ const { version } = await fetchLatestBaileysVersion();
18
64
 
19
65
  const sock = makeWASocket({
20
66
  version,
21
67
  auth: state,
22
68
  printQRInTerminal: false,
23
- logger: pino({ level: "silent" }),
24
- browser: ["Bailyes", "Chrome", "1.0.0"]
69
+ logger: this.logger,
70
+ browser: this.usePairingCode ? ["Ubuntu", "Chrome", "22.04.4"] : this.browser,
71
+ syncFullHistory: false,
72
+ ...this.socketOptions
25
73
  });
26
74
 
75
+ this.sock = sock;
76
+ this._pairingRequested = false;
77
+
78
+ if (this.store) {
79
+ this.store.bind(sock.ev);
80
+ }
81
+
27
82
  sock.ev.on("creds.update", saveCreds);
83
+ sock.ev.on("connection.update", (update) => this._handleConnectionUpdate(update, baileys));
28
84
 
29
- sock.ev.on("connection.update", (update) => {
30
- const { connection, lastDisconnect, qr } = update;
85
+ this._bindPassthroughEvents(sock);
31
86
 
32
- if (qr) {
33
- console.clear();
34
- qrcode.generate(qr, { small: true });
35
- }
87
+ if (this.usePairingCode && !sock.authState.creds.registered) {
88
+ await this._requestPairingCode(sock);
89
+ }
90
+
91
+ return sock;
92
+ }
93
+
94
+ async _requestPairingCode(sock) {
95
+ if (this._pairingRequested) return;
96
+ this._pairingRequested = true;
36
97
 
37
- if (connection === "open") {
38
- console.log(" WhatsApp Connected");
98
+ if (!this.phoneNumber) {
99
+ throw new Error("phoneNumber is required when pairingCode is enabled");
100
+ }
101
+
102
+ await new Promise((resolve) => setTimeout(resolve, 3000));
103
+
104
+ const cleanNumber = String(this.phoneNumber).replace(/[^0-9]/g, "");
105
+ const code = await sock.requestPairingCode(cleanNumber);
106
+
107
+ this.emit("pairing-code", code);
108
+ }
109
+
110
+ _handleConnectionUpdate(update, baileys) {
111
+ const { connection, lastDisconnect, qr } = update;
112
+
113
+ if (qr && !this.usePairingCode) {
114
+ qrcode.generate(qr, { small: true });
115
+ this.emit("qr", qr);
116
+ }
117
+
118
+ if (connection === "connecting") {
119
+ this.emit("connecting");
120
+ }
121
+
122
+ if (connection === "open") {
123
+ this._reconnectAttempts = 0;
124
+ this.emit("open", this.sock);
125
+ }
126
+
127
+ if (connection === "close") {
128
+ const statusCode = lastDisconnect?.error?.output?.statusCode;
129
+ const reason = this._resolveDisconnectReason(statusCode, baileys);
130
+
131
+ this.emit("close", { statusCode, reason, error: lastDisconnect?.error });
132
+
133
+ if (reason === "loggedOut") {
134
+ this._stopped = true;
135
+ this.emit("logged-out");
136
+ return;
39
137
  }
40
138
 
41
- if (connection === "close") {
42
- const code =
43
- lastDisconnect?.error?.output?.statusCode;
139
+ if (this._stopped) return;
44
140
 
45
- if (code !== DisconnectReason.loggedOut) {
46
- setTimeout(() => {
47
- this.connect(authFolder);
48
- }, 3000);
49
- }
141
+ if (this.maxReconnectAttempts && this._reconnectAttempts >= this.maxReconnectAttempts) {
142
+ this.emit("reconnect-failed");
143
+ return;
50
144
  }
51
- });
52
145
 
53
- return sock;
146
+ this._reconnectAttempts++;
147
+ const delay = Math.min(this.reconnectDelay * this._reconnectAttempts, this.maxReconnectDelay);
148
+
149
+ setTimeout(() => {
150
+ this.connect().catch((error) => this.emit("error", error));
151
+ }, delay);
152
+ }
153
+ }
154
+
155
+ _resolveDisconnectReason(statusCode, baileys) {
156
+ const { DisconnectReason } = baileys;
157
+ const entries = Object.entries(DisconnectReason || {});
158
+ const found = entries.find(([, value]) => value === statusCode);
159
+ return found ? found[0] : "unknown";
160
+ }
161
+
162
+ _bindPassthroughEvents(sock) {
163
+ for (const event of PASSTHROUGH_EVENTS) {
164
+ sock.ev.on(event, (data) => this.emit(event, data));
165
+ }
166
+ }
167
+
168
+ stop() {
169
+ this._stopped = true;
170
+ if (this.store) this.store.stop();
171
+ this.sock?.end?.(undefined);
54
172
  }
55
- }
173
+ }
174
+
175
+ module.exports = { WAClient };