lyxe-baileys 1.0.2 → 2.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/README.md +167 -1
- package/package.json +1 -1
- package/src/Lyxe/ai-stream.js +72 -0
- package/src/Lyxe/dashboard.js +301 -0
- package/src/Lyxe/group-bypass.js +87 -0
- package/src/Lyxe/humanizer.js +45 -0
- package/src/Lyxe/index.d.ts +85 -0
- package/src/Lyxe/index.js +118 -0
- package/src/Lyxe/media-tools.js +67 -0
- package/src/Lyxe/radar.js +95 -0
- package/src/Lyxe/session-manager.js +111 -0
- package/src/Lyxe/status-tools.js +75 -0
- package/src/Lyxe/telegram-bridge.js +188 -0
- package/src/Lyxe/vault.js +138 -0
- package/src/Lyxe/video-note.js +21 -0
- package/src/Lyxe/view-once.js +153 -0
- package/src/Socket/Client/proxy-agent.js +188 -0
- package/src/Socket/Client/websocket.js +60 -53
- package/src/Socket/index.js +4 -2
- package/src/index.d.ts +23 -2
- package/src/index.js +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="https://
|
|
3
|
+
<img src="https://i.ibb.co.com/fz44Z9Nz/images.jpg" width="400" alt="Lyxe Baileys Banner" />
|
|
4
4
|
<br><br>
|
|
5
5
|
|
|
6
6
|
<h1>lyxe-baileys</h1>
|
|
@@ -113,6 +113,172 @@ Baru di sini? Ini seluruh library dalam satu pandangan. Tiap baris menaut ke bag
|
|
|
113
113
|
|
|
114
114
|
---
|
|
115
115
|
|
|
116
|
+
## 🚀 Fitur Eksklusif Lyxe Baileys
|
|
117
|
+
|
|
118
|
+
### 1. 🛡️ Smart Anti-Ban & Humanizer
|
|
119
|
+
Hindari pemblokiran akun dengan jeda acak manusiawi dan simulasi presence:
|
|
120
|
+
```javascript
|
|
121
|
+
import { smartDelay } from 'lyxe-baileys'
|
|
122
|
+
|
|
123
|
+
// Jeda acak (misal 1000 - 2500ms)
|
|
124
|
+
await sock.smartDelay(1000, 2500)
|
|
125
|
+
|
|
126
|
+
// Simulasi mengetik otomatis sebelum pesan terkirim
|
|
127
|
+
await sock.simulateTyping(jid, 2000)
|
|
128
|
+
|
|
129
|
+
// Atau langsung kirim dengan opsi simulateTyping
|
|
130
|
+
await sock.sendMessage(jid, { text: 'Halo! Ada yang bisa dibantu?' }, { simulateTyping: true })
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 2. 👁️ View-Once & Anti-Delete Retriever
|
|
134
|
+
Bongkar media pesan 1x lihat (foto/video/audio) atau pulihkan pesan yang ditarik:
|
|
135
|
+
```javascript
|
|
136
|
+
// Download pesan View Once secara instan
|
|
137
|
+
const { buffer, mediaType, caption } = await sock.downloadViewOnce(m)
|
|
138
|
+
console.log(`Media ${mediaType} berhasil diunduh! Ukuran: ${buffer.length} bytes`)
|
|
139
|
+
|
|
140
|
+
// Tangkap pesan yang dihapus pengirim dari store
|
|
141
|
+
const original = sock.extractRevokedMessage(store, revocationMsg)
|
|
142
|
+
if (original) {
|
|
143
|
+
console.log('Pesan yang ditarik:', original.message)
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 3. 📢 WhatsApp Status / Story Supercharger
|
|
148
|
+
Otomatis baca story, beri reaksi emoji, buat status mention, atau unggah voice note ke story:
|
|
149
|
+
```javascript
|
|
150
|
+
// Otomatis tandai status sudah dibaca dengan jeda alami
|
|
151
|
+
await sock.readStatus(statusMessage, 1500)
|
|
152
|
+
|
|
153
|
+
// Beri reaksi emoji ke story kontak
|
|
154
|
+
await sock.reactStatus(statusMessage, '❤️')
|
|
155
|
+
|
|
156
|
+
// Buat status teks/media dengan mention kontak tertentu
|
|
157
|
+
await sock.sendStatusMention('Halo semua!', ['628123456789@s.whatsapp.net'])
|
|
158
|
+
|
|
159
|
+
// Unggah Voice Note (VN) ke Status WhatsApp
|
|
160
|
+
await sock.sendVoiceStatus(audioBuffer, { backgroundColor: '#7F5AF0' })
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 4. 🎙️ Media & Audio Helpers
|
|
164
|
+
Kirim Voice Note (PTT) asli dengan animasi waveform, stiker, dan media HD:
|
|
165
|
+
```javascript
|
|
166
|
+
// Kirim rekaman suara (Voice Note PTT) lengkap dengan waveform
|
|
167
|
+
await sock.sendVoiceNote(jid, './suara.mp3', quoted)
|
|
168
|
+
|
|
169
|
+
// Kirim stiker instan
|
|
170
|
+
await sock.sendImageAsSticker(jid, stickerBuffer, quoted)
|
|
171
|
+
|
|
172
|
+
// Kirim foto/video kualitas HD resmi
|
|
173
|
+
await sock.sendHDMedia(jid, { image: { url: './foto.jpg' }, caption: 'Kualitas HD' })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 5. 🤖 AI Streaming Message
|
|
177
|
+
Tampilkan pesan bertahap dengan animasi ketik langsung seperti ChatGPT/Gemini di WhatsApp:
|
|
178
|
+
```javascript
|
|
179
|
+
// Mengalirkan teks bertahap (AsyncIterable, generator, array, atau string)
|
|
180
|
+
await sock.streamMessage(jid, aiTextStream, {
|
|
181
|
+
throttleMs: 350,
|
|
182
|
+
initialPlaceholder: '⏳ Sedang merangkai jawaban...',
|
|
183
|
+
quoted: m
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 6. 📱 Multi-Session Manager
|
|
188
|
+
Kelola banyak nomor bot WhatsApp sekaligus dalam satu script:
|
|
189
|
+
```javascript
|
|
190
|
+
import makeWASocket, { LyxeSessionManager, useMultiFileAuthState } from 'lyxe-baileys'
|
|
191
|
+
|
|
192
|
+
const manager = new LyxeSessionManager({
|
|
193
|
+
createSocketFn: makeWASocket
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Buat dan jalankan sesi bot 1
|
|
197
|
+
const { state, saveCreds } = await useMultiFileAuthState('./session-bot1')
|
|
198
|
+
const sock1 = await manager.createSession('bot-1', { auth: state })
|
|
199
|
+
|
|
200
|
+
// Tangkap pesan dari semua sesi sekaligus
|
|
201
|
+
manager.on('messages.upsert', ({ sessionId, upsert, sock }) => {
|
|
202
|
+
console.log(`Pesan masuk di ${sessionId}`)
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 7. 🛡️ Built-in Proxy & TLS Chrome Spoofer (Anti-Banned Connection Shield)
|
|
207
|
+
Hubungkan bot melalui proxy HTTP, HTTPS, atau SOCKS5 dengan fingerprint TLS Chrome 131+ asli untuk meminimalkan deteksi bot dari server WhatsApp:
|
|
208
|
+
```javascript
|
|
209
|
+
import makeWASocket, { useMultiFileAuthState } from 'lyxe-baileys'
|
|
210
|
+
|
|
211
|
+
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
212
|
+
|
|
213
|
+
// Mendukung HTTP, HTTPS, atau SOCKS5 Proxy
|
|
214
|
+
const sock = makeWASocket({
|
|
215
|
+
auth: state,
|
|
216
|
+
proxy: 'socks5://user:password@127.0.0.1:1080' // atau 'http://proxyhost:8080'
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 8. 🏷️ Bypass Pembatasan Privasi Grup (Privacy 403 Bypass Engine)
|
|
221
|
+
Menambahkan peserta ke grup meskipun nomor target mengunci privasi grup (*Error 403*). Engine otomatis mengambil tiket server resmi dan mengirimkan kartu undangan GroupInviteMessage (V4) 1-klik langsung ke chat pribadinya:
|
|
222
|
+
```javascript
|
|
223
|
+
// Otomatis bypass jika nomor target mengunci privasi grupnya
|
|
224
|
+
const results = await sock.groupAddWithBypass(groupJid, ['6281234567890@s.whatsapp.net'], {
|
|
225
|
+
caption: 'Undangan resmi bergabung ke grup kami!'
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
console.log(results)
|
|
229
|
+
// Output: [{ jid: '...', status: '403_bypassed_with_invite', bypassed: true, inviteCode: '...' }]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 9. 🗄️ Immortal Media & Message Vault (Anti-Delete Otomatis)
|
|
233
|
+
Simpan semua pesan, media (gambar, video, audio, VN), dan pesan View-Once yang masuk secara otomatis ke brankas lokal sebelum sempat ditarik pengirim:
|
|
234
|
+
```javascript
|
|
235
|
+
// Mengambil statistik dari vault
|
|
236
|
+
const stats = sock.vault.getStats()
|
|
237
|
+
console.log(`Vault menyimpan ${stats.totalMessages} pesan dan ${stats.totalMedia} berkas media.`)
|
|
238
|
+
|
|
239
|
+
// Ambil riwayat chat tertentu dari brankas
|
|
240
|
+
const chatHistory = sock.vault.getMessages('628123456789@s.whatsapp.net', 20)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 10. 🚀 Built-in Web Cockpit & Live Dashboard
|
|
244
|
+
Pantau status koneksi bot, statistik memori, uptime, kirim pesan uji coba, dan lihat pesan masuk secara live real-time via browser tanpa dependensi tambahan (zero-dependency native HTTP):
|
|
245
|
+
```javascript
|
|
246
|
+
// Nyalakan Web Cockpit Dashboard di port pilihan
|
|
247
|
+
sock.startDashboard(3000)
|
|
248
|
+
// Buka browser Anda: http://localhost:3000
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 11. ✈️ Telegram to WhatsApp Auto-Bridge
|
|
252
|
+
Hubungkan bot Telegram langsung ke WhatsApp! Pesan teks, foto, video, dokumen, dan voice note dari Telegram otomatis diteruskan ke nomor atau grup WhatsApp yang ditentukan:
|
|
253
|
+
```javascript
|
|
254
|
+
const bridge = sock.bridgeTelegram({
|
|
255
|
+
botToken: '123456789:ABCdefGHIjklMNOpqrSTUvwxYZ',
|
|
256
|
+
targetWaJid: '120363023123456789@g.us', // JID WhatsApp tujuan
|
|
257
|
+
allowedTelegramChatIds: ['-1001234567890'], // (Opsional) filter ID Telegram
|
|
258
|
+
autoDownloadMedia: true
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
bridge.start()
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 12. 🛰️ Presence & Online Stalker Radar (Stealth Subscriptions)
|
|
265
|
+
Lacak status online / offline target secara diam-diam tanpa perlu membuka obrolan mereka menggunakan stanza presence WhatsApp tingkat rendah:
|
|
266
|
+
```javascript
|
|
267
|
+
// Pantau target secara real-time
|
|
268
|
+
await sock.trackPresence('628123456789@s.whatsapp.net', (presence) => {
|
|
269
|
+
console.log(`[RADAR] ${presence.jid} sekarang: ${presence.status} pada ${new Date(presence.lastSeen).toLocaleTimeString()}`)
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 13. ⚪ Circular Video Note PTV (WhatsApp Video Bulat)
|
|
274
|
+
Kirim video note berbentuk bulat layaknya pesan video instan di Telegram (Push-To-Video / PTV):
|
|
275
|
+
```javascript
|
|
276
|
+
// Kirim video note bulat instan
|
|
277
|
+
await sock.sendVideoNote(jid, './video.mp4')
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
116
282
|
## 📚 Daftar Isi
|
|
117
283
|
|
|
118
284
|
- [Ringkasan](#-ringkasan)
|
package/package.json
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { delay } from '../Utils/generics.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mengirim pesan dengan efek streaming (seperti animasi mengetik AI ChatGPT/Gemini).
|
|
5
|
+
* Pesan dikirim sekali lalu di-edit bertahap seiring potongan teks baru tiba.
|
|
6
|
+
*
|
|
7
|
+
* @param {any} sock Socket Baileys
|
|
8
|
+
* @param {string} jid Target chat
|
|
9
|
+
* @param {AsyncIterable<string>|string[]|Iterable<string>} stream Iterable teks/chunk
|
|
10
|
+
* @param {object} [options={}] Opsi
|
|
11
|
+
* @param {number} [options.throttleMs=350] Interval minimum antar edit agar tidak terkena rate-limit WA
|
|
12
|
+
* @param {string} [options.initialPlaceholder='⏳ Sedang berpikir...'] Teks awal saat proses mulai
|
|
13
|
+
* @param {any} [options.quoted] Pesan rujukan (quoted message)
|
|
14
|
+
* @returns {Promise<any>} Objek pesan terakhir yang dikirim
|
|
15
|
+
*/
|
|
16
|
+
export const streamMessage = async (sock, jid, stream, options = {}) => {
|
|
17
|
+
const throttleMs = options.throttleMs || 350;
|
|
18
|
+
const placeholder = options.initialPlaceholder || '⏳ Sedang berpikir...';
|
|
19
|
+
|
|
20
|
+
// Kirim pesan pertama
|
|
21
|
+
const sent = await sock.sendMessage(jid, { text: placeholder }, { quoted: options.quoted });
|
|
22
|
+
if (!sent?.key) return sent;
|
|
23
|
+
|
|
24
|
+
let accumulatedText = '';
|
|
25
|
+
let lastEditTime = Date.now();
|
|
26
|
+
|
|
27
|
+
const doEdit = async (text) => {
|
|
28
|
+
try {
|
|
29
|
+
await sock.sendMessage(jid, {
|
|
30
|
+
text: text,
|
|
31
|
+
edit: sent.key
|
|
32
|
+
});
|
|
33
|
+
lastEditTime = Date.now();
|
|
34
|
+
} catch {
|
|
35
|
+
// Abaikan error transien saat streaming edit
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (stream && typeof stream[Symbol.asyncIterator] === 'function') {
|
|
40
|
+
for await (const chunk of stream) {
|
|
41
|
+
accumulatedText += chunk;
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
if (now - lastEditTime >= throttleMs) {
|
|
44
|
+
await doEdit(accumulatedText);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} else if (stream && typeof stream[Symbol.iterator] === 'function') {
|
|
48
|
+
for (const chunk of stream) {
|
|
49
|
+
accumulatedText += chunk;
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
if (now - lastEditTime >= throttleMs) {
|
|
52
|
+
await doEdit(accumulatedText);
|
|
53
|
+
await delay(throttleMs);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} else if (typeof stream === 'string') {
|
|
57
|
+
// Jika diberikan full string, simulasikan streaming dengan memecah per kata
|
|
58
|
+
const words = stream.split(' ');
|
|
59
|
+
for (let i = 0; i < words.length; i += 3) {
|
|
60
|
+
accumulatedText = words.slice(0, i + 3).join(' ');
|
|
61
|
+
await doEdit(accumulatedText);
|
|
62
|
+
await delay(throttleMs);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Pastikan teks akhir terkirim utuh
|
|
67
|
+
if (accumulatedText && accumulatedText !== placeholder) {
|
|
68
|
+
await doEdit(accumulatedText);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return sent;
|
|
72
|
+
};
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Built-in Web Cockpit & Live Monitor Dashboard (Zero Dependency).
|
|
6
|
+
* Membuka antarmuka grafis di browser untuk memantau status bot, chat masuk, dan kontrol bot.
|
|
7
|
+
*/
|
|
8
|
+
export class LyxeDashboard {
|
|
9
|
+
constructor(sock, options = {}) {
|
|
10
|
+
this.sock = sock;
|
|
11
|
+
this.port = Number(options.port) || 3000;
|
|
12
|
+
this.recentMessages = [];
|
|
13
|
+
this.maxMessages = 40;
|
|
14
|
+
this.server = null;
|
|
15
|
+
this.qrCode = null;
|
|
16
|
+
this.connectionStatus = 'connecting';
|
|
17
|
+
this.startTime = Date.now();
|
|
18
|
+
|
|
19
|
+
this.bindEvents();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
bindEvents() {
|
|
23
|
+
if (!this.sock) return;
|
|
24
|
+
|
|
25
|
+
this.sock.ev.on('connection.update', (update) => {
|
|
26
|
+
if (update.qr) {
|
|
27
|
+
this.qrCode = update.qr;
|
|
28
|
+
}
|
|
29
|
+
if (update.connection) {
|
|
30
|
+
this.connectionStatus = update.connection;
|
|
31
|
+
if (update.connection === 'open') {
|
|
32
|
+
this.qrCode = null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.sock.ev.on('messages.upsert', ({ messages }) => {
|
|
38
|
+
for (const m of messages) {
|
|
39
|
+
if (!m.message) continue;
|
|
40
|
+
const text = m.message.conversation ||
|
|
41
|
+
m.message.extendedTextMessage?.text ||
|
|
42
|
+
m.message.imageMessage?.caption ||
|
|
43
|
+
(m.message.imageMessage ? '[Foto]' : m.message.videoMessage ? '[Video]' : '[Media]');
|
|
44
|
+
const from = m.key.remoteJid;
|
|
45
|
+
const sender = m.key.participant || from;
|
|
46
|
+
this.recentMessages.unshift({
|
|
47
|
+
id: m.key.id,
|
|
48
|
+
from,
|
|
49
|
+
sender: sender ? sender.split('@')[0] : 'Unknown',
|
|
50
|
+
fromMe: Boolean(m.key.fromMe),
|
|
51
|
+
text,
|
|
52
|
+
time: new Date().toLocaleTimeString('id-ID')
|
|
53
|
+
});
|
|
54
|
+
if (this.recentMessages.length > this.maxMessages) {
|
|
55
|
+
this.recentMessages.pop();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
start() {
|
|
62
|
+
if (this.server) return;
|
|
63
|
+
|
|
64
|
+
this.server = http.createServer((req, res) => {
|
|
65
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
66
|
+
|
|
67
|
+
// CORS headers
|
|
68
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
69
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
70
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
71
|
+
|
|
72
|
+
if (req.method === 'OPTIONS') {
|
|
73
|
+
res.writeHead(204);
|
|
74
|
+
res.end();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (url.pathname === '/api/status') {
|
|
79
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
80
|
+
res.end(JSON.stringify({
|
|
81
|
+
status: this.connectionStatus,
|
|
82
|
+
user: this.sock?.user || null,
|
|
83
|
+
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
|
84
|
+
memory: {
|
|
85
|
+
totalMB: Math.round(os.totalmem() / 1024 / 1024),
|
|
86
|
+
freeMB: Math.round(os.freemem() / 1024 / 1024),
|
|
87
|
+
processMB: Math.round(process.memoryUsage().rss / 1024 / 1024)
|
|
88
|
+
},
|
|
89
|
+
qrCode: this.qrCode
|
|
90
|
+
}));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (url.pathname === '/api/messages') {
|
|
95
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
96
|
+
res.end(JSON.stringify(this.recentMessages));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (url.pathname === '/api/send' && req.method === 'POST') {
|
|
101
|
+
let body = '';
|
|
102
|
+
req.on('data', chunk => body += chunk);
|
|
103
|
+
req.on('end', async () => {
|
|
104
|
+
try {
|
|
105
|
+
const { jid, text } = JSON.parse(body);
|
|
106
|
+
if (!jid || !text) {
|
|
107
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
108
|
+
return res.end(JSON.stringify({ error: 'JID dan Text wajib diisi' }));
|
|
109
|
+
}
|
|
110
|
+
const formattedJid = jid.includes('@') ? jid : `${jid.replace(/[^0-9]/g, '')}@s.whatsapp.net`;
|
|
111
|
+
const sent = await this.sock.sendMessage(formattedJid, { text });
|
|
112
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
113
|
+
res.end(JSON.stringify({ success: true, id: sent?.key?.id }));
|
|
114
|
+
} catch (e) {
|
|
115
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
116
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// HTML Frontend Dashboard
|
|
123
|
+
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
124
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
125
|
+
res.end(this.renderHTML());
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
130
|
+
res.end('Not Found');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
this.server.listen(this.port, () => {
|
|
134
|
+
console.log(`\n🎛️ [Lyxe Cockpit] Web Dashboard aktif di: http://localhost:${this.port}\n`);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
stop() {
|
|
139
|
+
if (this.server) {
|
|
140
|
+
this.server.close();
|
|
141
|
+
this.server = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
renderHTML() {
|
|
146
|
+
return `<!DOCTYPE html>
|
|
147
|
+
<html lang="id">
|
|
148
|
+
<head>
|
|
149
|
+
<meta charset="UTF-8">
|
|
150
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
151
|
+
<title>Lyxe Baileys - Live Cockpit Dashboard</title>
|
|
152
|
+
<style>
|
|
153
|
+
:root {
|
|
154
|
+
--bg: #0f172a;
|
|
155
|
+
--card: #1e293b;
|
|
156
|
+
--border: #334155;
|
|
157
|
+
--primary: #8b5cf6;
|
|
158
|
+
--accent: #06b6d4;
|
|
159
|
+
--text: #f8fafc;
|
|
160
|
+
--muted: #94a3b8;
|
|
161
|
+
--green: #10b981;
|
|
162
|
+
--red: #ef4444;
|
|
163
|
+
}
|
|
164
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
165
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); padding: 24px; }
|
|
166
|
+
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid var(--border); }
|
|
167
|
+
.header h1 { font-size: 24px; font-weight: 700; background: linear-gradient(135deg, var(--primary), var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
168
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px; }
|
|
169
|
+
.card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.2); }
|
|
170
|
+
.card h3 { font-size: 14px; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; letter-spacing: 0.5px; }
|
|
171
|
+
.card .val { font-size: 24px; font-weight: bold; }
|
|
172
|
+
.badge { display: inline-block; padding: 4px 10px; border-radius: 9999px; font-size: 12px; font-weight: 600; text-transform: uppercase; }
|
|
173
|
+
.badge-open { background: rgba(16, 185, 129, 0.2); color: var(--green); }
|
|
174
|
+
.badge-connecting { background: rgba(6, 182, 212, 0.2); color: var(--accent); }
|
|
175
|
+
.badge-close { background: rgba(239, 68, 68, 0.2); color: var(--red); }
|
|
176
|
+
.chat-container { display: grid; grid-template-columns: 1fr 340px; gap: 20px; }
|
|
177
|
+
.chat-box { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 20px; height: 500px; display: flex; flex-direction: column; }
|
|
178
|
+
.messages-list { flex: 1; overflow-y: auto; padding-right: 8px; }
|
|
179
|
+
.msg-item { padding: 10px; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 14px; }
|
|
180
|
+
.msg-item .meta { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-bottom: 4px; }
|
|
181
|
+
.send-box { display: flex; flex-direction: column; gap: 12px; }
|
|
182
|
+
input, textarea, button { width: 100%; padding: 10px 14px; background: #0f172a; border: 1px solid var(--border); border-radius: 8px; color: #fff; font-size: 14px; }
|
|
183
|
+
input:focus, textarea:focus { outline: none; border-color: var(--primary); }
|
|
184
|
+
button { background: var(--primary); color: #fff; font-weight: 600; border: none; cursor: pointer; transition: 0.2s; }
|
|
185
|
+
button:hover { background: #7c3aed; }
|
|
186
|
+
@media (max-width: 800px) { .chat-container { grid-template-columns: 1fr; } }
|
|
187
|
+
</style>
|
|
188
|
+
</head>
|
|
189
|
+
<body>
|
|
190
|
+
<div class="header">
|
|
191
|
+
<div>
|
|
192
|
+
<h1>⚡ LYXE BAILEYS COCKPIT</h1>
|
|
193
|
+
<p style="color: var(--muted); font-size: 13px;">Realtime Bot Engine & Protocol Telemetry</p>
|
|
194
|
+
</div>
|
|
195
|
+
<div id="badge-status" class="badge badge-connecting">Connecting...</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div class="grid">
|
|
199
|
+
<div class="card">
|
|
200
|
+
<h3>Nomor WhatsApp</h3>
|
|
201
|
+
<div id="val-user" class="val">-</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="card">
|
|
204
|
+
<h3>Penggunaan RAM</h3>
|
|
205
|
+
<div id="val-ram" class="val">-</div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="card">
|
|
208
|
+
<h3>Uptime Bot</h3>
|
|
209
|
+
<div id="val-uptime" class="val">-</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div class="chat-container">
|
|
214
|
+
<div class="chat-box">
|
|
215
|
+
<h3 style="margin-bottom: 12px;">📡 LIVE MESSAGE STREAM</h3>
|
|
216
|
+
<div id="messages-list" class="messages-list">
|
|
217
|
+
<p style="color: var(--muted); text-align: center; margin-top: 40px;">Menunggu pesan masuk...</p>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div class="card send-box">
|
|
222
|
+
<h3>🚀 TEST SEND MESSAGE</h3>
|
|
223
|
+
<input id="input-jid" placeholder="Nomor WA (contoh: 628123xxx)" />
|
|
224
|
+
<textarea id="input-text" rows="5" placeholder="Ketik pesan yang ingin dikirim..."></textarea>
|
|
225
|
+
<button onclick="sendMessage()">KIRIM PESAN</button>
|
|
226
|
+
<p id="send-status" style="font-size: 12px; text-align: center; color: var(--muted);"></p>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<script>
|
|
231
|
+
async function updateStatus() {
|
|
232
|
+
try {
|
|
233
|
+
const res = await fetch('/api/status');
|
|
234
|
+
const data = await res.json();
|
|
235
|
+
|
|
236
|
+
const badge = document.getElementById('badge-status');
|
|
237
|
+
badge.innerText = data.status || 'unknown';
|
|
238
|
+
badge.className = 'badge badge-' + (data.status || 'connecting');
|
|
239
|
+
|
|
240
|
+
document.getElementById('val-user').innerText = data.user?.id ? data.user.id.split('@')[0] : 'Belum Login';
|
|
241
|
+
document.getElementById('val-ram').innerText = data.memory ? data.memory.processMB + ' MB' : '-';
|
|
242
|
+
|
|
243
|
+
const uptime = data.uptime || 0;
|
|
244
|
+
const mins = Math.floor(uptime / 60);
|
|
245
|
+
const secs = uptime % 60;
|
|
246
|
+
document.getElementById('val-uptime').innerText = mins + 'm ' + secs + 's';
|
|
247
|
+
} catch (e) {}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function updateMessages() {
|
|
251
|
+
try {
|
|
252
|
+
const res = await fetch('/api/messages');
|
|
253
|
+
const list = await res.json();
|
|
254
|
+
const container = document.getElementById('messages-list');
|
|
255
|
+
if (!list || !list.length) return;
|
|
256
|
+
|
|
257
|
+
container.innerHTML = list.map(m => \`
|
|
258
|
+
<div class="msg-item">
|
|
259
|
+
<div class="meta">
|
|
260
|
+
<span>\${m.fromMe ? '🟢 OUT' : '🔵 IN'} • @\${m.sender}</span>
|
|
261
|
+
<span>\${m.time}</span>
|
|
262
|
+
</div>
|
|
263
|
+
<div>\${m.text}</div>
|
|
264
|
+
</div>
|
|
265
|
+
\`).join('');
|
|
266
|
+
} catch (e) {}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function sendMessage() {
|
|
270
|
+
const jid = document.getElementById('input-jid').value.trim();
|
|
271
|
+
const text = document.getElementById('input-text').value.trim();
|
|
272
|
+
const status = document.getElementById('send-status');
|
|
273
|
+
if (!jid || !text) return alert('Isi nomor dan pesan terlebih dahulu!');
|
|
274
|
+
status.innerText = 'Mengirim...';
|
|
275
|
+
try {
|
|
276
|
+
const res = await fetch('/api/send', {
|
|
277
|
+
method: 'POST',
|
|
278
|
+
headers: { 'Content-Type': 'application/json' },
|
|
279
|
+
body: JSON.stringify({ jid, text })
|
|
280
|
+
});
|
|
281
|
+
const data = await res.json();
|
|
282
|
+
if (data.success) {
|
|
283
|
+
status.innerText = '✅ Terkirim! ID: ' + data.id;
|
|
284
|
+
document.getElementById('input-text').value = '';
|
|
285
|
+
} else {
|
|
286
|
+
status.innerText = '❌ Gagal: ' + data.error;
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
status.innerText = '❌ Error: ' + err.message;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
setInterval(updateStatus, 2000);
|
|
294
|
+
setInterval(updateMessages, 1500);
|
|
295
|
+
updateStatus();
|
|
296
|
+
updateMessages();
|
|
297
|
+
</script>
|
|
298
|
+
</body>
|
|
299
|
+
</html>`;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getBinaryNodeChild } from '../WABinary/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Menambahkan peserta ke grup dengan engine bypass pembatasan privasi (Privacy 403 Bypass).
|
|
5
|
+
* Jika peserta menyetel privasi grup ke "Kontak Saya" atau "Tidak Ada" (Error 403),
|
|
6
|
+
* engine otomatis mengambil kode undangan resmi yang ditandatangani server WhatsApp,
|
|
7
|
+
* lalu mengirim kartu undangan GroupInviteMessage (V4) langsung ke chat pribadi target.
|
|
8
|
+
*
|
|
9
|
+
* @param {any} sock Socket Baileys
|
|
10
|
+
* @param {string} groupJid JID grup tujuan
|
|
11
|
+
* @param {string[]|string} participants JID nomor yang ingin ditambahkan
|
|
12
|
+
* @param {object} [options={}] Opsi tambahan
|
|
13
|
+
* @param {boolean} [options.autoInvite=true] Otomatis kirim kartu undangan jika terblokir privasi 403
|
|
14
|
+
* @param {string} [options.caption] Teks pengantar undangan
|
|
15
|
+
* @returns {Promise<Array<{ jid: string, status: string, bypassed: boolean, inviteCode?: string, inviteExpiration?: number }>>}
|
|
16
|
+
*/
|
|
17
|
+
export const groupAddWithBypass = async (sock, groupJid, participants, options = {}) => {
|
|
18
|
+
const list = Array.isArray(participants) ? participants : [participants];
|
|
19
|
+
const autoInvite = options.autoInvite !== false;
|
|
20
|
+
|
|
21
|
+
// 1. Eksekusi penambahan langsung via WhatsApp protocol
|
|
22
|
+
const updateResults = await sock.groupParticipantsUpdate(groupJid, list, 'add');
|
|
23
|
+
const finalResults = [];
|
|
24
|
+
|
|
25
|
+
let groupMeta = null;
|
|
26
|
+
const getMeta = async () => {
|
|
27
|
+
if (!groupMeta) {
|
|
28
|
+
try {
|
|
29
|
+
groupMeta = await sock.groupMetadata(groupJid);
|
|
30
|
+
} catch {
|
|
31
|
+
groupMeta = { subject: 'Grup WhatsApp' };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return groupMeta;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (const res of updateResults) {
|
|
38
|
+
if (res.status === '403') {
|
|
39
|
+
// Server WhatsApp menolak add langsung karena privasi target, tapi memberikan add_request token resmi
|
|
40
|
+
const addRequestNode = getBinaryNodeChild(res.content, 'add_request');
|
|
41
|
+
const code = addRequestNode?.attrs?.code;
|
|
42
|
+
const expiration = addRequestNode?.attrs?.expiration;
|
|
43
|
+
|
|
44
|
+
if (code && expiration && autoInvite) {
|
|
45
|
+
try {
|
|
46
|
+
const meta = await getMeta();
|
|
47
|
+
await sock.sendMessage(res.jid, {
|
|
48
|
+
groupInviteMessage: {
|
|
49
|
+
groupJid: groupJid,
|
|
50
|
+
groupName: meta.subject || 'Grup WhatsApp',
|
|
51
|
+
caption: options.caption || `Halo! Kamu diundang bergabung ke grup *${meta.subject || 'Grup'}*. Klik tombol di bawah untuk bergabung langsung.`,
|
|
52
|
+
inviteCode: code,
|
|
53
|
+
inviteExpiration: Number(expiration)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
finalResults.push({
|
|
58
|
+
jid: res.jid,
|
|
59
|
+
status: '403_bypassed_with_invite',
|
|
60
|
+
bypassed: true,
|
|
61
|
+
inviteCode: code,
|
|
62
|
+
inviteExpiration: Number(expiration)
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
} catch (inviteErr) {
|
|
66
|
+
sock.logger?.warn?.({ err: inviteErr }, 'groupAddWithBypass: Gagal mengirim V4 group invite card');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
finalResults.push({
|
|
71
|
+
jid: res.jid,
|
|
72
|
+
status: '403',
|
|
73
|
+
bypassed: false,
|
|
74
|
+
inviteCode: code,
|
|
75
|
+
inviteExpiration: expiration ? Number(expiration) : undefined
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
finalResults.push({
|
|
79
|
+
jid: res.jid,
|
|
80
|
+
status: res.status || '200',
|
|
81
|
+
bypassed: res.status === '200'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return finalResults;
|
|
87
|
+
};
|