@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.
@@ -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
- // Helper methods
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
- * @param {string} jid - Chat ID to send to
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
  }