@shoru/kitten 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/package.json +7 -5
- package/src/auth/init-session.js +3 -1
- package/src/auth/lmdb-auth-state.js +63 -57
- package/src/client/client.js +240 -55
- package/src/client/getConnectionConfig.js +5 -2
- package/src/config/default.js +14 -1
- package/src/formatter/format-message.js +144 -0
- package/src/formatter/index.js +14 -0
- package/src/index.js +5 -4
- package/src/internals/config.js +7 -2
- package/src/internals/index.js +5 -4
- package/src/internals/lmdb-manager.js +3 -1
- package/src/internals/logger.js +1 -1
- package/src/internals/plugin-manager.js +479 -0
- package/src/utils/buffer-json.js +18 -3
- package/src/utils/get-pn.js +9 -0
- package/src/utils/index.js +5 -3
- package/src/utils/time-string.js +19 -0
- package/src/utils/type-conversions.js +5 -0
- package/src/utils/retry.js +0 -29
package/src/client/client.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { DisconnectReason } from
|
|
2
|
-
import { Boom } from
|
|
3
|
-
import qrcode from
|
|
4
|
-
import chalk from
|
|
5
|
-
import { logger } from
|
|
6
|
-
import { initSession } from
|
|
7
|
-
import {
|
|
8
|
-
import { getConnectionConfig } from "./getConnectionConfig.js";
|
|
1
|
+
import { DisconnectReason } from 'baileys';
|
|
2
|
+
import { Boom } from '@hapi/boom';
|
|
3
|
+
import qrcode from 'qrcode-terminal';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { logger, pino, pluginManager } from '#internals.js';
|
|
6
|
+
import { initSession, listSessions } from '#auth.js';
|
|
7
|
+
import { getConnectionConfig } from './getConnectionConfig.js';
|
|
9
8
|
|
|
10
9
|
export const ConnectionState = Object.freeze({
|
|
11
10
|
DISCONNECTED: 'disconnected',
|
|
@@ -17,33 +16,83 @@ export const ConnectionState = Object.freeze({
|
|
|
17
16
|
class ConnectionError extends Error {
|
|
18
17
|
constructor(message, { statusCode, recoverable = true } = {}) {
|
|
19
18
|
super(message);
|
|
20
|
-
this.name =
|
|
19
|
+
this.name = 'ConnectionError';
|
|
21
20
|
this.statusCode = statusCode;
|
|
22
21
|
this.recoverable = recoverable;
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
const DISCONNECT_HANDLERS = new Map([
|
|
27
|
-
[DisconnectReason.connectionClosed, { message:
|
|
28
|
-
[DisconnectReason.restartRequired, { message:
|
|
29
|
-
[DisconnectReason.timedOut, { message:
|
|
30
|
-
[DisconnectReason.connectionLost, { message:
|
|
31
|
-
[DisconnectReason.unavailableService, { message:
|
|
32
|
-
[DisconnectReason.loggedOut, { message:
|
|
33
|
-
[DisconnectReason.forbidden, { message:
|
|
34
|
-
[405, { message:
|
|
26
|
+
[DisconnectReason.connectionClosed, { message: 'Connection closed', recoverable: true }],
|
|
27
|
+
[DisconnectReason.restartRequired, { message: 'QR Scanned', recoverable: true }],
|
|
28
|
+
[DisconnectReason.timedOut, { message: 'Connection timed out', recoverable: true }],
|
|
29
|
+
[DisconnectReason.connectionLost, { message: 'Connection lost', recoverable: true }],
|
|
30
|
+
[DisconnectReason.unavailableService, { message: 'Service unavailable', recoverable: true }],
|
|
31
|
+
[DisconnectReason.loggedOut, { message: 'Session logged out', recoverable: true, deleteSession: true }],
|
|
32
|
+
[DisconnectReason.forbidden, { message: 'Account banned', recoverable: false, deleteSession: true }],
|
|
33
|
+
[405, { message: 'Not logged in', recoverable: true, deleteSession: true }],
|
|
35
34
|
]);
|
|
36
35
|
|
|
36
|
+
const silentLogger = Object.freeze({
|
|
37
|
+
trace: () => {},
|
|
38
|
+
debug: () => {},
|
|
39
|
+
info: () => {},
|
|
40
|
+
warn: () => {},
|
|
41
|
+
error: () => {},
|
|
42
|
+
fatal: () => {},
|
|
43
|
+
prompt: () => {},
|
|
44
|
+
child: () => silentLogger,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const silentPino = pino({ level: 'silent' });
|
|
48
|
+
|
|
37
49
|
export class Client {
|
|
50
|
+
static #registry = new Map();
|
|
51
|
+
|
|
52
|
+
static #isSyncing = false;
|
|
53
|
+
static #isConfiguring = false;
|
|
54
|
+
|
|
55
|
+
// Static Registry API
|
|
56
|
+
|
|
57
|
+
static get(id) {
|
|
58
|
+
return Client.#registry.get(id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static has(id) {
|
|
62
|
+
return Client.#registry.has(id);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static get size() {
|
|
66
|
+
return Client.#registry.size;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static keys() {
|
|
70
|
+
return Client.#registry.keys();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static values() {
|
|
74
|
+
return Client.#registry.values();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static entries() {
|
|
78
|
+
return Client.#registry.entries();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static [Symbol.iterator]() {
|
|
82
|
+
return Client.#registry.values();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Instance Properties
|
|
86
|
+
|
|
38
87
|
sock = null;
|
|
39
88
|
session = null;
|
|
40
89
|
id = null;
|
|
41
90
|
|
|
42
|
-
#flag =
|
|
91
|
+
#flag = '';
|
|
92
|
+
#plugins = null;
|
|
43
93
|
#qr = null;
|
|
44
94
|
#state = ConnectionState.DISCONNECTED;
|
|
45
|
-
#cancelWait
|
|
46
|
-
#isConfiguring = false;
|
|
95
|
+
#cancelWait = null;
|
|
47
96
|
#hasConnectedOnce = false;
|
|
48
97
|
|
|
49
98
|
#socketConfig = null;
|
|
@@ -58,6 +107,12 @@ export class Client {
|
|
|
58
107
|
#maxRetries;
|
|
59
108
|
#backoff;
|
|
60
109
|
|
|
110
|
+
// Options
|
|
111
|
+
#silent;
|
|
112
|
+
#sync;
|
|
113
|
+
#logger;
|
|
114
|
+
|
|
115
|
+
// Callbacks
|
|
61
116
|
#onPairing;
|
|
62
117
|
#onConnect;
|
|
63
118
|
#onReconnect;
|
|
@@ -68,19 +123,24 @@ export class Client {
|
|
|
68
123
|
const {
|
|
69
124
|
id,
|
|
70
125
|
maxRetries = 30,
|
|
71
|
-
backoff = (attempt) => Math.min(1000 *
|
|
126
|
+
backoff = (attempt) => Math.min(1000 * 2 ** (attempt - 1), 60_000),
|
|
127
|
+
silent = false,
|
|
128
|
+
sync = false,
|
|
72
129
|
onPairing = null,
|
|
73
130
|
onConnect = null,
|
|
74
131
|
onReconnect = null,
|
|
75
132
|
onDisconnect = null,
|
|
76
133
|
onStateChange = null,
|
|
77
|
-
socketConfig = {}
|
|
134
|
+
socketConfig = {},
|
|
78
135
|
} = options;
|
|
79
136
|
|
|
80
137
|
this.id = id;
|
|
81
138
|
this.#socketConfig = socketConfig;
|
|
82
139
|
this.#maxRetries = maxRetries;
|
|
83
140
|
this.#backoff = backoff;
|
|
141
|
+
this.#silent = silent;
|
|
142
|
+
this.#sync = sync;
|
|
143
|
+
this.#logger = silent ? silentLogger : logger;
|
|
84
144
|
this.#onPairing = onPairing;
|
|
85
145
|
this.#onConnect = onConnect;
|
|
86
146
|
this.#onReconnect = onReconnect;
|
|
@@ -100,6 +160,20 @@ export class Client {
|
|
|
100
160
|
return this.#reconnectAttempts;
|
|
101
161
|
}
|
|
102
162
|
|
|
163
|
+
// Registry Management
|
|
164
|
+
|
|
165
|
+
#register() {
|
|
166
|
+
if (this.id != null) {
|
|
167
|
+
Client.#registry.set(this.id, this);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#unregister() {
|
|
172
|
+
if (this.id != null) {
|
|
173
|
+
Client.#registry.delete(this.id);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
103
177
|
// State Management
|
|
104
178
|
|
|
105
179
|
#setState(newState) {
|
|
@@ -125,7 +199,7 @@ export class Client {
|
|
|
125
199
|
try {
|
|
126
200
|
callback({ ...data, client: this });
|
|
127
201
|
} catch (err) {
|
|
128
|
-
logger.error(err, `[${this.#flag}] Error in ${event} callback`);
|
|
202
|
+
this.#logger.error(err, `[${this.#flag}] Error in ${event} callback`);
|
|
129
203
|
}
|
|
130
204
|
});
|
|
131
205
|
}
|
|
@@ -151,7 +225,6 @@ export class Client {
|
|
|
151
225
|
async #initConnection() {
|
|
152
226
|
this.#setState(ConnectionState.CONNECTING);
|
|
153
227
|
this.#reconnectAttempts = 0;
|
|
154
|
-
|
|
155
228
|
this.#pendingConnect = this.#createDeferred();
|
|
156
229
|
|
|
157
230
|
try {
|
|
@@ -189,8 +262,12 @@ export class Client {
|
|
|
189
262
|
async #createSocket() {
|
|
190
263
|
this.#cleanupSocket();
|
|
191
264
|
|
|
265
|
+
const socketConfig = this.#silent
|
|
266
|
+
? { ...this.#socketConfig, logger: silentPino }
|
|
267
|
+
: this.#socketConfig;
|
|
268
|
+
|
|
192
269
|
const { sock, session } = await initSession({
|
|
193
|
-
socketConfig
|
|
270
|
+
socketConfig,
|
|
194
271
|
id: this.id,
|
|
195
272
|
});
|
|
196
273
|
|
|
@@ -199,7 +276,7 @@ export class Client {
|
|
|
199
276
|
this.id = session.id;
|
|
200
277
|
this.#flag = `CLIENT-${session.id}`;
|
|
201
278
|
|
|
202
|
-
this.sock.ev.on(
|
|
279
|
+
this.sock.ev.on('connection.update', (update) => {
|
|
203
280
|
this.#handleConnectionUpdate(update);
|
|
204
281
|
});
|
|
205
282
|
}
|
|
@@ -210,54 +287,131 @@ export class Client {
|
|
|
210
287
|
try {
|
|
211
288
|
if (qr) {
|
|
212
289
|
await this.#handleAuth(qr);
|
|
213
|
-
} else if (connection ===
|
|
214
|
-
this.#onConnectionOpen();
|
|
215
|
-
} else if (connection ===
|
|
290
|
+
} else if (connection === 'open') {
|
|
291
|
+
await this.#onConnectionOpen();
|
|
292
|
+
} else if (connection === 'close') {
|
|
216
293
|
await this.#onConnectionClose(lastDisconnect);
|
|
217
294
|
}
|
|
218
295
|
} catch (err) {
|
|
219
|
-
logger.error(err, `[${this.#flag}] Error in connection update handler`);
|
|
296
|
+
this.#logger.error(err, `[${this.#flag}] Error in connection update handler`);
|
|
220
297
|
this.#resolvePending(null, err);
|
|
221
298
|
}
|
|
222
299
|
}
|
|
223
300
|
|
|
224
|
-
#onConnectionOpen() {
|
|
301
|
+
async #onConnectionOpen() {
|
|
225
302
|
const wasReconnecting = this.#state === ConnectionState.RECONNECTING;
|
|
226
303
|
this.#setState(ConnectionState.CONNECTED);
|
|
227
304
|
|
|
305
|
+
this.#register();
|
|
306
|
+
|
|
307
|
+
if (!this.#plugins || this.#plugins.destroyed) {
|
|
308
|
+
try {
|
|
309
|
+
this.#plugins = await pluginManager(this.sock);
|
|
310
|
+
} catch (err) {
|
|
311
|
+
this.#logger.error(err, `[${this.#flag}] Failed to initialize plugins`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
228
315
|
const attempts = this.#reconnectAttempts;
|
|
229
316
|
this.#reconnectAttempts = 0;
|
|
230
317
|
|
|
231
318
|
if (wasReconnecting) {
|
|
232
319
|
this.#emit('reconnect', { attempts });
|
|
233
|
-
logger.debug(`[${this.#flag}] Reconnected after ${attempts} attempt(s)`);
|
|
320
|
+
this.#logger.debug(`[${this.#flag}] Reconnected after ${attempts} attempt(s)`);
|
|
234
321
|
} else {
|
|
235
322
|
this.#hasConnectedOnce = true;
|
|
236
323
|
this.#emit('connect');
|
|
237
|
-
logger.debug(`[${this.#flag}] Connected successfully`);
|
|
238
|
-
this.#resolvePending({ sock: this.sock, session: this.session });
|
|
324
|
+
this.#logger.debug(`[${this.#flag}] Connected successfully`);
|
|
325
|
+
this.#resolvePending({ sock: this.sock, session: this.session, id: this.id });
|
|
326
|
+
|
|
327
|
+
if (!this.#sync) {
|
|
328
|
+
this.#syncOtherSessions();
|
|
329
|
+
}
|
|
239
330
|
}
|
|
240
331
|
}
|
|
241
332
|
|
|
333
|
+
// Automatic Session Synchronization
|
|
334
|
+
|
|
335
|
+
async #syncOtherSessions() {
|
|
336
|
+
if (Client.#isSyncing) return;
|
|
337
|
+
Client.#isSyncing = true;
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
const allSessionIds = listSessions();
|
|
341
|
+
const otherSessionIds = allSessionIds.filter((id) => id !== this.id);
|
|
342
|
+
|
|
343
|
+
if (otherSessionIds.length === 0) {
|
|
344
|
+
this.#logger.debug(`[${this.#flag}] No other sessions to sync`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
this.#logger.debug(
|
|
349
|
+
`[${this.#flag}] Syncing ${otherSessionIds.length} other session(s) in background`
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
const results = await Promise.allSettled(
|
|
353
|
+
otherSessionIds.map((sessionId) => this.#restoreSession(sessionId))
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
let successCount = 0;
|
|
357
|
+
let skipCount = 0;
|
|
358
|
+
let failCount = 0;
|
|
359
|
+
|
|
360
|
+
for (const result of results) {
|
|
361
|
+
if (result.status === 'fulfilled') {
|
|
362
|
+
if (result.value === true) successCount++;
|
|
363
|
+
else skipCount++;
|
|
364
|
+
} else {
|
|
365
|
+
failCount++;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
this.#logger.debug(
|
|
370
|
+
`[${this.#flag}] Session sync complete: ${successCount} restored, ${skipCount} skipped, ${failCount} failed`
|
|
371
|
+
);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
this.#logger.error(err, `[${this.#flag}] Error during session sync`);
|
|
374
|
+
} finally {
|
|
375
|
+
Client.#isSyncing = false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async #restoreSession(sessionId) {
|
|
380
|
+
if (Client.has(sessionId)) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const client = new Client({
|
|
385
|
+
id: sessionId,
|
|
386
|
+
silent: true,
|
|
387
|
+
sync: true,
|
|
388
|
+
maxRetries: 3,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
await client.connect();
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
242
395
|
async #onConnectionClose(lastDisconnect) {
|
|
243
396
|
const disconnectInfo = this.#parseDisconnectReason(lastDisconnect);
|
|
244
397
|
const { message, statusCode, recoverable, deleteSession } = disconnectInfo;
|
|
245
398
|
|
|
246
|
-
if (message ===
|
|
399
|
+
if (message === 'QR Scanned' && !this.#onPairing && !this.#silent) {
|
|
247
400
|
console.clear();
|
|
248
401
|
}
|
|
249
402
|
|
|
250
403
|
const level = recoverable ? 'debug' : 'warn';
|
|
251
|
-
|
|
252
|
-
logger[level](`[${this.#flag}] Disconnected: ${message} (code: ${statusCode})`);
|
|
404
|
+
this.#logger[level](`[${this.#flag}] Disconnected: ${message} (code: ${statusCode})`);
|
|
253
405
|
|
|
254
406
|
if (this.#hasConnectedOnce) {
|
|
255
407
|
this.#emit('disconnect', { message, statusCode, recoverable });
|
|
256
408
|
}
|
|
409
|
+
|
|
410
|
+
this.#unregister();
|
|
257
411
|
|
|
258
412
|
if (deleteSession) {
|
|
259
413
|
await this.session?.delete().catch((err) => {
|
|
260
|
-
logger.error(err, `[${this.#flag}] Failed to delete session`);
|
|
414
|
+
this.#logger.error(err, `[${this.#flag}] Failed to delete session`);
|
|
261
415
|
});
|
|
262
416
|
}
|
|
263
417
|
|
|
@@ -282,7 +436,7 @@ export class Client {
|
|
|
282
436
|
);
|
|
283
437
|
this.#setState(ConnectionState.DISCONNECTED);
|
|
284
438
|
this.#resolvePending(null, err);
|
|
285
|
-
logger.error(err, `[${this.#flag}] ${err.message}`);
|
|
439
|
+
this.#logger.error(err, `[${this.#flag}] ${err.message}`);
|
|
286
440
|
return;
|
|
287
441
|
}
|
|
288
442
|
|
|
@@ -291,20 +445,22 @@ export class Client {
|
|
|
291
445
|
}
|
|
292
446
|
|
|
293
447
|
const delay = this.#backoff(this.#reconnectAttempts);
|
|
448
|
+
const retriesInfo =
|
|
449
|
+
this.#maxRetries !== Infinity
|
|
450
|
+
? `(${this.#reconnectAttempts}/${this.#maxRetries})`
|
|
451
|
+
: '';
|
|
294
452
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
logger.debug(`[${this.#flag}] ${reason}. Reconnecting in ${delay} ms`);
|
|
453
|
+
this.#logger.debug(`[${this.#flag}] ${reason}. Reconnecting in ${delay}ms`);
|
|
298
454
|
|
|
299
455
|
const cancelled = await this.#wait(delay);
|
|
300
456
|
if (cancelled || this.#isShuttingDown) return;
|
|
301
457
|
|
|
302
|
-
logger.debug(`[${this.#flag}] Executing reconnect attempt ${retriesInfo}`);
|
|
458
|
+
this.#logger.debug(`[${this.#flag}] Executing reconnect attempt ${retriesInfo}`);
|
|
303
459
|
|
|
304
460
|
try {
|
|
305
461
|
await this.#createSocket();
|
|
306
462
|
} catch (err) {
|
|
307
|
-
logger.error(err, `[${this.#flag}] Socket creation failed during reconnect`);
|
|
463
|
+
this.#logger.error(err, `[${this.#flag}] Socket creation failed during reconnect`);
|
|
308
464
|
}
|
|
309
465
|
}
|
|
310
466
|
|
|
@@ -340,7 +496,19 @@ export class Client {
|
|
|
340
496
|
|
|
341
497
|
async #handleAuth(qr) {
|
|
342
498
|
this.#qr = qr;
|
|
343
|
-
|
|
499
|
+
|
|
500
|
+
if (this.#sync) {
|
|
501
|
+
const err = new ConnectionError('Authentication required for sync connection', {
|
|
502
|
+
recoverable: false,
|
|
503
|
+
});
|
|
504
|
+
this.#logger.debug(`[${this.#flag}] Sync connection requires auth, aborting`);
|
|
505
|
+
this.#cleanupSocket();
|
|
506
|
+
this.#setState(ConnectionState.DISCONNECTED);
|
|
507
|
+
this.#resolvePending(null, err);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (Client.#isConfiguring) return;
|
|
344
512
|
|
|
345
513
|
if (typeof this.#onPairing === 'function') {
|
|
346
514
|
const requestPairingCode = this.sock?.requestPairingCode?.bind(this.sock);
|
|
@@ -348,32 +516,46 @@ export class Client {
|
|
|
348
516
|
return;
|
|
349
517
|
}
|
|
350
518
|
|
|
351
|
-
this.#
|
|
352
|
-
|
|
353
|
-
|
|
519
|
+
if (this.#silent) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
Client.#isConfiguring = true;
|
|
524
|
+
try {
|
|
525
|
+
this.#authConfig ??= await getConnectionConfig();
|
|
526
|
+
} finally {
|
|
527
|
+
Client.#isConfiguring = false;
|
|
528
|
+
}
|
|
354
529
|
|
|
355
|
-
if (this.#authConfig.type ===
|
|
530
|
+
if (this.#authConfig.type === 'pn') {
|
|
356
531
|
const code = await this.sock.requestPairingCode(this.#authConfig.pn);
|
|
357
|
-
logger.prompt(this.#formatPairingCode(code));
|
|
532
|
+
this.#logger.prompt(this.#formatPairingCode(code));
|
|
358
533
|
} else {
|
|
359
534
|
qrcode.generate(this.#qr, { small: true });
|
|
360
|
-
process.stdout.write(
|
|
535
|
+
process.stdout.write('\n');
|
|
361
536
|
}
|
|
362
537
|
}
|
|
363
538
|
|
|
364
539
|
#formatPairingCode(code) {
|
|
365
|
-
const formatted = code.match(/.{1,4}/g)?.join(
|
|
366
|
-
return `\n${chalk.green(
|
|
540
|
+
const formatted = code.match(/.{1,4}/g)?.join(' ') ?? code;
|
|
541
|
+
return `\n${chalk.green('> Your OTP Code: ')}${chalk.bold(formatted)}`;
|
|
367
542
|
}
|
|
368
543
|
|
|
369
544
|
// Cleanup & Shutdown
|
|
370
545
|
|
|
371
546
|
#cleanupSocket() {
|
|
547
|
+
if (this.#plugins && !this.#plugins.destroyed) {
|
|
548
|
+
this.#plugins.destroy();
|
|
549
|
+
this.#plugins = null;
|
|
550
|
+
}
|
|
551
|
+
|
|
372
552
|
if (!this.sock) return;
|
|
373
553
|
|
|
374
554
|
try {
|
|
375
555
|
this.sock.ev.removeAllListeners();
|
|
376
|
-
} catch {
|
|
556
|
+
} catch {
|
|
557
|
+
/* noop */
|
|
558
|
+
}
|
|
377
559
|
|
|
378
560
|
this.sock = null;
|
|
379
561
|
}
|
|
@@ -393,6 +575,9 @@ export class Client {
|
|
|
393
575
|
this.#clearReconnectTimer();
|
|
394
576
|
this.#resolvePending(null, new Error('Client disconnected'));
|
|
395
577
|
|
|
578
|
+
// Unregister from registry
|
|
579
|
+
this.#unregister();
|
|
580
|
+
|
|
396
581
|
this.#cleanupSocket();
|
|
397
582
|
this.#setState(ConnectionState.DISCONNECTED);
|
|
398
583
|
|
|
@@ -406,7 +591,7 @@ export class Client {
|
|
|
406
591
|
await this.disconnect();
|
|
407
592
|
await this.session?.delete();
|
|
408
593
|
} catch (err) {
|
|
409
|
-
logger.error(err, `[${this.#flag}] Logging out failed
|
|
594
|
+
this.#logger.error(err, `[${this.#flag}] Logging out failed`);
|
|
410
595
|
}
|
|
411
596
|
}
|
|
412
597
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { select, input } from "@inquirer/prompts";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { logger } from "#internals.js";
|
|
4
|
+
import { pauseSpinner } from "#utils.js";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const connectionConfig = async () => {
|
|
6
7
|
try {
|
|
7
8
|
process.stdout.write("\n");
|
|
8
9
|
const type = await select({
|
|
@@ -45,4 +46,6 @@ export const getConnectionConfig = async () => {
|
|
|
45
46
|
logger.prompt(chalk.red("\nOperation cancelled"));
|
|
46
47
|
process.exit(0);
|
|
47
48
|
}
|
|
48
|
-
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const getConnectionConfig = () => pauseSpinner(connectionConfig);
|
package/src/config/default.js
CHANGED
|
@@ -20,7 +20,20 @@ const db = {
|
|
|
20
20
|
noMetaSync: false,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const plugins = {
|
|
24
|
+
dir: 'plugins',
|
|
25
|
+
prefixes: ['.', '\\', '!'],
|
|
26
|
+
defaultEvent: 'messages.upsert',
|
|
27
|
+
hmr: {
|
|
28
|
+
enable: false,
|
|
29
|
+
debounce: 200,
|
|
30
|
+
debug: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
export default {
|
|
24
35
|
db,
|
|
25
|
-
socket
|
|
36
|
+
socket,
|
|
37
|
+
plugins,
|
|
38
|
+
timeZone: 'Africa/Casablanca'
|
|
26
39
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isJidGroup,
|
|
3
|
+
areJidsSameUser,
|
|
4
|
+
getDevice,
|
|
5
|
+
downloadMediaMessage,
|
|
6
|
+
getContentType,
|
|
7
|
+
isLidUser,
|
|
8
|
+
jidNormalizedUser
|
|
9
|
+
} from 'baileys';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getTimeString,
|
|
13
|
+
isString,
|
|
14
|
+
toNumber,
|
|
15
|
+
toBase64,
|
|
16
|
+
getPN
|
|
17
|
+
} from '#utils.js';
|
|
18
|
+
|
|
19
|
+
const extractMessage = (message) => {
|
|
20
|
+
let type = getContentType(message);
|
|
21
|
+
let data = message?.[type];
|
|
22
|
+
|
|
23
|
+
// cases like viewonce or document messages etc
|
|
24
|
+
if (data?.message) {
|
|
25
|
+
const innerType = getContentType(data.message);
|
|
26
|
+
if (innerType) {
|
|
27
|
+
type = innerType;
|
|
28
|
+
data = data.message[type];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return [type, data];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const extractContent = (message = {}) => ({
|
|
35
|
+
body: message.text ?? message.caption ??
|
|
36
|
+
(isString(message) ? message : undefined),
|
|
37
|
+
mentions: message.contextInfo?.mentionedJid,
|
|
38
|
+
groupMentions: message.contextInfo?.groupMentions,
|
|
39
|
+
mimetype: message.mimetype,
|
|
40
|
+
fileName: message.fileName,
|
|
41
|
+
pageCount: message.pageCount,
|
|
42
|
+
fileLength: toNumber(message.fileLength),
|
|
43
|
+
hash: toBase64(message.fileSha256),
|
|
44
|
+
isViewOnce: message.viewOnce,
|
|
45
|
+
url: message.matchedText,
|
|
46
|
+
description: message.description,
|
|
47
|
+
thumbnail: message.jpegThumbnail,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const load = async (x) => downloadMediaMessage(x, 'buffer', {});
|
|
51
|
+
|
|
52
|
+
export const formatMessage = (sock, raw) => {
|
|
53
|
+
const myId = jidNormalizedUser(sock.user.lid);
|
|
54
|
+
const [type, messageData] = extractMessage(raw.message);
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
pushName: name,
|
|
58
|
+
messageTimestamp,
|
|
59
|
+
participant: p1,
|
|
60
|
+
broadcast,
|
|
61
|
+
key,
|
|
62
|
+
key: {
|
|
63
|
+
remoteJid: roomId,
|
|
64
|
+
id, fromMe,
|
|
65
|
+
participant: p2,
|
|
66
|
+
}
|
|
67
|
+
} = raw;
|
|
68
|
+
|
|
69
|
+
const jid = fromMe ? myId : p2 || p1 || roomId;
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
quotedMessage,
|
|
73
|
+
participant: quotedSender,
|
|
74
|
+
stanzaId: quotedId,
|
|
75
|
+
isForwarded,
|
|
76
|
+
forwardingScore,
|
|
77
|
+
} = messageData?.contextInfo || {};
|
|
78
|
+
const [quotedType, quotedData] = extractMessage(quotedMessage);
|
|
79
|
+
|
|
80
|
+
const quotedKey = {
|
|
81
|
+
id: quotedId,
|
|
82
|
+
participant: quotedSender,
|
|
83
|
+
remoteJid: roomId,
|
|
84
|
+
fromMe: areJidsSameUser(quotedSender, myId)
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
type,
|
|
89
|
+
name,
|
|
90
|
+
id,
|
|
91
|
+
broadcast,
|
|
92
|
+
isForwarded,
|
|
93
|
+
forwardingScore,
|
|
94
|
+
fromMe,
|
|
95
|
+
jid,
|
|
96
|
+
roomId,
|
|
97
|
+
timestamp: toNumber(messageTimestamp),
|
|
98
|
+
isLid: isLidUser(jid),
|
|
99
|
+
device: getDevice(id),
|
|
100
|
+
isGroup: isJidGroup(roomId),
|
|
101
|
+
timeString: getTimeString(messageTimestamp),
|
|
102
|
+
...extractContent(messageData),
|
|
103
|
+
key, raw,
|
|
104
|
+
contextInfo: {
|
|
105
|
+
stanzaId: id,
|
|
106
|
+
participant: jid,
|
|
107
|
+
remoteJid: roomId
|
|
108
|
+
},
|
|
109
|
+
load() {
|
|
110
|
+
return load(this.raw)
|
|
111
|
+
},
|
|
112
|
+
senderIs(id) {
|
|
113
|
+
return areJidsSameUser(this.jid, id)
|
|
114
|
+
},
|
|
115
|
+
pn() {
|
|
116
|
+
return getPN(sock, this.jid)
|
|
117
|
+
},
|
|
118
|
+
quoted: quotedData ? {
|
|
119
|
+
type: quotedType,
|
|
120
|
+
jid: quotedSender,
|
|
121
|
+
id: quotedId,
|
|
122
|
+
...extractContent(quotedData),
|
|
123
|
+
key: quotedKey,
|
|
124
|
+
contextInfo: {
|
|
125
|
+
stanzaId: quotedId,
|
|
126
|
+
participant: quotedSender,
|
|
127
|
+
remoteJid: roomId
|
|
128
|
+
},
|
|
129
|
+
raw: {
|
|
130
|
+
key: quotedKey,
|
|
131
|
+
message: quotedMessage
|
|
132
|
+
},
|
|
133
|
+
load() {
|
|
134
|
+
return load(this.raw)
|
|
135
|
+
},
|
|
136
|
+
senderIs(id) {
|
|
137
|
+
return areJidsSameUser(this.jid, id)
|
|
138
|
+
},
|
|
139
|
+
pn() {
|
|
140
|
+
return getPN(sock, this.jid)
|
|
141
|
+
}
|
|
142
|
+
}: undefined,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { formatMessage } from './format-message.js';
|
|
2
|
+
|
|
3
|
+
export const formatter = (wa, event, eventName) => {
|
|
4
|
+
eventName ??= 'messages.upsert';
|
|
5
|
+
|
|
6
|
+
switch (eventName) {
|
|
7
|
+
case 'messages.upsert':
|
|
8
|
+
return {
|
|
9
|
+
...formatMessage(wa, event),
|
|
10
|
+
event: eventName
|
|
11
|
+
};
|
|
12
|
+
default: return event;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export * from "
|
|
2
|
-
export * from "
|
|
3
|
-
export * from "
|
|
4
|
-
export * from "
|
|
1
|
+
export * from "#internals.js";
|
|
2
|
+
export * from "#utils.js";
|
|
3
|
+
export * from "#auth.js";
|
|
4
|
+
export * from "#client.js";
|
|
5
|
+
export * from "#formatter.js"
|