vinzzsync-wacli 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/c.js +2 -0
- package/func.js +3263 -0
- package/index.js +2416 -0
- package/index2.js +3611 -0
- package/lib/sqlAuth.js +109 -0
- package/package.json +21 -0
- package/plugins/_loader.js +191 -0
- package/plugins/addplugins.js +93 -0
- package/plugins/backup.js +91 -0
- package/plugins/cekch.js +157 -0
- package/plugins/cekgb.js +105 -0
- package/plugins/clear.js +12 -0
- package/plugins/cmd.js +69 -0
- package/plugins/cmfil.js +110 -0
- package/plugins/cms.js +474 -0
- package/plugins/delplugins.js +57 -0
- package/plugins/dmsg.js +112 -0
- package/plugins/eval.js +175 -0
- package/plugins/evfil.js +86 -0
- package/plugins/exit.js +11 -0
- package/plugins/fadm.js +107 -0
- package/plugins/fakemsg.js +154 -0
- package/plugins/fakesize.js +204 -0
- package/plugins/fclick.js +111 -0
- package/plugins/fitnah.js +87 -0
- package/plugins/getfile.js +169 -0
- package/plugins/getplugins.js +71 -0
- package/plugins/getquoted.js +76 -0
- package/plugins/getusn.js +92 -0
- package/plugins/groups.js +62 -0
- package/plugins/help.js +19 -0
- package/plugins/ht.js +45 -0
- package/plugins/ht2.js +59 -0
- package/plugins/ht3.js +66 -0
- package/plugins/isbot.js +177 -0
- package/plugins/listplugins.js +103 -0
- package/plugins/me.js +95 -0
- package/plugins/minigames.js +144 -0
- package/plugins/ping.js +124 -0
- package/plugins/quoted.js +102 -0
- package/plugins/rvo.js +88 -0
- package/plugins/savefile.js +334 -0
- package/plugins/send.js +52 -0
- package/plugins/session.js +18 -0
- package/plugins/smsg.js +204 -0
- package/plugins/status.js +18 -0
- package/plugins/typing_troll.js +91 -0
- package/pp.jpg +0 -0
- package/vkazee-send-message.js +1238 -0
package/index.js
ADDED
|
@@ -0,0 +1,2416 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SMART WA CLI
|
|
3
|
+
// - Auto detect MultiFile / SQLite session
|
|
4
|
+
// - Auto reconnect
|
|
5
|
+
// - Dynamic WA Web version when supported
|
|
6
|
+
// - Incoming message callback/debug
|
|
7
|
+
// - send / eval / cmd / exec / me / profile / status
|
|
8
|
+
// ============================================================
|
|
9
|
+
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import util from 'util';
|
|
13
|
+
import crypto from "crypto";
|
|
14
|
+
import qrcode from 'qrcode-terminal'
|
|
15
|
+
import readline from 'readline';
|
|
16
|
+
import { exec } from 'child_process';
|
|
17
|
+
import prompts from 'prompts';
|
|
18
|
+
import Database from 'better-sqlite3';
|
|
19
|
+
import { VERSION, Button, ButtonV2, Carousel, AIRich, Toolkit, copyNForward} from "./func.js";
|
|
20
|
+
import { pathToFileURL } from 'url';
|
|
21
|
+
import {
|
|
22
|
+
loadPlugins,
|
|
23
|
+
getPlugin,
|
|
24
|
+
getPluginCommands,
|
|
25
|
+
watchPlugins
|
|
26
|
+
} from './plugins/_loader.js';
|
|
27
|
+
// ------------------------------------------------------------
|
|
28
|
+
// BAILEYS - dynamic import supaya export fork lebih fleksibel
|
|
29
|
+
// ------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const baileys = await import('@vkazee/baileys');
|
|
32
|
+
|
|
33
|
+
const makeWASocket =
|
|
34
|
+
baileys.default ??
|
|
35
|
+
baileys.makeWASocket;
|
|
36
|
+
|
|
37
|
+
const delay = baileys.delay;
|
|
38
|
+
|
|
39
|
+
const useMultiFileAuthState =
|
|
40
|
+
baileys.useMultiFileAuthState;
|
|
41
|
+
|
|
42
|
+
const makeCacheableSignalKeyStore =
|
|
43
|
+
baileys.makeCacheableSignalKeyStore;
|
|
44
|
+
|
|
45
|
+
const fetchLatestWaWebVersion =
|
|
46
|
+
baileys.fetchLatestWaWebVersion;
|
|
47
|
+
|
|
48
|
+
const Browsers =
|
|
49
|
+
baileys.Browsers;
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
BufferJSON,
|
|
53
|
+
initAuthCreds
|
|
54
|
+
} = baileys;
|
|
55
|
+
|
|
56
|
+
// ------------------------------------------------------------
|
|
57
|
+
// CONFIG
|
|
58
|
+
// ------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
const SESSION_DIR = path.resolve('./session');
|
|
61
|
+
|
|
62
|
+
const LOG_LEVEL =
|
|
63
|
+
process.env.LOG_LEVEL || 'info';
|
|
64
|
+
|
|
65
|
+
const BASE_RECONNECT_DELAY = 3000;
|
|
66
|
+
const MAX_RECONNECT_DELAY = 30000;
|
|
67
|
+
|
|
68
|
+
const ENABLE_DYNAMIC_VERSION = true;
|
|
69
|
+
|
|
70
|
+
// ------------------------------------------------------------
|
|
71
|
+
// GLOBAL STATE
|
|
72
|
+
// ------------------------------------------------------------
|
|
73
|
+
global.owner = "6285185667890";
|
|
74
|
+
global.ownerr = "6285185667890@s.whatsapp.net";
|
|
75
|
+
global.v = {
|
|
76
|
+
key: {
|
|
77
|
+
participant: '13135550002@s.whatsapp.net',
|
|
78
|
+
remoteJid: '17608914335-1615035634@g.us',
|
|
79
|
+
},
|
|
80
|
+
message: {
|
|
81
|
+
conversation: `_Powered By Vinzz_`
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
global.prefix = ".";
|
|
85
|
+
let loginMethod = null;
|
|
86
|
+
let sock = null;
|
|
87
|
+
|
|
88
|
+
let sessionType = 'unknown';
|
|
89
|
+
let sessionPath = null;
|
|
90
|
+
|
|
91
|
+
let connectionState = 'closed';
|
|
92
|
+
|
|
93
|
+
let reconnectTimer = null;
|
|
94
|
+
let reconnectAttempts = 0;
|
|
95
|
+
|
|
96
|
+
let shuttingDown = false;
|
|
97
|
+
let terminalStarted = false;
|
|
98
|
+
let lastDisconnectInfo = null;
|
|
99
|
+
|
|
100
|
+
let currentWaVersion = null;
|
|
101
|
+
let lastDisconnect = null;
|
|
102
|
+
let lastConnectionUpdate = null;
|
|
103
|
+
|
|
104
|
+
let messageCount = 0;
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// MESSAGE CACHE
|
|
108
|
+
// ============================================================
|
|
109
|
+
|
|
110
|
+
function getMessageType(message) {
|
|
111
|
+
if (!message) {
|
|
112
|
+
return 'unknown';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const keys =
|
|
116
|
+
Object.keys(
|
|
117
|
+
message
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
keys[0] ||
|
|
122
|
+
'unknown'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
const messageStore = new Map();
|
|
128
|
+
const MAX_MESSAGES = 2000;
|
|
129
|
+
|
|
130
|
+
function saveMessage(message) {
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
!message?.key?.id ||
|
|
134
|
+
!message?.key?.remoteJid
|
|
135
|
+
) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const key =
|
|
140
|
+
`${message.key.remoteJid}:${message.key.id}`;
|
|
141
|
+
|
|
142
|
+
messageStore.set(
|
|
143
|
+
key,
|
|
144
|
+
message
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Hapus pesan paling lama jika cache penuh
|
|
148
|
+
if (
|
|
149
|
+
messageStore.size >
|
|
150
|
+
MAX_MESSAGES
|
|
151
|
+
) {
|
|
152
|
+
const oldestKey =
|
|
153
|
+
messageStore
|
|
154
|
+
.keys()
|
|
155
|
+
.next()
|
|
156
|
+
.value;
|
|
157
|
+
|
|
158
|
+
messageStore.delete(
|
|
159
|
+
oldestKey
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getDynamicHelpList() {
|
|
165
|
+
const source = handleCommand.toString();
|
|
166
|
+
|
|
167
|
+
const commands = [
|
|
168
|
+
...source.matchAll(/\bcase\s+['"]([^'"]+)['"]\s*:/g)
|
|
169
|
+
]
|
|
170
|
+
.map(match => match[1])
|
|
171
|
+
.filter(Boolean);
|
|
172
|
+
|
|
173
|
+
return [...new Set(commands)];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function chooseLoginMethod() {
|
|
177
|
+
const response = await prompts({
|
|
178
|
+
type: 'select',
|
|
179
|
+
name: 'method',
|
|
180
|
+
message: 'Pilih method login:',
|
|
181
|
+
choices: [
|
|
182
|
+
{
|
|
183
|
+
title: 'Scan QR Code',
|
|
184
|
+
value: 'qr'
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
title: 'Pairing Code',
|
|
188
|
+
value: 'pairing'
|
|
189
|
+
}
|
|
190
|
+
],
|
|
191
|
+
initial: 0
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (!response.method) {
|
|
195
|
+
console.log('\nOperasi dibatalkan.');
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return response.method;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ------------------------------------------------------------
|
|
203
|
+
// READLINE
|
|
204
|
+
// ------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
let rl = null;
|
|
207
|
+
|
|
208
|
+
function initReadline() {
|
|
209
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
210
|
+
process.stdin.setRawMode(false);
|
|
211
|
+
process.stdin.resume();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (rl) {
|
|
215
|
+
try { rl.close(); } catch {}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
rl = readline.createInterface({
|
|
219
|
+
input: process.stdin,
|
|
220
|
+
output: process.stdout,
|
|
221
|
+
terminal: true
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
rl.setPrompt('\x1b[32mroot@wa-cli\x1b[0m:\x1b[34m~\x1b[0m$ ');
|
|
225
|
+
|
|
226
|
+
rl.on('line', async input => {
|
|
227
|
+
if (shuttingDown || (loginMethod === 'pairing' && connectionState !== 'open')) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
await handleCommand(input);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error(err);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!shuttingDown && connectionState === 'open') {
|
|
238
|
+
rl.prompt();
|
|
239
|
+
} else {
|
|
240
|
+
terminalStarted = false;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
// ------------------------------------------------------------
|
|
247
|
+
// COLORS / LOGGING
|
|
248
|
+
// ------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
function now() {
|
|
251
|
+
return new Date().toISOString();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function debug(label, value = '') {
|
|
255
|
+
const prefix =
|
|
256
|
+
`\x1b[36m[${now()}] [DEBUG] ${label}\x1b[0m`;
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
typeof value === 'object' &&
|
|
260
|
+
value !== null
|
|
261
|
+
) {
|
|
262
|
+
console.log(
|
|
263
|
+
prefix,
|
|
264
|
+
util.inspect(value, {
|
|
265
|
+
depth: 10,
|
|
266
|
+
colors: true,
|
|
267
|
+
compact: false
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
} else {
|
|
271
|
+
console.log(prefix, value);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function info(message) {
|
|
276
|
+
console.log(
|
|
277
|
+
`\x1b[32m[+] ${message}\x1b[0m`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function warn(message) {
|
|
282
|
+
console.log(
|
|
283
|
+
`\x1b[33m[!] ${message}\x1b[0m`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function errlog(message, error = null) {
|
|
288
|
+
console.log(
|
|
289
|
+
`\x1b[31m[ERROR] ${message}\x1b[0m`
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (error) {
|
|
293
|
+
console.error(
|
|
294
|
+
util.inspect(error, {
|
|
295
|
+
depth: 10,
|
|
296
|
+
colors: true,
|
|
297
|
+
compact: false
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ------------------------------------------------------------
|
|
304
|
+
// SESSION DETECTION
|
|
305
|
+
// ------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
function fileExists(file) {
|
|
308
|
+
try {
|
|
309
|
+
return fs.existsSync(file);
|
|
310
|
+
} catch {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function isDirectory(dir) {
|
|
316
|
+
try {
|
|
317
|
+
return fs.statSync(dir).isDirectory();
|
|
318
|
+
} catch {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function detectSQLiteAuth(dbPath) {
|
|
324
|
+
let db;
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
db = new Database(
|
|
328
|
+
dbPath,
|
|
329
|
+
{
|
|
330
|
+
readonly: true,
|
|
331
|
+
fileMustExist: true
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const rows =
|
|
336
|
+
db.prepare(`
|
|
337
|
+
SELECT name
|
|
338
|
+
FROM sqlite_master
|
|
339
|
+
WHERE type = 'table'
|
|
340
|
+
`).all();
|
|
341
|
+
|
|
342
|
+
const tables =
|
|
343
|
+
rows.map(x => x.name);
|
|
344
|
+
|
|
345
|
+
const hasBaileysState =
|
|
346
|
+
tables.includes('baileys_state');
|
|
347
|
+
|
|
348
|
+
let registered = false;
|
|
349
|
+
|
|
350
|
+
if (hasBaileysState) {
|
|
351
|
+
const row =
|
|
352
|
+
db.prepare(`
|
|
353
|
+
SELECT value
|
|
354
|
+
FROM baileys_state
|
|
355
|
+
WHERE key = 'creds'
|
|
356
|
+
LIMIT 1
|
|
357
|
+
`).get();
|
|
358
|
+
|
|
359
|
+
if (row?.value) {
|
|
360
|
+
try {
|
|
361
|
+
const creds =
|
|
362
|
+
JSON.parse(
|
|
363
|
+
Buffer.from(
|
|
364
|
+
row.value
|
|
365
|
+
).toString(),
|
|
366
|
+
BufferJSON?.reviver
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
registered =
|
|
370
|
+
creds?.registered === true;
|
|
371
|
+
} catch {
|
|
372
|
+
registered = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
valid: hasBaileysState,
|
|
379
|
+
registered,
|
|
380
|
+
tables
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
} catch (error) {
|
|
384
|
+
debug(
|
|
385
|
+
`SQLite check gagal: ${dbPath}`,
|
|
386
|
+
error.message
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
valid: false,
|
|
391
|
+
registered: false,
|
|
392
|
+
tables: []
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
} finally {
|
|
396
|
+
try {
|
|
397
|
+
db?.close();
|
|
398
|
+
} catch {}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function detectMultiFileAuth(dir) {
|
|
403
|
+
if (!isDirectory(dir)) {
|
|
404
|
+
return {
|
|
405
|
+
valid: false,
|
|
406
|
+
registered: false,
|
|
407
|
+
files: []
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const files =
|
|
412
|
+
fs.readdirSync(dir);
|
|
413
|
+
|
|
414
|
+
const credsPath =
|
|
415
|
+
path.join(dir, 'creds.json');
|
|
416
|
+
|
|
417
|
+
if (!fileExists(credsPath)) {
|
|
418
|
+
return {
|
|
419
|
+
valid: false,
|
|
420
|
+
registered: false,
|
|
421
|
+
files
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const creds =
|
|
427
|
+
JSON.parse(
|
|
428
|
+
fs.readFileSync(
|
|
429
|
+
credsPath,
|
|
430
|
+
'utf8'
|
|
431
|
+
)
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
valid: true,
|
|
436
|
+
registered:
|
|
437
|
+
creds?.registered === true,
|
|
438
|
+
files
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
} catch {
|
|
442
|
+
return {
|
|
443
|
+
valid: false,
|
|
444
|
+
registered: false,
|
|
445
|
+
files
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Ubah signature fungsi menjadi menerima parameter dir
|
|
451
|
+
function detectSession(targetDir = SESSION_DIR) {
|
|
452
|
+
const result = {
|
|
453
|
+
type: 'none',
|
|
454
|
+
path: null,
|
|
455
|
+
registered: false,
|
|
456
|
+
details: {}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
if (!isDirectory(targetDir)) {
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
464
|
+
|
|
465
|
+
const sqliteCandidates = entries
|
|
466
|
+
.filter(entry => entry.isFile() && (entry.name.endsWith('.db') || entry.name.endsWith('.sqlite') || entry.name.endsWith('.sqlite3')))
|
|
467
|
+
.map(entry => path.join(targetDir, entry.name));
|
|
468
|
+
|
|
469
|
+
const sqliteResults = sqliteCandidates.map(dbPath => ({
|
|
470
|
+
dbPath,
|
|
471
|
+
...detectSQLiteAuth(dbPath)
|
|
472
|
+
}));
|
|
473
|
+
|
|
474
|
+
const registeredSQLite = sqliteResults.find(x => x.valid && x.registered);
|
|
475
|
+
if (registeredSQLite) {
|
|
476
|
+
return { type: 'sqlite', path: registeredSQLite.dbPath, registered: true, details: registeredSQLite };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const validSQLite = sqliteResults.find(x => x.valid);
|
|
480
|
+
if (validSQLite) {
|
|
481
|
+
return { type: 'sqlite', path: validSQLite.dbPath, registered: validSQLite.registered, details: validSQLite };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const multi = detectMultiFileAuth(targetDir);
|
|
485
|
+
if (multi.valid) {
|
|
486
|
+
return { type: 'multifile', path: targetDir, registered: multi.registered, details: multi };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
// ------------------------------------------------------------
|
|
494
|
+
// SQLITE AUTH LOADER
|
|
495
|
+
// Kompatibel dengan struktur:
|
|
496
|
+
// baileys_state(key TEXT PRIMARY KEY, value BLOB)
|
|
497
|
+
// ------------------------------------------------------------
|
|
498
|
+
|
|
499
|
+
async function useSQLiteAuthState(dbPath) {
|
|
500
|
+
const db =
|
|
501
|
+
new Database(dbPath);
|
|
502
|
+
|
|
503
|
+
db.pragma('journal_mode = WAL');
|
|
504
|
+
|
|
505
|
+
db.prepare(`
|
|
506
|
+
CREATE TABLE IF NOT EXISTS baileys_state (
|
|
507
|
+
key TEXT PRIMARY KEY,
|
|
508
|
+
value BLOB
|
|
509
|
+
)
|
|
510
|
+
`).run();
|
|
511
|
+
|
|
512
|
+
function load(key) {
|
|
513
|
+
const row =
|
|
514
|
+
db.prepare(`
|
|
515
|
+
SELECT value
|
|
516
|
+
FROM baileys_state
|
|
517
|
+
WHERE key = ?
|
|
518
|
+
LIMIT 1
|
|
519
|
+
`).get(key);
|
|
520
|
+
|
|
521
|
+
if (!row) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
return JSON.parse(
|
|
527
|
+
Buffer.from(
|
|
528
|
+
row.value
|
|
529
|
+
).toString(),
|
|
530
|
+
BufferJSON?.reviver
|
|
531
|
+
);
|
|
532
|
+
} catch {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function save(key, data) {
|
|
538
|
+
const json =
|
|
539
|
+
JSON.stringify(
|
|
540
|
+
data,
|
|
541
|
+
BufferJSON?.replacer
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
const buffer =
|
|
545
|
+
Buffer.from(
|
|
546
|
+
json,
|
|
547
|
+
'utf8'
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
db.prepare(`
|
|
551
|
+
INSERT OR REPLACE INTO baileys_state
|
|
552
|
+
(key, value)
|
|
553
|
+
VALUES (?, ?)
|
|
554
|
+
`).run(
|
|
555
|
+
key,
|
|
556
|
+
buffer
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function remove(key) {
|
|
561
|
+
db.prepare(`
|
|
562
|
+
DELETE FROM baileys_state
|
|
563
|
+
WHERE key = ?
|
|
564
|
+
`).run(key);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const creds =
|
|
568
|
+
load('creds') ||
|
|
569
|
+
initAuthCreds();
|
|
570
|
+
|
|
571
|
+
const keys = {};
|
|
572
|
+
|
|
573
|
+
const categories = [
|
|
574
|
+
'pre-key',
|
|
575
|
+
'session',
|
|
576
|
+
'sender-key',
|
|
577
|
+
'app-state-sync-key',
|
|
578
|
+
'app-state-sync-version',
|
|
579
|
+
'lid-mapping',
|
|
580
|
+
'device-list'
|
|
581
|
+
];
|
|
582
|
+
|
|
583
|
+
for (
|
|
584
|
+
const category of categories
|
|
585
|
+
) {
|
|
586
|
+
keys[category] = {};
|
|
587
|
+
|
|
588
|
+
const rows =
|
|
589
|
+
db.prepare(`
|
|
590
|
+
SELECT key, value
|
|
591
|
+
FROM baileys_state
|
|
592
|
+
WHERE key LIKE ?
|
|
593
|
+
`).all(
|
|
594
|
+
`${category}:%`
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
for (
|
|
598
|
+
const row of rows
|
|
599
|
+
) {
|
|
600
|
+
try {
|
|
601
|
+
const id =
|
|
602
|
+
row.key.slice(
|
|
603
|
+
category.length + 1
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
keys[category][id] =
|
|
607
|
+
JSON.parse(
|
|
608
|
+
Buffer.from(
|
|
609
|
+
row.value
|
|
610
|
+
).toString(),
|
|
611
|
+
BufferJSON?.reviver
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
} catch {
|
|
615
|
+
// skip corrupt row
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const state = {
|
|
621
|
+
creds,
|
|
622
|
+
|
|
623
|
+
keys: {
|
|
624
|
+
|
|
625
|
+
get: async (
|
|
626
|
+
type,
|
|
627
|
+
ids
|
|
628
|
+
) => {
|
|
629
|
+
|
|
630
|
+
const data = {};
|
|
631
|
+
|
|
632
|
+
for (
|
|
633
|
+
const id of ids
|
|
634
|
+
) {
|
|
635
|
+
const value =
|
|
636
|
+
load(
|
|
637
|
+
`${type}:${id}`
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
if (
|
|
641
|
+
value !== null &&
|
|
642
|
+
value !== undefined
|
|
643
|
+
) {
|
|
644
|
+
data[id] = value;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return data;
|
|
649
|
+
},
|
|
650
|
+
|
|
651
|
+
set: async (
|
|
652
|
+
data
|
|
653
|
+
) => {
|
|
654
|
+
|
|
655
|
+
for (
|
|
656
|
+
const category
|
|
657
|
+
in data
|
|
658
|
+
) {
|
|
659
|
+
|
|
660
|
+
for (
|
|
661
|
+
const id
|
|
662
|
+
in data[category]
|
|
663
|
+
) {
|
|
664
|
+
|
|
665
|
+
const value =
|
|
666
|
+
data[
|
|
667
|
+
category
|
|
668
|
+
][id];
|
|
669
|
+
|
|
670
|
+
save(
|
|
671
|
+
`${category}:${id}`,
|
|
672
|
+
value
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
async function saveCreds() {
|
|
681
|
+
save(
|
|
682
|
+
'creds',
|
|
683
|
+
creds
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Persist berkala
|
|
688
|
+
const interval =
|
|
689
|
+
setInterval(
|
|
690
|
+
() => {
|
|
691
|
+
try {
|
|
692
|
+
save(
|
|
693
|
+
'creds',
|
|
694
|
+
creds
|
|
695
|
+
);
|
|
696
|
+
} catch {}
|
|
697
|
+
},
|
|
698
|
+
30000
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
interval.unref?.();
|
|
702
|
+
|
|
703
|
+
return {
|
|
704
|
+
state,
|
|
705
|
+
saveCreds,
|
|
706
|
+
close: () => {
|
|
707
|
+
try {
|
|
708
|
+
clearInterval(
|
|
709
|
+
interval
|
|
710
|
+
);
|
|
711
|
+
} catch {}
|
|
712
|
+
|
|
713
|
+
try {
|
|
714
|
+
db.close();
|
|
715
|
+
} catch {}
|
|
716
|
+
},
|
|
717
|
+
|
|
718
|
+
// util internal
|
|
719
|
+
_db: db,
|
|
720
|
+
_remove: remove
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// ------------------------------------------------------------
|
|
725
|
+
// WEB VERSION
|
|
726
|
+
// ------------------------------------------------------------
|
|
727
|
+
|
|
728
|
+
async function getLatestVersion() {
|
|
729
|
+
if (
|
|
730
|
+
!ENABLE_DYNAMIC_VERSION ||
|
|
731
|
+
typeof fetchLatestWaWebVersion !==
|
|
732
|
+
'function'
|
|
733
|
+
) {
|
|
734
|
+
debug(
|
|
735
|
+
'fetchLatestWaWebVersion tidak tersedia'
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
const result =
|
|
743
|
+
await fetchLatestWaWebVersion();
|
|
744
|
+
|
|
745
|
+
debug(
|
|
746
|
+
'WA Web version result',
|
|
747
|
+
result
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
if (
|
|
751
|
+
result?.version &&
|
|
752
|
+
Array.isArray(
|
|
753
|
+
result.version
|
|
754
|
+
)
|
|
755
|
+
) {
|
|
756
|
+
|
|
757
|
+
currentWaVersion =
|
|
758
|
+
result.version;
|
|
759
|
+
|
|
760
|
+
info(
|
|
761
|
+
`WA Web version: ${
|
|
762
|
+
result.version.join('.')
|
|
763
|
+
}`
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
return result.version;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
} catch (error) {
|
|
770
|
+
warn(
|
|
771
|
+
`Gagal mengambil WA Web version: ${
|
|
772
|
+
error.message
|
|
773
|
+
}`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// ------------------------------------------------------------
|
|
781
|
+
// QR OPTIONAL RENDER
|
|
782
|
+
// Tidak wajib install qrcode-terminal.
|
|
783
|
+
// ------------------------------------------------------------
|
|
784
|
+
|
|
785
|
+
async function renderQR(qr) {
|
|
786
|
+
try {
|
|
787
|
+
const mod =
|
|
788
|
+
await import(
|
|
789
|
+
'qrcode-terminal'
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
const qrTerminal =
|
|
793
|
+
mod.default ??
|
|
794
|
+
mod;
|
|
795
|
+
|
|
796
|
+
if (
|
|
797
|
+
typeof qrTerminal.generate ===
|
|
798
|
+
'function'
|
|
799
|
+
) {
|
|
800
|
+
qrTerminal.generate(
|
|
801
|
+
qr,
|
|
802
|
+
{
|
|
803
|
+
small: true
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
return true;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
} catch {
|
|
811
|
+
// package tidak terpasang
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
console.log('');
|
|
815
|
+
console.log(
|
|
816
|
+
'[QR] qrcode-terminal tidak terpasang.'
|
|
817
|
+
);
|
|
818
|
+
console.log(
|
|
819
|
+
'[QR] QR tersedia pada event connection.update.'
|
|
820
|
+
);
|
|
821
|
+
console.log('');
|
|
822
|
+
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// ------------------------------------------------------------
|
|
827
|
+
// MESSAGE TEXT EXTRACTION
|
|
828
|
+
// ------------------------------------------------------------
|
|
829
|
+
|
|
830
|
+
function extractMessageText(message) {
|
|
831
|
+
if (!message) {
|
|
832
|
+
return '';
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
return (
|
|
836
|
+
message.conversation ||
|
|
837
|
+
|
|
838
|
+
message.extendedTextMessage
|
|
839
|
+
?.text ||
|
|
840
|
+
|
|
841
|
+
message.imageMessage
|
|
842
|
+
?.caption ||
|
|
843
|
+
|
|
844
|
+
message.videoMessage
|
|
845
|
+
?.caption ||
|
|
846
|
+
|
|
847
|
+
message.documentMessage
|
|
848
|
+
?.caption ||
|
|
849
|
+
|
|
850
|
+
message.buttonsResponseMessage
|
|
851
|
+
?.selectedDisplayText ||
|
|
852
|
+
|
|
853
|
+
message.listResponseMessage
|
|
854
|
+
?.title ||
|
|
855
|
+
|
|
856
|
+
message.templateButtonReplyMessage
|
|
857
|
+
?.selectedDisplayText ||
|
|
858
|
+
|
|
859
|
+
message.interactiveResponseMessage
|
|
860
|
+
?.body
|
|
861
|
+
?.text ||
|
|
862
|
+
|
|
863
|
+
''
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function setupCLIInput() {
|
|
868
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
869
|
+
process.stdin.setRawMode(false);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (rl) rl.close();
|
|
873
|
+
|
|
874
|
+
rl = readline.createInterface({
|
|
875
|
+
input: process.stdin,
|
|
876
|
+
output: process.stdout,
|
|
877
|
+
terminal: true
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// SET PROMPT CUSTOM DENGAN WARNA HIJAU ANSI (\x1b[32m)
|
|
881
|
+
const greenPrompt = '\x1b[32mroot@bot-wa:~$\x1b[0m ';
|
|
882
|
+
rl.setPrompt(greenPrompt);
|
|
883
|
+
|
|
884
|
+
rl.on('line', async (line) => {
|
|
885
|
+
const input = line.trim();
|
|
886
|
+
if (input) {
|
|
887
|
+
await handleCommand(input);
|
|
888
|
+
}
|
|
889
|
+
// Munculkan lagi prompt hijau setelah command selesai dieksekusi
|
|
890
|
+
rl.prompt();
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
// Tampilkan prompt untuk pertama kali
|
|
894
|
+
rl.prompt();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
// ------------------------------------------------------------
|
|
899
|
+
// JID HELPER
|
|
900
|
+
// ------------------------------------------------------------
|
|
901
|
+
|
|
902
|
+
function normalizeJid(input) {
|
|
903
|
+
if (!input) {
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (
|
|
908
|
+
input.includes('@')
|
|
909
|
+
) {
|
|
910
|
+
return input;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const clean =
|
|
914
|
+
input
|
|
915
|
+
.replace(
|
|
916
|
+
/[^\d]/g,
|
|
917
|
+
''
|
|
918
|
+
);
|
|
919
|
+
|
|
920
|
+
if (!clean) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
return (
|
|
925
|
+
`${clean}@s.whatsapp.net`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// ------------------------------------------------------------
|
|
930
|
+
// DISCONNECT
|
|
931
|
+
// ------------------------------------------------------------
|
|
932
|
+
|
|
933
|
+
function parseDisconnect(
|
|
934
|
+
lastDisconnect
|
|
935
|
+
) {
|
|
936
|
+
|
|
937
|
+
const error =
|
|
938
|
+
lastDisconnect?.error;
|
|
939
|
+
|
|
940
|
+
return {
|
|
941
|
+
message:
|
|
942
|
+
error?.message ||
|
|
943
|
+
'Unknown error',
|
|
944
|
+
|
|
945
|
+
statusCode:
|
|
946
|
+
error?.output?.statusCode ??
|
|
947
|
+
error?.statusCode ??
|
|
948
|
+
error?.data?.statusCode ??
|
|
949
|
+
null,
|
|
950
|
+
|
|
951
|
+
reason:
|
|
952
|
+
error?.data?.reason ??
|
|
953
|
+
null,
|
|
954
|
+
|
|
955
|
+
location:
|
|
956
|
+
error?.data?.location ??
|
|
957
|
+
null,
|
|
958
|
+
|
|
959
|
+
name:
|
|
960
|
+
error?.name ??
|
|
961
|
+
null,
|
|
962
|
+
|
|
963
|
+
stack:
|
|
964
|
+
error?.stack ??
|
|
965
|
+
null
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// ------------------------------------------------------------
|
|
970
|
+
// RECONNECT
|
|
971
|
+
// ------------------------------------------------------------
|
|
972
|
+
|
|
973
|
+
function reconnectDelay() {
|
|
974
|
+
const delay =
|
|
975
|
+
BASE_RECONNECT_DELAY *
|
|
976
|
+
Math.pow(
|
|
977
|
+
2,
|
|
978
|
+
Math.min(
|
|
979
|
+
reconnectAttempts,
|
|
980
|
+
4
|
|
981
|
+
)
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
return Math.min(
|
|
985
|
+
delay,
|
|
986
|
+
MAX_RECONNECT_DELAY
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function scheduleReconnect() {
|
|
991
|
+
if (
|
|
992
|
+
shuttingDown
|
|
993
|
+
) {
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (
|
|
998
|
+
reconnectTimer
|
|
999
|
+
) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
reconnectAttempts++;
|
|
1004
|
+
|
|
1005
|
+
const delay =
|
|
1006
|
+
reconnectDelay();
|
|
1007
|
+
|
|
1008
|
+
warn(
|
|
1009
|
+
`Reconnect #${reconnectAttempts} dalam ${delay} ms`
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
reconnectTimer =
|
|
1013
|
+
setTimeout(
|
|
1014
|
+
async () => {
|
|
1015
|
+
|
|
1016
|
+
reconnectTimer =
|
|
1017
|
+
null;
|
|
1018
|
+
|
|
1019
|
+
try {
|
|
1020
|
+
await connectWhatsApp(ACTIVE_SESSION_DIR);
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
errlog(
|
|
1023
|
+
'Reconnect error',
|
|
1024
|
+
error
|
|
1025
|
+
);
|
|
1026
|
+
|
|
1027
|
+
scheduleReconnect();
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
delay
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// ------------------------------------------------------------
|
|
1035
|
+
// TERMINAL PROMPT
|
|
1036
|
+
// ------------------------------------------------------------
|
|
1037
|
+
|
|
1038
|
+
function prompt() {
|
|
1039
|
+
if (terminalStarted || shuttingDown) {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (connectionState !== 'open') {
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
terminalStarted = true;
|
|
1048
|
+
|
|
1049
|
+
console.log('');
|
|
1050
|
+
console.log('Ketik "help" untuk daftar command.');
|
|
1051
|
+
console.log('');
|
|
1052
|
+
|
|
1053
|
+
ask();
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function ask() {
|
|
1057
|
+
if (shuttingDown || connectionState !== 'open' || !rl) {
|
|
1058
|
+
terminalStarted = false;
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
rl.prompt();
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
/* rl.on('line', async input => {
|
|
1067
|
+
if (
|
|
1068
|
+
shuttingDown ||
|
|
1069
|
+
loginMethod === 'pairing' &&
|
|
1070
|
+
connectionState !== 'open'
|
|
1071
|
+
) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
try {
|
|
1076
|
+
await handleCommand(input);
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
console.error(err);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
if (
|
|
1082
|
+
!shuttingDown &&
|
|
1083
|
+
connectionState === 'open'
|
|
1084
|
+
) {
|
|
1085
|
+
rl.prompt();
|
|
1086
|
+
} else {
|
|
1087
|
+
terminalStarted = false;
|
|
1088
|
+
}
|
|
1089
|
+
}); */
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
// ------------------------------------------------------------
|
|
1093
|
+
// MESSAGE CONTEXT / BOT STYLE HANDLER
|
|
1094
|
+
// ------------------------------------------------------------
|
|
1095
|
+
|
|
1096
|
+
function jidNumber(jid) {
|
|
1097
|
+
return String(jid || '')
|
|
1098
|
+
.split('@')[0]
|
|
1099
|
+
.split(':')[0]
|
|
1100
|
+
.replace(/\D/g, '');
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function isSameJid(a, b) {
|
|
1104
|
+
const aa = jidNumber(a);
|
|
1105
|
+
const bb = jidNumber(b);
|
|
1106
|
+
return !!aa && !!bb && aa === bb;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function unwrapMessage(message) {
|
|
1110
|
+
let msg = message || {};
|
|
1111
|
+
|
|
1112
|
+
for (let i = 0; i < 8; i++) {
|
|
1113
|
+
const next =
|
|
1114
|
+
msg?.ephemeralMessage?.message ||
|
|
1115
|
+
msg?.viewOnceMessage?.message ||
|
|
1116
|
+
msg?.viewOnceMessageV2?.message ||
|
|
1117
|
+
msg?.viewOnceMessageV2Extension?.message ||
|
|
1118
|
+
msg?.documentWithCaptionMessage?.message ||
|
|
1119
|
+
msg;
|
|
1120
|
+
|
|
1121
|
+
if (next === msg) break;
|
|
1122
|
+
msg = next;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
return msg;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function getContextInfo(message) {
|
|
1129
|
+
const msg = unwrapMessage(message);
|
|
1130
|
+
for (const value of Object.values(msg || {})) {
|
|
1131
|
+
if (value?.contextInfo) return value.contextInfo;
|
|
1132
|
+
}
|
|
1133
|
+
return msg?.contextInfo || null;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function getQuotedMessage(message, chat) {
|
|
1137
|
+
const contextInfo = getContextInfo(message);
|
|
1138
|
+
const quotedMessage = contextInfo?.quotedMessage;
|
|
1139
|
+
const quotedId = contextInfo?.stanzaId;
|
|
1140
|
+
|
|
1141
|
+
if (!quotedMessage || !quotedId) return null;
|
|
1142
|
+
|
|
1143
|
+
const participant =
|
|
1144
|
+
contextInfo?.participant ||
|
|
1145
|
+
contextInfo?.remoteJid ||
|
|
1146
|
+
chat ||
|
|
1147
|
+
null;
|
|
1148
|
+
|
|
1149
|
+
const stored =
|
|
1150
|
+
messageStore.get(`${chat}:${quotedId}`) ||
|
|
1151
|
+
[...messageStore.values()].find(x =>
|
|
1152
|
+
x?.key?.id === quotedId &&
|
|
1153
|
+
x?.key?.remoteJid === chat
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
const key = stored?.key || {
|
|
1157
|
+
remoteJid: chat,
|
|
1158
|
+
id: quotedId,
|
|
1159
|
+
participant,
|
|
1160
|
+
fromMe: false
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
return {
|
|
1164
|
+
key,
|
|
1165
|
+
message: stored?.message || quotedMessage,
|
|
1166
|
+
id: key.id || quotedId,
|
|
1167
|
+
chat: key.remoteJid || chat,
|
|
1168
|
+
sender: key.participant || key.senderPn || participant,
|
|
1169
|
+
fromMe: key.fromMe === true
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function createMessageContext(message) {
|
|
1174
|
+
const key = message?.key || {};
|
|
1175
|
+
const chat = key.remoteJid || null;
|
|
1176
|
+
|
|
1177
|
+
const sender =
|
|
1178
|
+
key.participant ||
|
|
1179
|
+
key.senderPn ||
|
|
1180
|
+
key.participantPn ||
|
|
1181
|
+
chat ||
|
|
1182
|
+
null;
|
|
1183
|
+
|
|
1184
|
+
const fromMe = key.fromMe === true;
|
|
1185
|
+
|
|
1186
|
+
const ownerJid = global.ownerr;
|
|
1187
|
+
|
|
1188
|
+
const isOwner =
|
|
1189
|
+
fromMe ||
|
|
1190
|
+
isSameJid(sender, ownerJid) ||
|
|
1191
|
+
isSameJid(key.senderPn, ownerJid);
|
|
1192
|
+
|
|
1193
|
+
const rawText =
|
|
1194
|
+
extractMessageText(message?.message) || '';
|
|
1195
|
+
|
|
1196
|
+
global.vv = {
|
|
1197
|
+
key: {
|
|
1198
|
+
participant: '13135550002@s.whatsapp.net',
|
|
1199
|
+
remoteJid: '17608914335-1615035634@g.us',
|
|
1200
|
+
},
|
|
1201
|
+
message: {
|
|
1202
|
+
conversation: rawText
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
const prefix = global.prefix || '.';
|
|
1207
|
+
|
|
1208
|
+
const trimmed = rawText.trim();
|
|
1209
|
+
|
|
1210
|
+
const hasPrefix =
|
|
1211
|
+
trimmed.startsWith(prefix);
|
|
1212
|
+
|
|
1213
|
+
const commandInput =
|
|
1214
|
+
hasPrefix
|
|
1215
|
+
? trimmed.slice(prefix.length).trim()
|
|
1216
|
+
: trimmed;
|
|
1217
|
+
|
|
1218
|
+
const parts =
|
|
1219
|
+
commandInput
|
|
1220
|
+
? commandInput.split(/\s+/g)
|
|
1221
|
+
: [];
|
|
1222
|
+
|
|
1223
|
+
const command =
|
|
1224
|
+
parts.shift()?.toLowerCase() || '';
|
|
1225
|
+
|
|
1226
|
+
const args = parts;
|
|
1227
|
+
|
|
1228
|
+
const text =
|
|
1229
|
+
args.join(' ');
|
|
1230
|
+
|
|
1231
|
+
return {
|
|
1232
|
+
...message,
|
|
1233
|
+
key,
|
|
1234
|
+
chat,
|
|
1235
|
+
sender,
|
|
1236
|
+
fromMe,
|
|
1237
|
+
isOwner,
|
|
1238
|
+
isGroup:
|
|
1239
|
+
typeof chat === 'string' &&
|
|
1240
|
+
chat.endsWith('@g.us'),
|
|
1241
|
+
text: rawText,
|
|
1242
|
+
body: rawText,
|
|
1243
|
+
command,
|
|
1244
|
+
args,
|
|
1245
|
+
textCommand: text,
|
|
1246
|
+
prefix: hasPrefix ? prefix : '',
|
|
1247
|
+
quoted:
|
|
1248
|
+
getQuotedMessage(
|
|
1249
|
+
message?.message,
|
|
1250
|
+
chat
|
|
1251
|
+
),
|
|
1252
|
+
reply: async (
|
|
1253
|
+
text,
|
|
1254
|
+
chatId,
|
|
1255
|
+
options
|
|
1256
|
+
) => {
|
|
1257
|
+
if (!sock) return null;
|
|
1258
|
+
|
|
1259
|
+
const target =
|
|
1260
|
+
chatId || chat;
|
|
1261
|
+
|
|
1262
|
+
if (!target) return null;
|
|
1263
|
+
|
|
1264
|
+
const content =
|
|
1265
|
+
typeof text === 'string'
|
|
1266
|
+
? { text }
|
|
1267
|
+
: text;
|
|
1268
|
+
|
|
1269
|
+
const sendOptions = {
|
|
1270
|
+
contextInfo: {
|
|
1271
|
+
mentionedJid: [],
|
|
1272
|
+
groupMentions: [],
|
|
1273
|
+
isForwarded: false,
|
|
1274
|
+
forwardedNewsletterMessageInfo: {
|
|
1275
|
+
newsletterJid:
|
|
1276
|
+
'120363252742621904@newsletter',
|
|
1277
|
+
newsletterName:
|
|
1278
|
+
global.title || 'Vinzz',
|
|
1279
|
+
serverMessageId: -1
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
quoted: global.vv, onTarget: true,
|
|
1283
|
+
...(options || {})
|
|
1284
|
+
};
|
|
1285
|
+
|
|
1286
|
+
return sock.sendMessage(
|
|
1287
|
+
target,
|
|
1288
|
+
content,
|
|
1289
|
+
sendOptions
|
|
1290
|
+
);
|
|
1291
|
+
},
|
|
1292
|
+
send: async (
|
|
1293
|
+
content,
|
|
1294
|
+
options = {}
|
|
1295
|
+
) => {
|
|
1296
|
+
if (!sock || !chat) {
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
return sock.sendMessage(
|
|
1301
|
+
chat,
|
|
1302
|
+
content,
|
|
1303
|
+
options
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function getCommandInputFromMessage(m) {
|
|
1310
|
+
if (!m?.isOwner) return null;
|
|
1311
|
+
|
|
1312
|
+
const input = String(m.text || '').trim();
|
|
1313
|
+
if (!input) return null;
|
|
1314
|
+
|
|
1315
|
+
const prefix = String(global.prefix || '.');
|
|
1316
|
+
|
|
1317
|
+
if (!input.startsWith(prefix)) {
|
|
1318
|
+
return null;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
const commandInput = input.slice(prefix.length).trim();
|
|
1322
|
+
if (!commandInput) return null;
|
|
1323
|
+
|
|
1324
|
+
const firstToken = commandInput.split(/\s+/g)[0] || '';
|
|
1325
|
+
|
|
1326
|
+
if (!/^[a-z0-9_]+$/i.test(firstToken)) {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return commandInput;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// ------------------------------------------------------------
|
|
1334
|
+
// COMMANDS
|
|
1335
|
+
// ------------------------------------------------------------
|
|
1336
|
+
|
|
1337
|
+
async function handleCommand(
|
|
1338
|
+
input,
|
|
1339
|
+
m = null
|
|
1340
|
+
) {
|
|
1341
|
+
const trimmed = input.trim();
|
|
1342
|
+
|
|
1343
|
+
if (!trimmed) {
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
const args = trimmed.split(/\s+/g);
|
|
1348
|
+
const command = args.shift()?.toLowerCase();
|
|
1349
|
+
const text = args.join(' ');
|
|
1350
|
+
|
|
1351
|
+
try {
|
|
1352
|
+
const plugin = getPlugin(command);
|
|
1353
|
+
|
|
1354
|
+
if (!plugin) {
|
|
1355
|
+
console.log(
|
|
1356
|
+
`\x1b[31mCommand tidak ditemukan: ${command}\x1b[0m`
|
|
1357
|
+
);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
await plugin.run({
|
|
1362
|
+
sock,
|
|
1363
|
+
m,
|
|
1364
|
+
args,
|
|
1365
|
+
text,
|
|
1366
|
+
command,
|
|
1367
|
+
messageStore,
|
|
1368
|
+
MAX_MESSAGES,
|
|
1369
|
+
delay,
|
|
1370
|
+
fs,
|
|
1371
|
+
path,
|
|
1372
|
+
util,
|
|
1373
|
+
crypto,
|
|
1374
|
+
normalizeJid,
|
|
1375
|
+
getMessageType,
|
|
1376
|
+
extractMessageText,
|
|
1377
|
+
unwrapMessage,
|
|
1378
|
+
getContextInfo,
|
|
1379
|
+
getQuotedMessage,
|
|
1380
|
+
createMessageContext,
|
|
1381
|
+
getPluginCommands,
|
|
1382
|
+
detectSession,
|
|
1383
|
+
connectionState,
|
|
1384
|
+
sessionType,
|
|
1385
|
+
sessionPath,
|
|
1386
|
+
reconnectAttempts,
|
|
1387
|
+
currentWaVersion,
|
|
1388
|
+
messageCount,
|
|
1389
|
+
lastDisconnect,
|
|
1390
|
+
ownerr: global.ownerr,
|
|
1391
|
+
shutdown,
|
|
1392
|
+
connectWhatsApp,
|
|
1393
|
+
ACTIVE_SESSION_DIR,
|
|
1394
|
+
debug,
|
|
1395
|
+
info,
|
|
1396
|
+
warn,
|
|
1397
|
+
errlog,
|
|
1398
|
+
exec
|
|
1399
|
+
});
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
errlog(
|
|
1402
|
+
`Error menjalankan "${command}"`,
|
|
1403
|
+
error
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// ------------------------------------------------------------
|
|
1409
|
+
// CONNECT
|
|
1410
|
+
// ------------------------------------------------------------
|
|
1411
|
+
|
|
1412
|
+
const SESSIONS_ROOT = path.resolve('./session');
|
|
1413
|
+
|
|
1414
|
+
async function selectOrCreateSession() {
|
|
1415
|
+
if (!fs.existsSync(SESSIONS_ROOT)) {
|
|
1416
|
+
fs.mkdirSync(SESSIONS_ROOT, { recursive: true });
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
const existingDirs = fs.readdirSync(SESSIONS_ROOT, { withFileTypes: true })
|
|
1420
|
+
.filter(dirent => dirent.isDirectory())
|
|
1421
|
+
.map(dirent => dirent.name);
|
|
1422
|
+
|
|
1423
|
+
const choices = existingDirs.map(dirName => ({
|
|
1424
|
+
title: `📂 ${dirName}`,
|
|
1425
|
+
value: dirName
|
|
1426
|
+
}));
|
|
1427
|
+
|
|
1428
|
+
choices.unshift({
|
|
1429
|
+
title: '➕ [Add New Session]',
|
|
1430
|
+
value: 'NEW_SESSION'
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
const response = await prompts({
|
|
1434
|
+
type: 'select',
|
|
1435
|
+
name: 'selectedSession',
|
|
1436
|
+
message: 'Pilih session yang ingin digunakan:',
|
|
1437
|
+
choices: choices,
|
|
1438
|
+
initial: 0
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
if (!response.selectedSession) {
|
|
1442
|
+
console.log('\nOperasi dibatalkan.');
|
|
1443
|
+
process.exit(0);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
let targetSessionFolder = response.selectedSession;
|
|
1447
|
+
|
|
1448
|
+
if (targetSessionFolder === 'NEW_SESSION') {
|
|
1449
|
+
const nameInput = await prompts({
|
|
1450
|
+
type: 'text',
|
|
1451
|
+
name: 'sessionName',
|
|
1452
|
+
message: 'Please add name of this session:'
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
const rawName = nameInput.sessionName?.trim();
|
|
1456
|
+
|
|
1457
|
+
if (!rawName) {
|
|
1458
|
+
targetSessionFolder = `session-62xx-${Date.now().toString().slice(-4)}`;
|
|
1459
|
+
info(`Nama kosong, session dinamai otomatis: ${targetSessionFolder}`);
|
|
1460
|
+
} else {
|
|
1461
|
+
targetSessionFolder = `session-${rawName.replace(/\s+/g, '-').toLowerCase()}`;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
const fullPath = path.join(SESSIONS_ROOT, targetSessionFolder);
|
|
1465
|
+
if (!fs.existsSync(fullPath)) {
|
|
1466
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
return path.join(SESSIONS_ROOT, targetSessionFolder);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
async function connectWhatsApp(targetSessionDir = SESSION_DIR) {
|
|
1475
|
+
|
|
1476
|
+
|
|
1477
|
+
if (
|
|
1478
|
+
shuttingDown
|
|
1479
|
+
) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
connectionState =
|
|
1484
|
+
'connecting';
|
|
1485
|
+
|
|
1486
|
+
console.log('');
|
|
1487
|
+
console.log(
|
|
1488
|
+
'\x1b[36m=========================================='
|
|
1489
|
+
);
|
|
1490
|
+
console.log(
|
|
1491
|
+
' WHATSAPP SMART CONNECTION'
|
|
1492
|
+
);
|
|
1493
|
+
console.log(
|
|
1494
|
+
'==========================================\x1b[0m'
|
|
1495
|
+
);
|
|
1496
|
+
|
|
1497
|
+
debug(
|
|
1498
|
+
'Node',
|
|
1499
|
+
process.version
|
|
1500
|
+
);
|
|
1501
|
+
|
|
1502
|
+
debug(
|
|
1503
|
+
'Platform',
|
|
1504
|
+
process.platform
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
debug(
|
|
1508
|
+
'Architecture',
|
|
1509
|
+
process.arch
|
|
1510
|
+
);
|
|
1511
|
+
|
|
1512
|
+
debug(
|
|
1513
|
+
'CWD',
|
|
1514
|
+
process.cwd()
|
|
1515
|
+
);
|
|
1516
|
+
|
|
1517
|
+
debug(
|
|
1518
|
+
'Session directory',
|
|
1519
|
+
SESSION_DIR
|
|
1520
|
+
);
|
|
1521
|
+
|
|
1522
|
+
// --------------------------------------------------------
|
|
1523
|
+
// DETECT SESSION
|
|
1524
|
+
// --------------------------------------------------------
|
|
1525
|
+
|
|
1526
|
+
const detected =
|
|
1527
|
+
detectSession(targetSessionDir);
|
|
1528
|
+
|
|
1529
|
+
sessionType =
|
|
1530
|
+
detected.type;
|
|
1531
|
+
|
|
1532
|
+
sessionPath =
|
|
1533
|
+
detected.path;
|
|
1534
|
+
|
|
1535
|
+
console.log('');
|
|
1536
|
+
console.log(
|
|
1537
|
+
`[SESSION] Type : ${sessionType}`
|
|
1538
|
+
);
|
|
1539
|
+
|
|
1540
|
+
console.log(
|
|
1541
|
+
`[SESSION] Path : ${
|
|
1542
|
+
sessionPath ?? '-'
|
|
1543
|
+
}`
|
|
1544
|
+
);
|
|
1545
|
+
|
|
1546
|
+
console.log(
|
|
1547
|
+
`[SESSION] Registered : ${
|
|
1548
|
+
detected.registered
|
|
1549
|
+
}`
|
|
1550
|
+
);
|
|
1551
|
+
|
|
1552
|
+
debug(
|
|
1553
|
+
'Session detection',
|
|
1554
|
+
detected
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
if (
|
|
1558
|
+
!detected.registered
|
|
1559
|
+
) {
|
|
1560
|
+
loginMethod =
|
|
1561
|
+
await chooseLoginMethod();
|
|
1562
|
+
|
|
1563
|
+
console.log('');
|
|
1564
|
+
|
|
1565
|
+
info(
|
|
1566
|
+
`Login method: ${
|
|
1567
|
+
loginMethod === 'pairing'
|
|
1568
|
+
? 'PAIRING CODE'
|
|
1569
|
+
: 'QR CODE'
|
|
1570
|
+
}`
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// --------------------------------------------------------
|
|
1575
|
+
// AUTH
|
|
1576
|
+
// --------------------------------------------------------
|
|
1577
|
+
|
|
1578
|
+
let authState;
|
|
1579
|
+
let saveCreds;
|
|
1580
|
+
let closeAuth = null;
|
|
1581
|
+
|
|
1582
|
+
if (sessionType === 'sqlite') {
|
|
1583
|
+
console.log('[SESSION] Menggunakan SQLite auth.');
|
|
1584
|
+
const sqlite = await useSQLiteAuthState(sessionPath);
|
|
1585
|
+
authState = sqlite.state;
|
|
1586
|
+
saveCreds = sqlite.saveCreds;
|
|
1587
|
+
closeAuth = sqlite.close;
|
|
1588
|
+
} else if (sessionType === 'multifile') {
|
|
1589
|
+
console.log('[SESSION] Menggunakan MultiFile auth.');
|
|
1590
|
+
const multi = await useMultiFileAuthState(targetSessionDir);
|
|
1591
|
+
authState = multi.state;
|
|
1592
|
+
saveCreds = multi.saveCreds;
|
|
1593
|
+
} else {
|
|
1594
|
+
console.log('[SESSION] Belum menemukan auth session.');
|
|
1595
|
+
console.log('[SESSION] Membuat session MultiFile baru.');
|
|
1596
|
+
|
|
1597
|
+
if (typeof useMultiFileAuthState !== 'function') {
|
|
1598
|
+
throw new Error('useMultiFileAuthState tidak tersedia pada fork ini.');
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const multi = await useMultiFileAuthState(targetSessionDir);
|
|
1602
|
+
authState = multi.state;
|
|
1603
|
+
saveCreds = multi.saveCreds;
|
|
1604
|
+
|
|
1605
|
+
sessionType = 'multifile';
|
|
1606
|
+
sessionPath = targetSessionDir;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
debug(
|
|
1610
|
+
'Credentials registered',
|
|
1611
|
+
authState?.creds?.registered
|
|
1612
|
+
);
|
|
1613
|
+
|
|
1614
|
+
// --------------------------------------------------------
|
|
1615
|
+
// WA WEB VERSION
|
|
1616
|
+
// --------------------------------------------------------
|
|
1617
|
+
|
|
1618
|
+
const version =
|
|
1619
|
+
await getLatestVersion();
|
|
1620
|
+
|
|
1621
|
+
// --------------------------------------------------------
|
|
1622
|
+
// SOCKET CONFIG
|
|
1623
|
+
// --------------------------------------------------------
|
|
1624
|
+
|
|
1625
|
+
const config = {
|
|
1626
|
+
auth: {
|
|
1627
|
+
creds:
|
|
1628
|
+
authState.creds,
|
|
1629
|
+
|
|
1630
|
+
keys:
|
|
1631
|
+
typeof makeCacheableSignalKeyStore ===
|
|
1632
|
+
'function'
|
|
1633
|
+
? makeCacheableSignalKeyStore(
|
|
1634
|
+
authState.keys,
|
|
1635
|
+
pinoLogger()
|
|
1636
|
+
)
|
|
1637
|
+
: authState.keys
|
|
1638
|
+
},
|
|
1639
|
+
|
|
1640
|
+
logger:
|
|
1641
|
+
pinoLogger(),
|
|
1642
|
+
|
|
1643
|
+
browser:
|
|
1644
|
+
typeof Browsers?.macOS === 'function'
|
|
1645
|
+
? Browsers.macOS('Safari')
|
|
1646
|
+
: [
|
|
1647
|
+
'Mac OS',
|
|
1648
|
+
'Safari',
|
|
1649
|
+
'14.4.1'
|
|
1650
|
+
]
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
if (version) {
|
|
1654
|
+
config.version =
|
|
1655
|
+
version;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
debug(
|
|
1659
|
+
'Socket config',
|
|
1660
|
+
{
|
|
1661
|
+
version:
|
|
1662
|
+
config.version,
|
|
1663
|
+
|
|
1664
|
+
browser:
|
|
1665
|
+
config.browser,
|
|
1666
|
+
|
|
1667
|
+
sessionType,
|
|
1668
|
+
|
|
1669
|
+
registered:
|
|
1670
|
+
authState?.creds?.registered
|
|
1671
|
+
}
|
|
1672
|
+
);
|
|
1673
|
+
|
|
1674
|
+
// --------------------------------------------------------
|
|
1675
|
+
// SOCKET
|
|
1676
|
+
// --------------------------------------------------------
|
|
1677
|
+
|
|
1678
|
+
sock =
|
|
1679
|
+
makeWASocket(
|
|
1680
|
+
config
|
|
1681
|
+
);
|
|
1682
|
+
|
|
1683
|
+
debug(
|
|
1684
|
+
'Socket berhasil dibuat'
|
|
1685
|
+
);
|
|
1686
|
+
|
|
1687
|
+
if (
|
|
1688
|
+
!authState.creds.registered &&
|
|
1689
|
+
loginMethod === 'pairing'
|
|
1690
|
+
) {
|
|
1691
|
+
const phone = await new Promise(resolve => {
|
|
1692
|
+
// Matikan sementara handler terminal
|
|
1693
|
+
rl.pause();
|
|
1694
|
+
|
|
1695
|
+
const tempRl = readline.createInterface({
|
|
1696
|
+
input: process.stdin,
|
|
1697
|
+
output: process.stdout,
|
|
1698
|
+
terminal: true
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
tempRl.question(
|
|
1702
|
+
'Masukkan nomor WhatsApp (contoh 628123456789): ',
|
|
1703
|
+
answer => {
|
|
1704
|
+
tempRl.close();
|
|
1705
|
+
rl.resume();
|
|
1706
|
+
resolve(answer);
|
|
1707
|
+
}
|
|
1708
|
+
);
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
const number = phone.replace(/\D/g, '');
|
|
1712
|
+
|
|
1713
|
+
if (!number) {
|
|
1714
|
+
warn('Nomor tidak valid.');
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
try {
|
|
1719
|
+
info('Menunggu socket siap...');
|
|
1720
|
+
|
|
1721
|
+
await new Promise(resolve =>
|
|
1722
|
+
setTimeout(resolve, 6000)
|
|
1723
|
+
);
|
|
1724
|
+
|
|
1725
|
+
info('Meminta pairing code...');
|
|
1726
|
+
|
|
1727
|
+
const customCode = 'VINZZOFC';
|
|
1728
|
+
|
|
1729
|
+
const code =
|
|
1730
|
+
await sock.requestPairingCode(
|
|
1731
|
+
number,
|
|
1732
|
+
customCode
|
|
1733
|
+
);
|
|
1734
|
+
|
|
1735
|
+
console.log('');
|
|
1736
|
+
console.log(
|
|
1737
|
+
`🔑 PAIRING CODE: ${code}`
|
|
1738
|
+
);
|
|
1739
|
+
console.log('');
|
|
1740
|
+
info('Masukkan pairing code tersebut di WhatsApp.');
|
|
1741
|
+
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
errlog(
|
|
1744
|
+
'Gagal meminta pairing code',
|
|
1745
|
+
error
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// --------------------------------------------------------
|
|
1751
|
+
// CREDENTIALS
|
|
1752
|
+
// --------------------------------------------------------
|
|
1753
|
+
|
|
1754
|
+
sock.ev.on(
|
|
1755
|
+
'creds.update',
|
|
1756
|
+
async creds => {
|
|
1757
|
+
|
|
1758
|
+
try {
|
|
1759
|
+
|
|
1760
|
+
debug(
|
|
1761
|
+
'creds.update'
|
|
1762
|
+
);
|
|
1763
|
+
|
|
1764
|
+
// Untuk state auth yang digunakan socket,
|
|
1765
|
+
// Baileys sudah memutasi authState.creds.
|
|
1766
|
+
await saveCreds(
|
|
1767
|
+
creds
|
|
1768
|
+
);
|
|
1769
|
+
|
|
1770
|
+
} catch (error) {
|
|
1771
|
+
|
|
1772
|
+
errlog(
|
|
1773
|
+
'Gagal menyimpan credentials',
|
|
1774
|
+
error
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
);
|
|
1779
|
+
|
|
1780
|
+
// --------------------------------------------------------
|
|
1781
|
+
// CONNECTION UPDATE
|
|
1782
|
+
// --------------------------------------------------------
|
|
1783
|
+
|
|
1784
|
+
sock.ev.on(
|
|
1785
|
+
'connection.update',
|
|
1786
|
+
async update => {
|
|
1787
|
+
|
|
1788
|
+
lastConnectionUpdate =
|
|
1789
|
+
update;
|
|
1790
|
+
|
|
1791
|
+
debug(
|
|
1792
|
+
'connection.update',
|
|
1793
|
+
update
|
|
1794
|
+
);
|
|
1795
|
+
|
|
1796
|
+
const {
|
|
1797
|
+
connection,
|
|
1798
|
+
lastDisconnect,
|
|
1799
|
+
qr,
|
|
1800
|
+
isNewLogin,
|
|
1801
|
+
receivedPendingNotifications
|
|
1802
|
+
} = update;
|
|
1803
|
+
|
|
1804
|
+
if (
|
|
1805
|
+
connection
|
|
1806
|
+
) {
|
|
1807
|
+
connectionState =
|
|
1808
|
+
connection;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// ----------------------------
|
|
1812
|
+
// QR
|
|
1813
|
+
// ----------------------------
|
|
1814
|
+
|
|
1815
|
+
if (qr) {
|
|
1816
|
+
|
|
1817
|
+
if (loginMethod === 'qr') {
|
|
1818
|
+
|
|
1819
|
+
console.log('');
|
|
1820
|
+
console.log(
|
|
1821
|
+
'\x1b[33m📱 Scan QR WhatsApp ini:\x1b[0m'
|
|
1822
|
+
);
|
|
1823
|
+
|
|
1824
|
+
await renderQR(qr);
|
|
1825
|
+
|
|
1826
|
+
} else {
|
|
1827
|
+
|
|
1828
|
+
debug(
|
|
1829
|
+
'QR diabaikan karena login method Pairing Code'
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// ----------------------------
|
|
1835
|
+
// CONNECTING
|
|
1836
|
+
// ----------------------------
|
|
1837
|
+
|
|
1838
|
+
if (
|
|
1839
|
+
connection ===
|
|
1840
|
+
'connecting'
|
|
1841
|
+
) {
|
|
1842
|
+
|
|
1843
|
+
warn(
|
|
1844
|
+
'Sedang menghubungkan...'
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// ----------------------------
|
|
1849
|
+
// OPEN
|
|
1850
|
+
// ----------------------------
|
|
1851
|
+
|
|
1852
|
+
if (
|
|
1853
|
+
connection ===
|
|
1854
|
+
'open'
|
|
1855
|
+
) {
|
|
1856
|
+
|
|
1857
|
+
reconnectAttempts =
|
|
1858
|
+
0;
|
|
1859
|
+
|
|
1860
|
+
connectionState =
|
|
1861
|
+
'open';
|
|
1862
|
+
|
|
1863
|
+
lastDisconnectInfo =
|
|
1864
|
+
null;
|
|
1865
|
+
|
|
1866
|
+
info(
|
|
1867
|
+
'BERHASIL TERHUBUNG KE WHATSAPP!'
|
|
1868
|
+
);
|
|
1869
|
+
|
|
1870
|
+
console.log('');
|
|
1871
|
+
|
|
1872
|
+
if (
|
|
1873
|
+
sock?.user
|
|
1874
|
+
) {
|
|
1875
|
+
|
|
1876
|
+
info(
|
|
1877
|
+
`Account: ${
|
|
1878
|
+
sock.user.id ??
|
|
1879
|
+
'-'
|
|
1880
|
+
}`
|
|
1881
|
+
);
|
|
1882
|
+
|
|
1883
|
+
if (
|
|
1884
|
+
sock.user.name
|
|
1885
|
+
) {
|
|
1886
|
+
info(
|
|
1887
|
+
`Name: ${
|
|
1888
|
+
sock.user.name
|
|
1889
|
+
}`
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
debug(
|
|
1895
|
+
'Pending notifications',
|
|
1896
|
+
receivedPendingNotifications
|
|
1897
|
+
);
|
|
1898
|
+
|
|
1899
|
+
prompt();
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// ----------------------------
|
|
1903
|
+
// CLOSE
|
|
1904
|
+
// ----------------------------
|
|
1905
|
+
|
|
1906
|
+
if (
|
|
1907
|
+
connection ===
|
|
1908
|
+
'close'
|
|
1909
|
+
) {
|
|
1910
|
+
|
|
1911
|
+
const disconnect =
|
|
1912
|
+
parseDisconnect(
|
|
1913
|
+
lastDisconnect
|
|
1914
|
+
);
|
|
1915
|
+
|
|
1916
|
+
lastDisconnectInfo =
|
|
1917
|
+
disconnect;
|
|
1918
|
+
|
|
1919
|
+
connectionState =
|
|
1920
|
+
'closed';
|
|
1921
|
+
|
|
1922
|
+
terminalStarted =
|
|
1923
|
+
false;
|
|
1924
|
+
|
|
1925
|
+
console.log('');
|
|
1926
|
+
console.log(
|
|
1927
|
+
'\x1b[31m========== CONNECTION CLOSED ==========\x1b[0m'
|
|
1928
|
+
);
|
|
1929
|
+
|
|
1930
|
+
debug(
|
|
1931
|
+
'Disconnect info',
|
|
1932
|
+
disconnect
|
|
1933
|
+
);
|
|
1934
|
+
|
|
1935
|
+
warn(
|
|
1936
|
+
`Disconnect: ${
|
|
1937
|
+
disconnect.message
|
|
1938
|
+
}`
|
|
1939
|
+
);
|
|
1940
|
+
|
|
1941
|
+
if (
|
|
1942
|
+
disconnect.statusCode !==
|
|
1943
|
+
null
|
|
1944
|
+
) {
|
|
1945
|
+
warn(
|
|
1946
|
+
`Status code: ${
|
|
1947
|
+
disconnect.statusCode
|
|
1948
|
+
}`
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
if (
|
|
1953
|
+
disconnect.reason
|
|
1954
|
+
) {
|
|
1955
|
+
warn(
|
|
1956
|
+
`Reason: ${
|
|
1957
|
+
disconnect.reason
|
|
1958
|
+
}`
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
if (
|
|
1963
|
+
disconnect.location
|
|
1964
|
+
) {
|
|
1965
|
+
warn(
|
|
1966
|
+
`Location: ${
|
|
1967
|
+
disconnect.location
|
|
1968
|
+
}`
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// Tutup DB handle kalau SQLite
|
|
1973
|
+
try {
|
|
1974
|
+
closeAuth?.();
|
|
1975
|
+
} catch {}
|
|
1976
|
+
|
|
1977
|
+
sock =
|
|
1978
|
+
null;
|
|
1979
|
+
|
|
1980
|
+
if (!shuttingDown) {
|
|
1981
|
+
if (disconnect.statusCode === 401) {
|
|
1982
|
+
warn('Login ditolak / session tidak valid.');
|
|
1983
|
+
warn('Hapus session lalu jalankan ulang.');
|
|
1984
|
+
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
scheduleReconnect();
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
);
|
|
1993
|
+
|
|
1994
|
+
// --------------------------------------------------------
|
|
1995
|
+
// INCOMING MESSAGES
|
|
1996
|
+
// --------------------------------------------------------
|
|
1997
|
+
|
|
1998
|
+
sock.ev.on(
|
|
1999
|
+
'messages.upsert',
|
|
2000
|
+
async ({ messages, type }) => {
|
|
2001
|
+
|
|
2002
|
+
if (!Array.isArray(messages)) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
for (
|
|
2007
|
+
const message
|
|
2008
|
+
of messages
|
|
2009
|
+
) {
|
|
2010
|
+
|
|
2011
|
+
if (!message) {
|
|
2012
|
+
continue;
|
|
2013
|
+
}
|
|
2014
|
+
saveMessage(message);
|
|
2015
|
+
const key =
|
|
2016
|
+
message.key;
|
|
2017
|
+
|
|
2018
|
+
const remoteJid =
|
|
2019
|
+
key?.remoteJid ??
|
|
2020
|
+
null;
|
|
2021
|
+
|
|
2022
|
+
const sender =
|
|
2023
|
+
key?.participant ??
|
|
2024
|
+
remoteJid ??
|
|
2025
|
+
null;
|
|
2026
|
+
|
|
2027
|
+
const fromMe =
|
|
2028
|
+
key?.fromMe === true;
|
|
2029
|
+
|
|
2030
|
+
const isGroup =
|
|
2031
|
+
typeof remoteJid ===
|
|
2032
|
+
'string' &&
|
|
2033
|
+
remoteJid.endsWith(
|
|
2034
|
+
'@g.us'
|
|
2035
|
+
);
|
|
2036
|
+
|
|
2037
|
+
const status =
|
|
2038
|
+
isGroup
|
|
2039
|
+
? 'GROUP'
|
|
2040
|
+
: 'PRIVATE';
|
|
2041
|
+
|
|
2042
|
+
const text =
|
|
2043
|
+
extractMessageText(
|
|
2044
|
+
message.message
|
|
2045
|
+
) ||
|
|
2046
|
+
null;
|
|
2047
|
+
|
|
2048
|
+
const messageType =
|
|
2049
|
+
getMessageType(
|
|
2050
|
+
message.message
|
|
2051
|
+
) ??
|
|
2052
|
+
'unknown';
|
|
2053
|
+
|
|
2054
|
+
const senderName =
|
|
2055
|
+
message.pushName ??
|
|
2056
|
+
null;
|
|
2057
|
+
|
|
2058
|
+
let groupName =
|
|
2059
|
+
null;
|
|
2060
|
+
|
|
2061
|
+
// Ambil nama grup kalau pesan dari grup
|
|
2062
|
+
if (isGroup) {
|
|
2063
|
+
|
|
2064
|
+
try {
|
|
2065
|
+
|
|
2066
|
+
const metadata =
|
|
2067
|
+
await sock.groupMetadata(
|
|
2068
|
+
remoteJid
|
|
2069
|
+
);
|
|
2070
|
+
|
|
2071
|
+
groupName =
|
|
2072
|
+
metadata?.subject ??
|
|
2073
|
+
null;
|
|
2074
|
+
|
|
2075
|
+
} catch {
|
|
2076
|
+
|
|
2077
|
+
groupName =
|
|
2078
|
+
null;
|
|
2079
|
+
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// Jangan tampilkan message kosong
|
|
2084
|
+
if (
|
|
2085
|
+
!message.message
|
|
2086
|
+
) {
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
messageCount++;
|
|
2091
|
+
|
|
2092
|
+
console.log('');
|
|
2093
|
+
console.log(
|
|
2094
|
+
'\x1b[35m╔══════════════════════════════════════════════════╗\x1b[0m'
|
|
2095
|
+
);
|
|
2096
|
+
|
|
2097
|
+
console.log(
|
|
2098
|
+
'\x1b[35m║ 📩 PESAN MASUK BARU ║\x1b[0m'
|
|
2099
|
+
);
|
|
2100
|
+
|
|
2101
|
+
console.log(
|
|
2102
|
+
'\x1b[35m╚══════════════════════════════════════════════════╝\x1b[0m'
|
|
2103
|
+
);
|
|
2104
|
+
|
|
2105
|
+
console.log('');
|
|
2106
|
+
|
|
2107
|
+
console.log(
|
|
2108
|
+
`📌 STATUS : ${
|
|
2109
|
+
status
|
|
2110
|
+
}`
|
|
2111
|
+
);
|
|
2112
|
+
|
|
2113
|
+
console.log(
|
|
2114
|
+
`📨 EVENT : ${
|
|
2115
|
+
type ??
|
|
2116
|
+
'unknown'
|
|
2117
|
+
}`
|
|
2118
|
+
);
|
|
2119
|
+
|
|
2120
|
+
console.log(
|
|
2121
|
+
`👤 PENGIRIM : ${
|
|
2122
|
+
sender ??
|
|
2123
|
+
'null'
|
|
2124
|
+
}`
|
|
2125
|
+
);
|
|
2126
|
+
|
|
2127
|
+
console.log(
|
|
2128
|
+
`📛 NAMA : ${
|
|
2129
|
+
senderName ??
|
|
2130
|
+
'null'
|
|
2131
|
+
}`
|
|
2132
|
+
);
|
|
2133
|
+
|
|
2134
|
+
console.log(
|
|
2135
|
+
`💬 CHAT : ${
|
|
2136
|
+
remoteJid ??
|
|
2137
|
+
'null'
|
|
2138
|
+
}`
|
|
2139
|
+
);
|
|
2140
|
+
|
|
2141
|
+
console.log(
|
|
2142
|
+
`👥 GRUP : ${
|
|
2143
|
+
groupName ??
|
|
2144
|
+
'null'
|
|
2145
|
+
}`
|
|
2146
|
+
);
|
|
2147
|
+
|
|
2148
|
+
console.log(
|
|
2149
|
+
`📦 JENIS PESAN : ${
|
|
2150
|
+
messageType
|
|
2151
|
+
}`
|
|
2152
|
+
);
|
|
2153
|
+
|
|
2154
|
+
console.log(
|
|
2155
|
+
`↩️ DARI SAYA : ${
|
|
2156
|
+
fromMe
|
|
2157
|
+
}`
|
|
2158
|
+
);
|
|
2159
|
+
|
|
2160
|
+
console.log('');
|
|
2161
|
+
|
|
2162
|
+
console.log(
|
|
2163
|
+
'📝 ISI PESAN'
|
|
2164
|
+
);
|
|
2165
|
+
|
|
2166
|
+
console.log(
|
|
2167
|
+
'──────────────────────────────────────────────────'
|
|
2168
|
+
);
|
|
2169
|
+
|
|
2170
|
+
console.log(
|
|
2171
|
+
text ??
|
|
2172
|
+
'[Tidak ada teks]'
|
|
2173
|
+
);
|
|
2174
|
+
|
|
2175
|
+
console.log(
|
|
2176
|
+
'──────────────────────────────────────────────────'
|
|
2177
|
+
);
|
|
2178
|
+
|
|
2179
|
+
console.log('');
|
|
2180
|
+
|
|
2181
|
+
console.log(
|
|
2182
|
+
`🆔 MESSAGE ID : ${
|
|
2183
|
+
key?.id ??
|
|
2184
|
+
'null'
|
|
2185
|
+
}`
|
|
2186
|
+
);
|
|
2187
|
+
|
|
2188
|
+
console.log(
|
|
2189
|
+
`🕒 WAKTU : ${
|
|
2190
|
+
message.messageTimestamp
|
|
2191
|
+
? new Date(
|
|
2192
|
+
Number(
|
|
2193
|
+
message.messageTimestamp
|
|
2194
|
+
) * 1000
|
|
2195
|
+
).toISOString()
|
|
2196
|
+
: 'null'
|
|
2197
|
+
}`
|
|
2198
|
+
);
|
|
2199
|
+
|
|
2200
|
+
console.log('');
|
|
2201
|
+
|
|
2202
|
+
console.log(
|
|
2203
|
+
'\x1b[35m════════════════════════════════════════════════════\x1b[0m'
|
|
2204
|
+
);
|
|
2205
|
+
|
|
2206
|
+
console.log('');
|
|
2207
|
+
|
|
2208
|
+
const m = createMessageContext(message);
|
|
2209
|
+
const commandInput = getCommandInputFromMessage(m);
|
|
2210
|
+
|
|
2211
|
+
if (commandInput && m.isOwner) {
|
|
2212
|
+
try {
|
|
2213
|
+
await handleCommand(commandInput, m);
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
errlog('WA command gagal', error);
|
|
2216
|
+
try {
|
|
2217
|
+
await m.reply({ text: `Error: ${error?.message || error}` });
|
|
2218
|
+
} catch {}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
);
|
|
2224
|
+
|
|
2225
|
+
// --------------------------------------------------------
|
|
2226
|
+
// GROUP / PRESENCE DEBUG
|
|
2227
|
+
// --------------------------------------------------------
|
|
2228
|
+
|
|
2229
|
+
sock.ev.on(
|
|
2230
|
+
'presence.update',
|
|
2231
|
+
update => {
|
|
2232
|
+
debug(
|
|
2233
|
+
'presence.update',
|
|
2234
|
+
update
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
);
|
|
2238
|
+
|
|
2239
|
+
if (
|
|
2240
|
+
typeof closeAuth ===
|
|
2241
|
+
'function'
|
|
2242
|
+
) {
|
|
2243
|
+
// closeAuth dikelola saat connection close
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// ------------------------------------------------------------
|
|
2248
|
+
// Pino lazy logger
|
|
2249
|
+
// ------------------------------------------------------------
|
|
2250
|
+
|
|
2251
|
+
function pinoLogger() {
|
|
2252
|
+
|
|
2253
|
+
// Import synchronous sudah tersedia lewat dynamic import?
|
|
2254
|
+
// Pino belum di-import di atas supaya script fleksibel.
|
|
2255
|
+
// Gunakan require tidak bisa pada ESM, jadi ambil dari cache
|
|
2256
|
+
// menggunakan createRequire.
|
|
2257
|
+
return loggerInstance;
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
let loggerInstance;
|
|
2261
|
+
|
|
2262
|
+
{
|
|
2263
|
+
const {
|
|
2264
|
+
createRequire
|
|
2265
|
+
} = await import(
|
|
2266
|
+
'module'
|
|
2267
|
+
);
|
|
2268
|
+
|
|
2269
|
+
const require =
|
|
2270
|
+
createRequire(
|
|
2271
|
+
import.meta.url
|
|
2272
|
+
);
|
|
2273
|
+
|
|
2274
|
+
const pino =
|
|
2275
|
+
require('pino');
|
|
2276
|
+
|
|
2277
|
+
loggerInstance =
|
|
2278
|
+
pino({
|
|
2279
|
+
level: LOG_LEVEL
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// ------------------------------------------------------------
|
|
2284
|
+
// SHUTDOWN
|
|
2285
|
+
// ------------------------------------------------------------
|
|
2286
|
+
|
|
2287
|
+
async function shutdown() {
|
|
2288
|
+
|
|
2289
|
+
if (
|
|
2290
|
+
shuttingDown
|
|
2291
|
+
) {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
shuttingDown =
|
|
2296
|
+
true;
|
|
2297
|
+
|
|
2298
|
+
console.log('');
|
|
2299
|
+
warn(
|
|
2300
|
+
'Shutdown dimulai...'
|
|
2301
|
+
);
|
|
2302
|
+
|
|
2303
|
+
if (
|
|
2304
|
+
reconnectTimer
|
|
2305
|
+
) {
|
|
2306
|
+
clearTimeout(
|
|
2307
|
+
reconnectTimer
|
|
2308
|
+
);
|
|
2309
|
+
|
|
2310
|
+
reconnectTimer =
|
|
2311
|
+
null;
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
try {
|
|
2315
|
+
if (sock) {
|
|
2316
|
+
sock.end();
|
|
2317
|
+
}
|
|
2318
|
+
} catch {}
|
|
2319
|
+
|
|
2320
|
+
try {
|
|
2321
|
+
rl.close();
|
|
2322
|
+
} catch {}
|
|
2323
|
+
|
|
2324
|
+
info(
|
|
2325
|
+
'Bot dihentikan.'
|
|
2326
|
+
);
|
|
2327
|
+
|
|
2328
|
+
process.exit(0);
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// ------------------------------------------------------------
|
|
2332
|
+
// GLOBAL ERRORS
|
|
2333
|
+
// ------------------------------------------------------------
|
|
2334
|
+
|
|
2335
|
+
process.on(
|
|
2336
|
+
'uncaughtException',
|
|
2337
|
+
error => {
|
|
2338
|
+
|
|
2339
|
+
errlog(
|
|
2340
|
+
'UNCAUGHT EXCEPTION',
|
|
2341
|
+
error
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
);
|
|
2345
|
+
|
|
2346
|
+
process.on(
|
|
2347
|
+
'unhandledRejection',
|
|
2348
|
+
reason => {
|
|
2349
|
+
|
|
2350
|
+
errlog(
|
|
2351
|
+
'UNHANDLED REJECTION',
|
|
2352
|
+
reason
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
2355
|
+
);
|
|
2356
|
+
|
|
2357
|
+
process.on(
|
|
2358
|
+
'SIGINT',
|
|
2359
|
+
async () => {
|
|
2360
|
+
await shutdown();
|
|
2361
|
+
}
|
|
2362
|
+
);
|
|
2363
|
+
|
|
2364
|
+
process.on(
|
|
2365
|
+
'SIGTERM',
|
|
2366
|
+
async () => {
|
|
2367
|
+
await shutdown();
|
|
2368
|
+
}
|
|
2369
|
+
);
|
|
2370
|
+
|
|
2371
|
+
// ------------------------------------------------------------
|
|
2372
|
+
// START
|
|
2373
|
+
// ------------------------------------------------------------
|
|
2374
|
+
|
|
2375
|
+
console.clear();
|
|
2376
|
+
|
|
2377
|
+
console.log(
|
|
2378
|
+
'\x1b[36m=========================================='
|
|
2379
|
+
);
|
|
2380
|
+
|
|
2381
|
+
console.log(
|
|
2382
|
+
' SMART WHATSAPP CLI'
|
|
2383
|
+
);
|
|
2384
|
+
|
|
2385
|
+
console.log(
|
|
2386
|
+
'==========================================\x1b[0m'
|
|
2387
|
+
);
|
|
2388
|
+
|
|
2389
|
+
console.log('');
|
|
2390
|
+
|
|
2391
|
+
let ACTIVE_SESSION_DIR = null;
|
|
2392
|
+
|
|
2393
|
+
async function startApp() {
|
|
2394
|
+
console.clear();
|
|
2395
|
+
console.log('\x1b[36m==========================================');
|
|
2396
|
+
console.log(' SMART WHATSAPP CLI');
|
|
2397
|
+
console.log('==========================================\x1b[0m\n');
|
|
2398
|
+
|
|
2399
|
+
// 1. Pilih session saat readline CLI belum aktif (Joystick/Panah Lancar)
|
|
2400
|
+
ACTIVE_SESSION_DIR = await selectOrCreateSession();
|
|
2401
|
+
|
|
2402
|
+
// 2. Inisialisasi Readline & Prompt HIJAU setelah selection selesai
|
|
2403
|
+
initReadline();
|
|
2404
|
+
|
|
2405
|
+
info(`Session aktif: ${path.basename(ACTIVE_SESSION_DIR)}`);
|
|
2406
|
+
console.log('');
|
|
2407
|
+
|
|
2408
|
+
// 3. Connect ke WhatsApp
|
|
2409
|
+
await connectWhatsApp(ACTIVE_SESSION_DIR);
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
await loadPlugins({ info, errlog });
|
|
2413
|
+
watchPlugins({ info, errlog });
|
|
2414
|
+
startApp();
|
|
2415
|
+
|
|
2416
|
+
export { createMessageContext, getQuotedMessage, handleCommand, messageStore };
|