@roidev/kachina-md 2.1.0 → 2.1.1
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/lib/client/Client.js +283 -7
- package/lib/handlers/PluginHandler.js +110 -0
- package/lib/helpers/database.js +140 -0
- package/lib/helpers/index.js +85 -0
- package/lib/helpers/logger.js +69 -0
- package/lib/helpers/serialize.js +43 -0
- package/lib/helpers/sticker.js +52 -7
- package/lib/index.d.ts +265 -0
- package/package.json +4 -2
package/lib/client/Client.js
CHANGED
|
@@ -16,9 +16,56 @@ import {
|
|
|
16
16
|
StickerTypes
|
|
17
17
|
} from '../helpers/sticker.js';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} ClientOptions
|
|
21
|
+
* @property {string} [sessionId='kachina-session'] - Session ID for storing authentication data
|
|
22
|
+
* @property {string} [phoneNumber=''] - Phone number for pairing method (format: 628123456789)
|
|
23
|
+
* @property {'qr'|'pairing'} [loginMethod='qr'] - Login method: 'qr' for QR code, 'pairing' for pairing code
|
|
24
|
+
* @property {Array<string>} [browser=['Kachina-MD', 'Chrome', '1.0.0']] - Browser identification
|
|
25
|
+
* @property {Object} [logger] - Pino logger instance
|
|
26
|
+
* @property {string} [prefix='!'] - Command prefix for plugin system
|
|
27
|
+
* @property {Object} [store] - Optional message store for caching
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Main WhatsApp bot client class
|
|
32
|
+
*
|
|
33
|
+
* @class Client
|
|
34
|
+
* @extends EventEmitter
|
|
35
|
+
*
|
|
36
|
+
* @fires Client#ready - Emitted when bot is connected and ready
|
|
37
|
+
* @fires Client#message - Emitted when a new message is received
|
|
38
|
+
* @fires Client#group.update - Emitted when group participants are updated
|
|
39
|
+
* @fires Client#groups.update - Emitted when group info is updated
|
|
40
|
+
* @fires Client#call - Emitted when receiving a call
|
|
41
|
+
* @fires Client#pairing.code - Emitted when pairing code is generated (pairing mode only)
|
|
42
|
+
* @fires Client#pairing.error - Emitted when pairing code request fails
|
|
43
|
+
* @fires Client#reconnecting - Emitted when bot is reconnecting
|
|
44
|
+
* @fires Client#connecting - Emitted when bot is connecting
|
|
45
|
+
* @fires Client#logout - Emitted when bot is logged out
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // QR Code login
|
|
49
|
+
* const client = new Client({
|
|
50
|
+
* sessionId: 'my-bot',
|
|
51
|
+
* loginMethod: 'qr'
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Pairing code login
|
|
56
|
+
* const client = new Client({
|
|
57
|
+
* sessionId: 'my-bot',
|
|
58
|
+
* phoneNumber: '628123456789',
|
|
59
|
+
* loginMethod: 'pairing'
|
|
60
|
+
* });
|
|
61
|
+
*/
|
|
19
62
|
export class Client extends EventEmitter {
|
|
20
63
|
static StickerTypes = StickerTypes;
|
|
21
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Creates a new Client instance
|
|
67
|
+
* @param {ClientOptions} [options={}] - Client configuration options
|
|
68
|
+
*/
|
|
22
69
|
constructor(options = {}) {
|
|
23
70
|
super();
|
|
24
71
|
|
|
@@ -37,6 +84,18 @@ export class Client extends EventEmitter {
|
|
|
37
84
|
this.pluginHandler = new PluginHandler(this);
|
|
38
85
|
}
|
|
39
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Start the WhatsApp bot connection
|
|
89
|
+
* Initializes the socket connection and handles authentication
|
|
90
|
+
* @async
|
|
91
|
+
* @returns {Promise<Object>} The initialized socket connection
|
|
92
|
+
* @throws {Error} If phone number is invalid (pairing mode)
|
|
93
|
+
* @example
|
|
94
|
+
* await client.start();
|
|
95
|
+
* client.on('ready', (user) => {
|
|
96
|
+
* console.log('Bot ready:', user.id);
|
|
97
|
+
* });
|
|
98
|
+
*/
|
|
40
99
|
async start() {
|
|
41
100
|
const { state, saveCreds } = await useMultiFileAuthState(this.config.sessionId);
|
|
42
101
|
const { version } = await fetchLatestBaileysVersion();
|
|
@@ -135,6 +194,15 @@ export class Client extends EventEmitter {
|
|
|
135
194
|
return this.sock;
|
|
136
195
|
}
|
|
137
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Handle connection updates from WhatsApp socket
|
|
199
|
+
* @private
|
|
200
|
+
* @async
|
|
201
|
+
* @param {Object} update - Connection update object
|
|
202
|
+
* @param {string} [update.connection] - Connection status: 'open', 'close', 'connecting'
|
|
203
|
+
* @param {Object} [update.lastDisconnect] - Last disconnect information
|
|
204
|
+
* @param {string} [update.qr] - QR code data for scanning
|
|
205
|
+
*/
|
|
138
206
|
async handleConnectionUpdate(update) {
|
|
139
207
|
const { connection, lastDisconnect, qr } = update;
|
|
140
208
|
|
|
@@ -170,15 +238,45 @@ export class Client extends EventEmitter {
|
|
|
170
238
|
}
|
|
171
239
|
}
|
|
172
240
|
|
|
173
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Send a raw message to WhatsApp
|
|
243
|
+
* @async
|
|
244
|
+
* @param {string} jid - WhatsApp JID (chat ID) - format: number@s.whatsapp.net or groupId@g.us
|
|
245
|
+
* @param {Object} content - Message content object (text, image, video, etc.)
|
|
246
|
+
* @param {Object} [options={}] - Additional options for message
|
|
247
|
+
* @returns {Promise<Object>} Sent message object
|
|
248
|
+
* @example
|
|
249
|
+
* await client.sendMessage('628xxx@s.whatsapp.net', { text: 'Hello' });
|
|
250
|
+
*/
|
|
174
251
|
async sendMessage(jid, content, options = {}) {
|
|
175
252
|
return await this.sock.sendMessage(jid, content, options);
|
|
176
253
|
}
|
|
177
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Send a text message
|
|
257
|
+
* @async
|
|
258
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
259
|
+
* @param {string} text - Text message content
|
|
260
|
+
* @param {Object} [options={}] - Additional options
|
|
261
|
+
* @returns {Promise<Object>} Sent message object
|
|
262
|
+
* @example
|
|
263
|
+
* await client.sendText('628xxx@s.whatsapp.net', 'Hello World!');
|
|
264
|
+
*/
|
|
178
265
|
async sendText(jid, text, options = {}) {
|
|
179
266
|
return await this.sendMessage(jid, { text }, options);
|
|
180
267
|
}
|
|
181
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Send an image message
|
|
271
|
+
* @async
|
|
272
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
273
|
+
* @param {Buffer|string} buffer - Image buffer or file path/URL
|
|
274
|
+
* @param {string} [caption=''] - Image caption
|
|
275
|
+
* @param {Object} [options={}] - Additional options
|
|
276
|
+
* @returns {Promise<Object>} Sent message object
|
|
277
|
+
* @example
|
|
278
|
+
* await client.sendImage('628xxx@s.whatsapp.net', imageBuffer, 'My Image');
|
|
279
|
+
*/
|
|
182
280
|
async sendImage(jid, buffer, caption = '', options = {}) {
|
|
183
281
|
const content = {
|
|
184
282
|
image: buffer,
|
|
@@ -188,6 +286,17 @@ export class Client extends EventEmitter {
|
|
|
188
286
|
return await this.sendMessage(jid, content, options);
|
|
189
287
|
}
|
|
190
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Send a video message
|
|
291
|
+
* @async
|
|
292
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
293
|
+
* @param {Buffer|string} buffer - Video buffer or file path/URL
|
|
294
|
+
* @param {string} [caption=''] - Video caption
|
|
295
|
+
* @param {Object} [options={}] - Additional options
|
|
296
|
+
* @returns {Promise<Object>} Sent message object
|
|
297
|
+
* @example
|
|
298
|
+
* await client.sendVideo('628xxx@s.whatsapp.net', videoBuffer, 'My Video');
|
|
299
|
+
*/
|
|
191
300
|
async sendVideo(jid, buffer, caption = '', options = {}) {
|
|
192
301
|
const content = {
|
|
193
302
|
video: buffer,
|
|
@@ -197,6 +306,21 @@ export class Client extends EventEmitter {
|
|
|
197
306
|
return await this.sendMessage(jid, content, options);
|
|
198
307
|
}
|
|
199
308
|
|
|
309
|
+
/**
|
|
310
|
+
* Send an audio message
|
|
311
|
+
* @async
|
|
312
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
313
|
+
* @param {Buffer|string} buffer - Audio buffer or file path/URL
|
|
314
|
+
* @param {Object} [options={}] - Additional options
|
|
315
|
+
* @param {string} [options.mimetype='audio/mp4'] - Audio mime type
|
|
316
|
+
* @param {boolean} [options.ptt=false] - Push to talk (voice note)
|
|
317
|
+
* @returns {Promise<Object>} Sent message object
|
|
318
|
+
* @example
|
|
319
|
+
* // Send as audio file
|
|
320
|
+
* await client.sendAudio('628xxx@s.whatsapp.net', audioBuffer);
|
|
321
|
+
* // Send as voice note
|
|
322
|
+
* await client.sendAudio('628xxx@s.whatsapp.net', audioBuffer, { ptt: true });
|
|
323
|
+
*/
|
|
200
324
|
async sendAudio(jid, buffer, options = {}) {
|
|
201
325
|
const content = {
|
|
202
326
|
audio: buffer,
|
|
@@ -207,6 +331,18 @@ export class Client extends EventEmitter {
|
|
|
207
331
|
return await this.sendMessage(jid, content, options);
|
|
208
332
|
}
|
|
209
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Send a document message
|
|
336
|
+
* @async
|
|
337
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
338
|
+
* @param {Buffer|string} buffer - Document buffer or file path/URL
|
|
339
|
+
* @param {string} filename - Document filename with extension
|
|
340
|
+
* @param {string} mimetype - Document mime type (e.g., 'application/pdf')
|
|
341
|
+
* @param {Object} [options={}] - Additional options
|
|
342
|
+
* @returns {Promise<Object>} Sent message object
|
|
343
|
+
* @example
|
|
344
|
+
* await client.sendDocument('628xxx@s.whatsapp.net', pdfBuffer, 'file.pdf', 'application/pdf');
|
|
345
|
+
*/
|
|
210
346
|
async sendDocument(jid, buffer, filename, mimetype, options = {}) {
|
|
211
347
|
return await this.sendMessage(jid, {
|
|
212
348
|
document: buffer,
|
|
@@ -216,7 +352,22 @@ export class Client extends EventEmitter {
|
|
|
216
352
|
});
|
|
217
353
|
}
|
|
218
354
|
|
|
219
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Send a sticker message
|
|
357
|
+
* @async
|
|
358
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
359
|
+
* @param {Buffer|string} buffer - Image buffer or file path/URL to convert to sticker
|
|
360
|
+
* @param {Object} [options={}] - Sticker options
|
|
361
|
+
* @param {string} [options.pack] - Sticker pack name
|
|
362
|
+
* @param {string} [options.author] - Sticker author name
|
|
363
|
+
* @param {string} [options.type] - Sticker type from Client.StickerTypes
|
|
364
|
+
* @returns {Promise<Object>} Sent message object
|
|
365
|
+
* @example
|
|
366
|
+
* await client.sendSticker('628xxx@s.whatsapp.net', imageBuffer, {
|
|
367
|
+
* pack: 'My Stickers',
|
|
368
|
+
* author: 'Bot'
|
|
369
|
+
* });
|
|
370
|
+
*/
|
|
220
371
|
async sendSticker(jid, buffer, options = {}) {
|
|
221
372
|
const stickerBuffer = await createSticker(buffer, options);
|
|
222
373
|
return await this.sendMessage(jid, {
|
|
@@ -225,14 +376,37 @@ export class Client extends EventEmitter {
|
|
|
225
376
|
});
|
|
226
377
|
}
|
|
227
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Send contact(s) message
|
|
381
|
+
* @async
|
|
382
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
383
|
+
* @param {Array<{displayName: string, vcard: string}>} contacts - Array of contact objects
|
|
384
|
+
* @param {Object} [options={}] - Additional options
|
|
385
|
+
* @returns {Promise<Object>} Sent message object
|
|
386
|
+
* @example
|
|
387
|
+
* await client.sendContact('628xxx@s.whatsapp.net', [{
|
|
388
|
+
* displayName: 'John Doe',
|
|
389
|
+
* vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+1234567890\nEND:VCARD'
|
|
390
|
+
* }]);
|
|
391
|
+
*/
|
|
228
392
|
async sendContact(jid, contacts, options = {}) {
|
|
229
|
-
// contacts = [{ displayName, vcard }]
|
|
230
393
|
return await this.sendMessage(jid, {
|
|
231
394
|
contacts: { contacts },
|
|
232
395
|
...options
|
|
233
396
|
});
|
|
234
397
|
}
|
|
235
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Send location message
|
|
401
|
+
* @async
|
|
402
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
403
|
+
* @param {number} latitude - Location latitude
|
|
404
|
+
* @param {number} longitude - Location longitude
|
|
405
|
+
* @param {Object} [options={}] - Additional options
|
|
406
|
+
* @returns {Promise<Object>} Sent message object
|
|
407
|
+
* @example
|
|
408
|
+
* await client.sendLocation('628xxx@s.whatsapp.net', -6.200000, 106.816666);
|
|
409
|
+
*/
|
|
236
410
|
async sendLocation(jid, latitude, longitude, options = {}) {
|
|
237
411
|
return await this.sendMessage(jid, {
|
|
238
412
|
location: { degreesLatitude: latitude, degreesLongitude: longitude },
|
|
@@ -240,6 +414,18 @@ export class Client extends EventEmitter {
|
|
|
240
414
|
});
|
|
241
415
|
}
|
|
242
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Send poll message
|
|
419
|
+
* @async
|
|
420
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
421
|
+
* @param {string} name - Poll question
|
|
422
|
+
* @param {Array<string>} values - Poll options
|
|
423
|
+
* @param {Object} [options={}] - Additional options
|
|
424
|
+
* @param {number} [options.selectableCount=1] - Number of selectable options
|
|
425
|
+
* @returns {Promise<Object>} Sent message object
|
|
426
|
+
* @example
|
|
427
|
+
* await client.sendPoll('628xxx@s.whatsapp.net', 'Favorite color?', ['Red', 'Blue', 'Green']);
|
|
428
|
+
*/
|
|
243
429
|
async sendPoll(jid, name, values, options = {}) {
|
|
244
430
|
return await this.sendMessage(jid, {
|
|
245
431
|
poll: {
|
|
@@ -251,6 +437,16 @@ export class Client extends EventEmitter {
|
|
|
251
437
|
});
|
|
252
438
|
}
|
|
253
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Send reaction to a message
|
|
442
|
+
* @async
|
|
443
|
+
* @param {string} jid - WhatsApp JID (chat ID)
|
|
444
|
+
* @param {Object} messageKey - Message key object from the message to react to
|
|
445
|
+
* @param {string} emoji - Emoji to react with
|
|
446
|
+
* @returns {Promise<Object>} Sent reaction object
|
|
447
|
+
* @example
|
|
448
|
+
* await client.sendReact('628xxx@s.whatsapp.net', message.key, '👍');
|
|
449
|
+
*/
|
|
254
450
|
async sendReact(jid, messageKey, emoji) {
|
|
255
451
|
return await this.sendMessage(jid, {
|
|
256
452
|
react: { text: emoji, key: messageKey }
|
|
@@ -259,8 +455,15 @@ export class Client extends EventEmitter {
|
|
|
259
455
|
|
|
260
456
|
/**
|
|
261
457
|
* Read and download view once message
|
|
458
|
+
* @async
|
|
262
459
|
* @param {Object} quotedMessage - The quoted message object (m.quoted)
|
|
263
|
-
* @returns {Promise<{buffer: Buffer, type: 'image'|'video', caption: string}>}
|
|
460
|
+
* @returns {Promise<{buffer: Buffer, type: 'image'|'video', caption: string}>} Object containing media buffer, type, and caption
|
|
461
|
+
* @throws {Error} If quoted message is not provided or not a view once message
|
|
462
|
+
* @example
|
|
463
|
+
* const { buffer, type, caption } = await client.readViewOnce(m.quoted);
|
|
464
|
+
* if (type === 'image') {
|
|
465
|
+
* await client.sendImage(m.from, buffer, caption);
|
|
466
|
+
* }
|
|
264
467
|
*/
|
|
265
468
|
async readViewOnce(quotedMessage) {
|
|
266
469
|
if (!quotedMessage) {
|
|
@@ -294,10 +497,15 @@ export class Client extends EventEmitter {
|
|
|
294
497
|
|
|
295
498
|
/**
|
|
296
499
|
* Read view once message and send to chat
|
|
297
|
-
* @
|
|
500
|
+
* @async
|
|
501
|
+
* @param {string} jid - WhatsApp JID (chat ID) to send to
|
|
298
502
|
* @param {Object} quotedMessage - The quoted message object (m.quoted)
|
|
299
|
-
* @param {Object} options - Additional options
|
|
300
|
-
* @returns {Promise}
|
|
503
|
+
* @param {Object} [options={}] - Additional options
|
|
504
|
+
* @returns {Promise<Object>} Sent message object
|
|
505
|
+
* @throws {Error} If reading or sending view once message fails
|
|
506
|
+
* @example
|
|
507
|
+
* // Forward a view once message
|
|
508
|
+
* await client.sendViewOnce(m.from, m.quoted);
|
|
301
509
|
*/
|
|
302
510
|
async sendViewOnce(jid, quotedMessage, options = {}) {
|
|
303
511
|
try {
|
|
@@ -321,34 +529,102 @@ export class Client extends EventEmitter {
|
|
|
321
529
|
}
|
|
322
530
|
}
|
|
323
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Get group metadata
|
|
534
|
+
* @async
|
|
535
|
+
* @param {string} jid - Group JID (format: groupId@g.us)
|
|
536
|
+
* @returns {Promise<Object>} Group metadata object containing id, subject, participants, etc.
|
|
537
|
+
* @example
|
|
538
|
+
* const metadata = await client.groupMetadata('groupId@g.us');
|
|
539
|
+
* console.log(metadata.subject); // Group name
|
|
540
|
+
*/
|
|
324
541
|
async groupMetadata(jid) {
|
|
325
542
|
return await this.sock.groupMetadata(jid);
|
|
326
543
|
}
|
|
327
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Update group participants (add, remove, promote, demote)
|
|
547
|
+
* @async
|
|
548
|
+
* @param {string} jid - Group JID (format: groupId@g.us)
|
|
549
|
+
* @param {Array<string>} participants - Array of participant JIDs
|
|
550
|
+
* @param {'add'|'remove'|'promote'|'demote'} action - Action to perform
|
|
551
|
+
* @returns {Promise<Object>} Update result
|
|
552
|
+
* @example
|
|
553
|
+
* // Remove participants
|
|
554
|
+
* await client.groupParticipantsUpdate('groupId@g.us', ['628xxx@s.whatsapp.net'], 'remove');
|
|
555
|
+
* // Promote to admin
|
|
556
|
+
* await client.groupParticipantsUpdate('groupId@g.us', ['628xxx@s.whatsapp.net'], 'promote');
|
|
557
|
+
*/
|
|
328
558
|
async groupParticipantsUpdate(jid, participants, action) {
|
|
329
559
|
return await this.sock.groupParticipantsUpdate(jid, participants, action);
|
|
330
560
|
}
|
|
331
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Update group subject (name)
|
|
564
|
+
* @async
|
|
565
|
+
* @param {string} jid - Group JID (format: groupId@g.us)
|
|
566
|
+
* @param {string} subject - New group name
|
|
567
|
+
* @returns {Promise<Object>} Update result
|
|
568
|
+
* @example
|
|
569
|
+
* await client.groupUpdateSubject('groupId@g.us', 'My New Group Name');
|
|
570
|
+
*/
|
|
332
571
|
async groupUpdateSubject(jid, subject) {
|
|
333
572
|
return await this.sock.groupUpdateSubject(jid, subject);
|
|
334
573
|
}
|
|
335
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Update group description
|
|
577
|
+
* @async
|
|
578
|
+
* @param {string} jid - Group JID (format: groupId@g.us)
|
|
579
|
+
* @param {string} description - New group description
|
|
580
|
+
* @returns {Promise<Object>} Update result
|
|
581
|
+
* @example
|
|
582
|
+
* await client.groupUpdateDescription('groupId@g.us', 'This is my group description');
|
|
583
|
+
*/
|
|
336
584
|
async groupUpdateDescription(jid, description) {
|
|
337
585
|
return await this.sock.groupUpdateDescription(jid, description);
|
|
338
586
|
}
|
|
339
587
|
|
|
588
|
+
/**
|
|
589
|
+
* Load a single plugin from file path
|
|
590
|
+
* @async
|
|
591
|
+
* @param {string} path - Path to plugin file
|
|
592
|
+
* @returns {Promise<void>}
|
|
593
|
+
* @example
|
|
594
|
+
* await client.loadPlugin('./plugins/my-plugin.js');
|
|
595
|
+
*/
|
|
340
596
|
async loadPlugin(path) {
|
|
341
597
|
await this.pluginHandler.load(path);
|
|
342
598
|
}
|
|
343
599
|
|
|
600
|
+
/**
|
|
601
|
+
* Load all plugins from a directory
|
|
602
|
+
* @async
|
|
603
|
+
* @param {string} directory - Path to plugins directory
|
|
604
|
+
* @returns {Promise<void>}
|
|
605
|
+
* @example
|
|
606
|
+
* await client.loadPlugins('./plugins');
|
|
607
|
+
*/
|
|
344
608
|
async loadPlugins(directory) {
|
|
345
609
|
await this.pluginHandler.loadAll(directory);
|
|
346
610
|
}
|
|
347
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Get command prefix
|
|
614
|
+
* @returns {string} Current command prefix
|
|
615
|
+
* @example
|
|
616
|
+
* console.log(client.prefix); // '!'
|
|
617
|
+
*/
|
|
348
618
|
get prefix() {
|
|
349
619
|
return this.config.prefix || '!';
|
|
350
620
|
}
|
|
351
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Set command prefix
|
|
624
|
+
* @param {string} prefix - New command prefix
|
|
625
|
+
* @example
|
|
626
|
+
* client.prefix = '.';
|
|
627
|
+
*/
|
|
352
628
|
set prefix(prefix) {
|
|
353
629
|
this.config.prefix = prefix;
|
|
354
630
|
}
|
|
@@ -2,7 +2,42 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} Plugin
|
|
7
|
+
* @property {string} name - Plugin name
|
|
8
|
+
* @property {Array<string>} commands - Command aliases
|
|
9
|
+
* @property {string} [category] - Plugin category
|
|
10
|
+
* @property {string} [description] - Plugin description
|
|
11
|
+
* @property {boolean} [owner=false] - Owner only command
|
|
12
|
+
* @property {boolean} [group=false] - Group only command
|
|
13
|
+
* @property {boolean} [private=false] - Private chat only command
|
|
14
|
+
* @property {boolean} [admin=false] - Admin only command
|
|
15
|
+
* @property {boolean} [botAdmin=false] - Requires bot to be admin
|
|
16
|
+
* @property {Function} exec - Plugin execution function
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} PluginContext
|
|
21
|
+
* @property {import('../client/Client.js').Client} client - Bot client instance
|
|
22
|
+
* @property {Object} m - Serialized message object
|
|
23
|
+
* @property {Array<string>} args - Command arguments
|
|
24
|
+
* @property {string} command - Command name that was used
|
|
25
|
+
* @property {string} prefix - Command prefix
|
|
26
|
+
* @property {Object} sock - Raw WhatsApp socket
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Plugin handler for loading and executing bot plugins
|
|
31
|
+
* @class PluginHandler
|
|
32
|
+
* @example
|
|
33
|
+
* const handler = new PluginHandler(client);
|
|
34
|
+
* await handler.loadAll('./plugins');
|
|
35
|
+
*/
|
|
5
36
|
export class PluginHandler {
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new PluginHandler instance
|
|
39
|
+
* @param {import('../client/Client.js').Client} client - Bot client instance
|
|
40
|
+
*/
|
|
6
41
|
constructor(client) {
|
|
7
42
|
this.client = client;
|
|
8
43
|
this.plugins = new Map();
|
|
@@ -10,6 +45,18 @@ export class PluginHandler {
|
|
|
10
45
|
this.isLoaded = false;
|
|
11
46
|
}
|
|
12
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Load a single plugin from file path
|
|
50
|
+
* @async
|
|
51
|
+
* @param {string} pluginPath - Absolute path to plugin file
|
|
52
|
+
* @returns {Promise<Plugin|null>} Loaded plugin object or null if failed
|
|
53
|
+
* @throws {Error} If plugin is invalid
|
|
54
|
+
* @example
|
|
55
|
+
* const plugin = await handler.load('/path/to/plugin.js');
|
|
56
|
+
* if (plugin) {
|
|
57
|
+
* console.log('Loaded:', plugin.name);
|
|
58
|
+
* }
|
|
59
|
+
*/
|
|
13
60
|
async load(pluginPath) {
|
|
14
61
|
try {
|
|
15
62
|
const module = await import(`file://${pluginPath}?update=${Date.now()}`);
|
|
@@ -55,6 +102,15 @@ export class PluginHandler {
|
|
|
55
102
|
}
|
|
56
103
|
}
|
|
57
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Load all plugins from a directory recursively
|
|
107
|
+
* @async
|
|
108
|
+
* @param {string} directory - Path to plugins directory
|
|
109
|
+
* @returns {Promise<number>} Number of successfully loaded plugins
|
|
110
|
+
* @example
|
|
111
|
+
* const count = await handler.loadAll('./plugins');
|
|
112
|
+
* console.log(`Loaded ${count} plugins`);
|
|
113
|
+
*/
|
|
58
114
|
async loadAll(directory) {
|
|
59
115
|
if (!fs.existsSync(directory)) {
|
|
60
116
|
console.warn(`Plugin directory not found: ${directory}`);
|
|
@@ -75,6 +131,13 @@ export class PluginHandler {
|
|
|
75
131
|
return loaded;
|
|
76
132
|
}
|
|
77
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Find all files with specific extension in directory recursively
|
|
136
|
+
* @private
|
|
137
|
+
* @param {string} dir - Directory to search
|
|
138
|
+
* @param {string} ext - File extension (e.g., '.js')
|
|
139
|
+
* @returns {Array<string>} Array of absolute file paths
|
|
140
|
+
*/
|
|
78
141
|
findFiles(dir, ext) {
|
|
79
142
|
let results = [];
|
|
80
143
|
const list = fs.readdirSync(dir);
|
|
@@ -93,6 +156,17 @@ export class PluginHandler {
|
|
|
93
156
|
return results;
|
|
94
157
|
}
|
|
95
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Execute plugin command from message
|
|
161
|
+
* @async
|
|
162
|
+
* @param {Object} m - Serialized message object
|
|
163
|
+
* @returns {Promise<void>}
|
|
164
|
+
* @example
|
|
165
|
+
* // This is automatically called by Client when message starts with prefix
|
|
166
|
+
* client.on('message', async (m) => {
|
|
167
|
+
* await handler.execute(m);
|
|
168
|
+
* });
|
|
169
|
+
*/
|
|
96
170
|
async execute(m) {
|
|
97
171
|
if (!m.body) return;
|
|
98
172
|
|
|
@@ -158,6 +232,15 @@ export class PluginHandler {
|
|
|
158
232
|
}
|
|
159
233
|
}
|
|
160
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Check if user is bot owner
|
|
237
|
+
* @param {string} jid - User JID to check
|
|
238
|
+
* @returns {boolean} True if user is owner
|
|
239
|
+
* @example
|
|
240
|
+
* if (handler.isOwner(m.sender)) {
|
|
241
|
+
* // Owner-only logic
|
|
242
|
+
* }
|
|
243
|
+
*/
|
|
161
244
|
isOwner(jid) {
|
|
162
245
|
const owners = this.client.config.owners || this.client.config.owner || [];
|
|
163
246
|
const ownerList = Array.isArray(owners) ? owners : [owners];
|
|
@@ -165,6 +248,16 @@ export class PluginHandler {
|
|
|
165
248
|
return ownerList.includes(number) || ownerList.includes(jid);
|
|
166
249
|
}
|
|
167
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Reload/unload a plugin by name
|
|
253
|
+
* @param {string} pluginName - Name of plugin to reload
|
|
254
|
+
* @returns {boolean} True if plugin was found and reloaded
|
|
255
|
+
* @example
|
|
256
|
+
* if (handler.reload('ping')) {
|
|
257
|
+
* await handler.load('./plugins/ping.js');
|
|
258
|
+
* console.log('Plugin reloaded');
|
|
259
|
+
* }
|
|
260
|
+
*/
|
|
168
261
|
reload(pluginName) {
|
|
169
262
|
const plugin = this.plugins.get(pluginName);
|
|
170
263
|
if (!plugin) return false;
|
|
@@ -178,10 +271,27 @@ export class PluginHandler {
|
|
|
178
271
|
return true;
|
|
179
272
|
}
|
|
180
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Get list of all loaded plugins
|
|
276
|
+
* @returns {Array<Plugin>} Array of plugin objects
|
|
277
|
+
* @example
|
|
278
|
+
* const plugins = handler.list();
|
|
279
|
+
* plugins.forEach(p => console.log(p.name));
|
|
280
|
+
*/
|
|
181
281
|
list() {
|
|
182
282
|
return Array.from(this.plugins.values());
|
|
183
283
|
}
|
|
184
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Get a specific plugin by name
|
|
287
|
+
* @param {string} name - Plugin name
|
|
288
|
+
* @returns {Plugin|undefined} Plugin object or undefined if not found
|
|
289
|
+
* @example
|
|
290
|
+
* const pingPlugin = handler.get('ping');
|
|
291
|
+
* if (pingPlugin) {
|
|
292
|
+
* console.log('Commands:', pingPlugin.commands);
|
|
293
|
+
* }
|
|
294
|
+
*/
|
|
185
295
|
get(name) {
|
|
186
296
|
return this.plugins.get(name);
|
|
187
297
|
}
|