@roidev/kachina-md 2.0.5 → 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 +345 -4
- 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 +11 -4
package/lib/client/Client.js
CHANGED
|
@@ -2,7 +2,8 @@ import makeWASocket, {
|
|
|
2
2
|
DisconnectReason,
|
|
3
3
|
useMultiFileAuthState,
|
|
4
4
|
makeCacheableSignalKeyStore,
|
|
5
|
-
fetchLatestBaileysVersion
|
|
5
|
+
fetchLatestBaileysVersion,
|
|
6
|
+
downloadMediaMessage
|
|
6
7
|
} from 'baileys';
|
|
7
8
|
import { Boom } from '@hapi/boom';
|
|
8
9
|
import pino from 'pino';
|
|
@@ -15,9 +16,56 @@ import {
|
|
|
15
16
|
StickerTypes
|
|
16
17
|
} from '../helpers/sticker.js';
|
|
17
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
|
+
*/
|
|
18
62
|
export class Client extends EventEmitter {
|
|
19
63
|
static StickerTypes = StickerTypes;
|
|
20
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Creates a new Client instance
|
|
67
|
+
* @param {ClientOptions} [options={}] - Client configuration options
|
|
68
|
+
*/
|
|
21
69
|
constructor(options = {}) {
|
|
22
70
|
super();
|
|
23
71
|
|
|
@@ -36,6 +84,18 @@ export class Client extends EventEmitter {
|
|
|
36
84
|
this.pluginHandler = new PluginHandler(this);
|
|
37
85
|
}
|
|
38
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
|
+
*/
|
|
39
99
|
async start() {
|
|
40
100
|
const { state, saveCreds } = await useMultiFileAuthState(this.config.sessionId);
|
|
41
101
|
const { version } = await fetchLatestBaileysVersion();
|
|
@@ -134,6 +194,15 @@ export class Client extends EventEmitter {
|
|
|
134
194
|
return this.sock;
|
|
135
195
|
}
|
|
136
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
|
+
*/
|
|
137
206
|
async handleConnectionUpdate(update) {
|
|
138
207
|
const { connection, lastDisconnect, qr } = update;
|
|
139
208
|
|
|
@@ -169,15 +238,45 @@ export class Client extends EventEmitter {
|
|
|
169
238
|
}
|
|
170
239
|
}
|
|
171
240
|
|
|
172
|
-
|
|
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
|
+
*/
|
|
173
251
|
async sendMessage(jid, content, options = {}) {
|
|
174
252
|
return await this.sock.sendMessage(jid, content, options);
|
|
175
253
|
}
|
|
176
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
|
+
*/
|
|
177
265
|
async sendText(jid, text, options = {}) {
|
|
178
266
|
return await this.sendMessage(jid, { text }, options);
|
|
179
267
|
}
|
|
180
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
|
+
*/
|
|
181
280
|
async sendImage(jid, buffer, caption = '', options = {}) {
|
|
182
281
|
const content = {
|
|
183
282
|
image: buffer,
|
|
@@ -187,6 +286,17 @@ export class Client extends EventEmitter {
|
|
|
187
286
|
return await this.sendMessage(jid, content, options);
|
|
188
287
|
}
|
|
189
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
|
+
*/
|
|
190
300
|
async sendVideo(jid, buffer, caption = '', options = {}) {
|
|
191
301
|
const content = {
|
|
192
302
|
video: buffer,
|
|
@@ -196,6 +306,21 @@ export class Client extends EventEmitter {
|
|
|
196
306
|
return await this.sendMessage(jid, content, options);
|
|
197
307
|
}
|
|
198
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
|
+
*/
|
|
199
324
|
async sendAudio(jid, buffer, options = {}) {
|
|
200
325
|
const content = {
|
|
201
326
|
audio: buffer,
|
|
@@ -206,6 +331,18 @@ export class Client extends EventEmitter {
|
|
|
206
331
|
return await this.sendMessage(jid, content, options);
|
|
207
332
|
}
|
|
208
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
|
+
*/
|
|
209
346
|
async sendDocument(jid, buffer, filename, mimetype, options = {}) {
|
|
210
347
|
return await this.sendMessage(jid, {
|
|
211
348
|
document: buffer,
|
|
@@ -215,7 +352,22 @@ export class Client extends EventEmitter {
|
|
|
215
352
|
});
|
|
216
353
|
}
|
|
217
354
|
|
|
218
|
-
|
|
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
|
+
*/
|
|
219
371
|
async sendSticker(jid, buffer, options = {}) {
|
|
220
372
|
const stickerBuffer = await createSticker(buffer, options);
|
|
221
373
|
return await this.sendMessage(jid, {
|
|
@@ -224,14 +376,37 @@ export class Client extends EventEmitter {
|
|
|
224
376
|
});
|
|
225
377
|
}
|
|
226
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
|
+
*/
|
|
227
392
|
async sendContact(jid, contacts, options = {}) {
|
|
228
|
-
// contacts = [{ displayName, vcard }]
|
|
229
393
|
return await this.sendMessage(jid, {
|
|
230
394
|
contacts: { contacts },
|
|
231
395
|
...options
|
|
232
396
|
});
|
|
233
397
|
}
|
|
234
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
|
+
*/
|
|
235
410
|
async sendLocation(jid, latitude, longitude, options = {}) {
|
|
236
411
|
return await this.sendMessage(jid, {
|
|
237
412
|
location: { degreesLatitude: latitude, degreesLongitude: longitude },
|
|
@@ -239,6 +414,18 @@ export class Client extends EventEmitter {
|
|
|
239
414
|
});
|
|
240
415
|
}
|
|
241
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
|
+
*/
|
|
242
429
|
async sendPoll(jid, name, values, options = {}) {
|
|
243
430
|
return await this.sendMessage(jid, {
|
|
244
431
|
poll: {
|
|
@@ -250,40 +437,194 @@ export class Client extends EventEmitter {
|
|
|
250
437
|
});
|
|
251
438
|
}
|
|
252
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
|
+
*/
|
|
253
450
|
async sendReact(jid, messageKey, emoji) {
|
|
254
451
|
return await this.sendMessage(jid, {
|
|
255
452
|
react: { text: emoji, key: messageKey }
|
|
256
453
|
});
|
|
257
454
|
}
|
|
258
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Read and download view once message
|
|
458
|
+
* @async
|
|
459
|
+
* @param {Object} quotedMessage - The quoted message object (m.quoted)
|
|
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
|
+
* }
|
|
467
|
+
*/
|
|
468
|
+
async readViewOnce(quotedMessage) {
|
|
469
|
+
if (!quotedMessage) {
|
|
470
|
+
throw new Error('Quoted message is required');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Get view once message
|
|
474
|
+
const ViewOnceImg = quotedMessage?.message?.imageMessage;
|
|
475
|
+
const ViewOnceVid = quotedMessage?.message?.videoMessage;
|
|
476
|
+
|
|
477
|
+
// Check if it's a view once message
|
|
478
|
+
if (!ViewOnceImg?.viewOnce && !ViewOnceVid?.viewOnce) {
|
|
479
|
+
throw new Error('Message is not a view once message');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Download the media
|
|
483
|
+
const buffer = await downloadMediaMessage(
|
|
484
|
+
quotedMessage,
|
|
485
|
+
'buffer',
|
|
486
|
+
{},
|
|
487
|
+
{ logger: this.config.logger }
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Return buffer with type and caption
|
|
491
|
+
return {
|
|
492
|
+
buffer,
|
|
493
|
+
type: ViewOnceImg ? 'image' : 'video',
|
|
494
|
+
caption: ViewOnceImg?.caption || ViewOnceVid?.caption || ''
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Read view once message and send to chat
|
|
500
|
+
* @async
|
|
501
|
+
* @param {string} jid - WhatsApp JID (chat ID) to send to
|
|
502
|
+
* @param {Object} quotedMessage - The quoted message object (m.quoted)
|
|
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);
|
|
509
|
+
*/
|
|
510
|
+
async sendViewOnce(jid, quotedMessage, options = {}) {
|
|
511
|
+
try {
|
|
512
|
+
// Read the view once message
|
|
513
|
+
const { buffer, type, caption } = await this.readViewOnce(quotedMessage);
|
|
514
|
+
|
|
515
|
+
// Send based on type
|
|
516
|
+
if (type === 'image') {
|
|
517
|
+
return await this.sendImage(jid, buffer, caption, {
|
|
518
|
+
jpegThumbnail: null,
|
|
519
|
+
...options
|
|
520
|
+
});
|
|
521
|
+
} else {
|
|
522
|
+
return await this.sendVideo(jid, buffer, caption, {
|
|
523
|
+
jpegThumbnail: null,
|
|
524
|
+
...options
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
} catch (error) {
|
|
528
|
+
throw new Error(`Failed to send view once: ${error.message}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
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
|
+
*/
|
|
259
541
|
async groupMetadata(jid) {
|
|
260
542
|
return await this.sock.groupMetadata(jid);
|
|
261
543
|
}
|
|
262
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
|
+
*/
|
|
263
558
|
async groupParticipantsUpdate(jid, participants, action) {
|
|
264
559
|
return await this.sock.groupParticipantsUpdate(jid, participants, action);
|
|
265
560
|
}
|
|
266
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
|
+
*/
|
|
267
571
|
async groupUpdateSubject(jid, subject) {
|
|
268
572
|
return await this.sock.groupUpdateSubject(jid, subject);
|
|
269
573
|
}
|
|
270
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
|
+
*/
|
|
271
584
|
async groupUpdateDescription(jid, description) {
|
|
272
585
|
return await this.sock.groupUpdateDescription(jid, description);
|
|
273
586
|
}
|
|
274
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
|
+
*/
|
|
275
596
|
async loadPlugin(path) {
|
|
276
597
|
await this.pluginHandler.load(path);
|
|
277
598
|
}
|
|
278
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
|
+
*/
|
|
279
608
|
async loadPlugins(directory) {
|
|
280
609
|
await this.pluginHandler.loadAll(directory);
|
|
281
610
|
}
|
|
282
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Get command prefix
|
|
614
|
+
* @returns {string} Current command prefix
|
|
615
|
+
* @example
|
|
616
|
+
* console.log(client.prefix); // '!'
|
|
617
|
+
*/
|
|
283
618
|
get prefix() {
|
|
284
619
|
return this.config.prefix || '!';
|
|
285
620
|
}
|
|
286
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Set command prefix
|
|
624
|
+
* @param {string} prefix - New command prefix
|
|
625
|
+
* @example
|
|
626
|
+
* client.prefix = '.';
|
|
627
|
+
*/
|
|
287
628
|
set prefix(prefix) {
|
|
288
629
|
this.config.prefix = prefix;
|
|
289
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
|
}
|