@shoru/kitten 0.0.3 → 0.0.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="left">
2
2
 
3
- <img src="https://files.catbox.moe/j7dpni.png" align="right" width="200" alt="Kitten Logo">
3
+ <img src="https://files.catbox.moe/j7dpni.png" align="right" width="220" alt="Kitten Logo">
4
4
 
5
5
  <a name="-kitten"></a>
6
6
  # **Kitten Framework**
@@ -15,10 +15,602 @@
15
15
 
16
16
  ---
17
17
 
18
- <br clear="left"/>
18
+ ## Table of Contents
19
+
20
+ 1. [Introduction](#introduction)
21
+ 2. [Getting Started](#getting-started)
22
+ 3. [Configuration](#configuration)
23
+ 4. [Plugin System](#plugin-system)
24
+ 5. [Message Formatting](#message-formatting)
25
+ 6. [Session Management](#session-management)
26
+ 7. [Utilities](#utilities)
27
+ 8. [API Reference](#api-reference)
28
+
29
+ ---
30
+
31
+ ## Introduction
32
+
33
+ Kitten simplifies WhatsApp bot development by providing:
34
+
35
+ - **Persistent Sessions** — LMDB-powered storage with automatic recovery
36
+ - **Smart Reconnection** — Exponential backoff with configurable retry limits
37
+ - **Plugin System** — File-based plugins with Hot Module Replacement
38
+ - **Multi-Session** — Automatic restoration of all saved sessions
39
+ - **Flexible Auth** — QR code or pairing code authentication
40
+
41
+ ---
42
+
43
+ ## Getting Started
44
+
45
+ ### Installation
46
+
47
+ ```bash
48
+ npm install @shoru/kitten
49
+ ```
50
+
51
+ ### Basic Usage
52
+
53
+ ```javascript
54
+ import { getClient } from '@shoru/kitten';
55
+
56
+ const { sock, session, id } = await getClient();
57
+
58
+ sock.ev.on('messages.upsert', async ({ messages }) => {
59
+ const msg = messages[0];
60
+ if (msg.message?.conversation === 'ping') {
61
+ await sock.sendMessage(msg.key.remoteJid, { text: 'pong!' });
62
+ }
63
+ });
64
+ ```
65
+
66
+ > **Note:** `getClient()` automatically restores all previously saved sessions in the background.
67
+
68
+ ### With Options
69
+
70
+ ```javascript
71
+ import { getClient } from '@shoru/kitten';
72
+
73
+ const { sock, session, id } = await getClient({
74
+ id: 0, // Session ID (auto-generated if omitted)
75
+ maxRetries: 30, // Reconnection attempts
76
+ silent: false, // Suppress output
77
+ socketConfig: {}, // Baileys socket overrides
78
+
79
+ // Callbacks
80
+ onConnect: ({ client }) => {
81
+ console.log(`Connected with session ${client.id}`);
82
+ },
83
+ onReconnect: ({ client, attempts }) => {
84
+ console.log(`Reconnected after ${attempts} attempts`);
85
+ },
86
+ onDisconnect: ({ message, statusCode, recoverable }) => {
87
+ console.log(`Disconnected: ${message}`);
88
+ },
89
+ onStateChange: ({ oldState, newState }) => {
90
+ console.log(`State: ${oldState} → ${newState}`);
91
+ }
92
+ });
93
+ ```
94
+
95
+ ### Custom Authentication
96
+
97
+ ```javascript
98
+ import qrcode from 'qrcode-terminal';
99
+
100
+ const { sock, id } = await getClient({
101
+ onPairing: async ({ qr, requestPairingCode }) => {
102
+ // Option 1: Display QR code
103
+ console.log('Scan QR:');
104
+ qrcode.generate(qr, { small: true });
105
+
106
+ // Option 2: Use pairing code
107
+ const code = await requestPairingCode('1234567890');
108
+ console.log('Enter this code in WhatsApp:', code);
109
+ }
110
+ });
111
+ ```
112
+
113
+ ### Return Value
114
+
115
+ ```javascript
116
+ const { sock, session, id } = await getClient();
117
+
118
+ sock // Baileys WASocket instance
119
+ id // Numeric session identifier
120
+ session.id // Same as id
121
+ await session.delete() // Remove session from database
122
+ await session.clear() // Clear keys, keep credentials
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Configuration
128
+
129
+ Kitten uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). Create one of:
130
+
131
+ - `kittenwa.config.js`
132
+ - `kittenwa.config.mjs`
133
+ - `.kittenwarc.json`
134
+ - `package.json` → `"kittenwa": {}`
135
+
136
+ ### Default Values
137
+
138
+ ```javascript
139
+ export default {
140
+ socket: {
141
+ browser: Browsers.ubuntu('Chrome'),
142
+ markOnlineOnConnect: false,
143
+ syncFullHistory: false,
144
+ generateHighQualityLinkPreview: true,
145
+ logger: pino({ level: 'silent' })
146
+ },
147
+
148
+ db: {
149
+ path: './db',
150
+ compression: true,
151
+ mapSize: 2 * 1024 * 1024 * 1024 // 2GB
152
+ },
153
+
154
+ plugins: {
155
+ dir: 'plugins',
156
+ prefixes: ['.', '\\', '!'],
157
+ defaultEvent: 'messages.upsert',
158
+ hmr: {
159
+ enable: false,
160
+ debounce: 200,
161
+ debug: false
162
+ }
163
+ },
164
+
165
+ timeZone: 'Africa/Casablanca'
166
+ }
167
+ ```
168
+
169
+ ### Custom Configuration
170
+
171
+ ```javascript
172
+ // kittenwa.config.js
173
+ export default {
174
+ plugins: {
175
+ dir: 'src/plugins',
176
+ prefixes: ['/', '!'],
177
+ hmr: { enable: true }
178
+ },
179
+ timeZone: 'America/New_York'
180
+ };
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Plugin System
186
+
187
+ File-based plugins with automatic loading and optional Hot Module Replacement.
188
+
189
+ ### Directory Structure
190
+
191
+ ```
192
+ plugins/
193
+ ├── greetings/
194
+ │ ├── hello.js
195
+ │ └── goodbye.js
196
+ ├── admin/
197
+ │ └── ban.js
198
+ └── utils.js
199
+ ```
200
+
201
+ ### Basic Plugin
202
+
203
+ ```javascript
204
+ // plugins/ping.js
205
+ export function ping(sock, ctx, event) {
206
+ if (ctx.body === '!ping') {
207
+ sock.sendMessage(ctx.roomId, { text: 'pong!' });
208
+ }
209
+ }
210
+ ```
211
+
212
+ ### Plugin with Options
213
+
214
+ ```javascript
215
+ // plugins/commands.js
216
+ export const help = async (sock, ctx) => {
217
+ await sock.sendMessage(ctx.roomId, {
218
+ text: 'Available: !help, !ping, !info'
219
+ });
220
+ }
221
+
222
+ help.match: ['help', 'h', '?'];
223
+ help.prefix: ['!', '/'];
224
+ help.events: ['messages.upsert'];
225
+ help.enabled: true;
226
+ ```
227
+
228
+ ### Options Reference
229
+
230
+ | Option | Type | Default | Description |
231
+ |--------|------|---------|-------------|
232
+ | `match` | `(string \| RegExp)[]` | `[]` | Command triggers |
233
+ | `prefix` | `string[] \| false` | Config value | Required prefixes |
234
+ | `events` | `string[]` | `['messages.upsert']` | Events to listen |
235
+ | `enabled` | `boolean` | `true` | Toggle plugin |
236
+
237
+ ### Regex Matching
238
+
239
+ ```javascript
240
+ export const urlDetector = (sock, ctx) => {
241
+ const match = ctx._match.match; // RegExp exec result
242
+ console.log('URL found:', match[0]);
243
+ }
244
+
245
+ urlDetector.match = [/https?:\/\/\S+/gi];
246
+ ```
247
+
248
+ ### Match Context
249
+
250
+ When a command matches, `ctx._match` is added:
251
+
252
+ ```javascript
253
+ // User sends: "!help me"
254
+ ctx._match = {
255
+ match: 'help', // Matched string or RegExp result
256
+ prefix: '!' // Used prefix (null for regex)
257
+ }
258
+ ```
259
+
260
+ ### Supported Events
261
+
262
+ ```
263
+ messages.upsert messages.update messages.delete
264
+ messages.reaction message-receipt.update messaging-history.set
265
+ chats.upsert chats.update chats.delete
266
+ contacts.upsert contacts.update groups.update
267
+ group-participants.update presence.update
268
+ connection.update creds.update call
269
+ blocklist.set blocklist.update
270
+ ```
271
+
272
+ ### Hot Module Replacement
273
+
274
+ Enable for development:
275
+
276
+ ```javascript
277
+ // kittenwa.config.js
278
+ export default {
279
+ plugins: {
280
+ hmr: {
281
+ enable: true,
282
+ debounce: 200,
283
+ debug: true
284
+ }
285
+ }
286
+ };
287
+ ```
288
+
289
+ Console output:
290
+ ```
291
+ [HMR] Added: greetings/hello.js (2)
292
+ [HMR] Reloaded: commands.js (3)
293
+ [HMR] Unloaded: old-plugin.js (1)
294
+ ```
295
+
296
+ ### Naming Rules
297
+
298
+ | Pattern | Status |
299
+ |---------|--------|
300
+ | `command.js` | ✅ Loaded |
301
+ | `command.ts` | ✅ Loaded |
302
+ | `_helper.js` | ❌ Ignored |
303
+ | `types.d.ts` | ❌ Ignored |
304
+
305
+ ---
306
+
307
+ ## Message Formatting
308
+
309
+ Normalize raw Baileys messages into a consistent structure.
310
+
311
+ ### Usage
312
+
313
+ ```javascript
314
+ import { formatter } from '@shoru/kitten';
315
+
316
+ sock.ev.on('messages.upsert', ({ messages }) => {
317
+ const msg = formatter(sock, messages[0]);
318
+
319
+ console.log(msg.name); // Sender name
320
+ console.log(msg.body); // Text content
321
+ console.log(msg.isGroup); // Boolean
322
+ });
323
+ ```
324
+
325
+ ### Message Structure
326
+
327
+ ```javascript
328
+ {
329
+ // Identity
330
+ type: 'conversation',
331
+ id: 'ABC123',
332
+ name: 'John',
333
+ jid: '123@s.whatsapp.net',
334
+ fromMe: false,
335
+
336
+ // Chat
337
+ roomId: '123@s.whatsapp.net',
338
+ isGroup: false,
339
+ broadcast: false,
340
+
341
+ // Content
342
+ body: 'Hello!',
343
+ mentions: [],
344
+ mimetype: 'image/jpeg',
345
+ fileName: 'photo.jpg',
346
+ fileLength: 12345,
347
+ hash: 'abc123...',
348
+ isViewOnce: false,
349
+ thumbnail: Buffer,
350
+
351
+ // Forwarding
352
+ isForwarded: false,
353
+ forwardingScore: 0,
354
+
355
+ // Timing
356
+ timestamp: 1699999999,
357
+ timeString: ['November 14, 2023', '12:26:39'],
358
+
359
+ // Device
360
+ device: 'android',
361
+ isLid: false,
362
+
363
+ // Raw data
364
+ key: { ... },
365
+ raw: { ... },
366
+ contextInfo: { ... },
367
+
368
+ // Methods
369
+ async load() → Buffer,
370
+ senderIs(jid) → boolean,
371
+ async pn() → string,
372
+
373
+ // Quoted message (when replying)
374
+ quoted: { /* same structure */ }
375
+ }
376
+ ```
377
+
378
+ ### Common Patterns
379
+
380
+ ```javascript
381
+ // Download media
382
+ if (msg.mimetype?.startsWith('image/')) {
383
+ const buffer = await msg.load();
384
+ fs.writeFileSync('image.jpg', buffer);
385
+ }
386
+
387
+ // Handle replies
388
+ if (msg.quoted) {
389
+ console.log('Replying to:', msg.quoted.body);
390
+ }
391
+
392
+ // Check sender
393
+ if (msg.senderIs(adminJid)) {
394
+ // Execute admin command
395
+ }
396
+
397
+ // Get phone number
398
+ const phone = await msg.pn();
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Session Management
404
+
405
+ Sessions persist automatically in LMDB and are restored on startup.
406
+
407
+ ### Automatic Restoration
408
+
409
+ When you call `getClient()`, all previously saved sessions are automatically restored in the background. No manual iteration required.
410
+
411
+ ```javascript
412
+ import { getClient } from '@shoru/kitten';
413
+
414
+ // This connects the current session AND restores all others
415
+ const { sock } = await getClient({ id: 0 });
416
+ ```
417
+
418
+ ### Session Utilities
419
+
420
+ ```javascript
421
+ import { listSessions, sessionExists } from '@shoru/kitten';
422
+
423
+ // List all saved session IDs
424
+ const ids = listSessions(); // [0, 1, 2]
425
+
426
+ // Check if a session exists
427
+ if (sessionExists(1)) {
428
+ console.log('Session 1 exists');
429
+ }
430
+ ```
431
+
432
+ ### Session Operations
433
+
434
+ ```javascript
435
+ const { session } = await getClient();
436
+
437
+ session.id // Numeric identifier
438
+ await session.clear() // Clear keys, keep credentials
439
+ await session.delete() // Remove entire session
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Utilities
445
+
446
+ ### Logger
447
+
448
+ ```javascript
449
+ import { logger } from '@shoru/kitten';
450
+
451
+ logger.info('Information');
452
+ logger.debug({ data }, 'Context');
453
+ logger.warn('Warning');
454
+ logger.error(err, 'Error');
455
+ logger.prompt('Direct output');
456
+ ```
457
+
458
+ ### Database
459
+
460
+ ```javascript
461
+ import { LMDBManager } from '@shoru/kitten';
462
+
463
+ const { db } = LMDBManager;
464
+
465
+ const value = db.get('key');
466
+ await db.put('key', 'value');
467
+ await db.remove('key');
468
+
469
+ LMDBManager.isOpen // Boolean
470
+ await LMDBManager.close();
471
+ ```
472
+
473
+ ### Configuration
474
+
475
+ ```javascript
476
+ import { getConfig } from '@shoru/kitten';
477
+
478
+ const config = await getConfig();
479
+ console.log(config.plugins.dir);
480
+ ```
481
+
482
+ ### Serialization
483
+
484
+ ```javascript
485
+ import { serialize, deserialize } from '@shoru/kitten';
486
+
487
+ const json = serialize({ buffer: Buffer.from('hello') });
488
+ const data = deserialize(json);
489
+ ```
490
+
491
+ ### Time Formatting
492
+
493
+ ```javascript
494
+ import { getTimeString } from '@shoru/kitten';
495
+
496
+ const [date, time] = getTimeString(1699999999);
497
+ // ['November 14, 2023', '12:26:39']
498
+ ```
499
+
500
+ ### Type Helpers
501
+
502
+ ```javascript
503
+ import { isString, toNumber, toBase64 } from '@shoru/kitten';
504
+
505
+ isString('hello') // true
506
+ toNumber(BigInt(123)) // 123
507
+ toBase64(Buffer.from('hi')) // 'aGk='
508
+ ```
509
+
510
+ ### Phone Number
511
+
512
+ ```javascript
513
+ import { getPN } from '@shoru/kitten';
514
+
515
+ const phone = await getPN(sock, jid); // '1234567890'
516
+ ```
517
+
518
+ ### Spinner
519
+
520
+ ```javascript
521
+ import { spinner, pauseSpinner } from '@shoru/kitten';
522
+
523
+ spinner.start('Loading...');
524
+ spinner.stop();
525
+
526
+ // Pause during prompts
527
+ const result = await pauseSpinner(async () => {
528
+ return await askUser();
529
+ });
530
+ ```
531
+
532
+ ---
533
+
534
+ ## API Reference
535
+
536
+ ### Primary
537
+
538
+ ```javascript
539
+ import { getClient } from '@shoru/kitten';
540
+
541
+ await getClient(options?) → { sock, session, id }
542
+ ```
543
+
544
+ ### Sessions
545
+
546
+ ```javascript
547
+ import { listSessions, sessionExists } from '@shoru/kitten';
548
+
549
+ listSessions() → number[]
550
+ sessionExists(id) → boolean
551
+ ```
552
+
553
+ ### Formatting
554
+
555
+ ```javascript
556
+ import { formatter } from '@shoru/kitten';
557
+
558
+ formatter(sock, rawMessage, eventName?) → FormattedMessage
559
+ ```
560
+
561
+ ### Infrastructure
562
+
563
+ ```javascript
564
+ import {
565
+ getConfig,
566
+ logger,
567
+ LMDBManager,
568
+ spinner,
569
+ pauseSpinner
570
+ } from '@shoru/kitten';
571
+ ```
572
+
573
+ ### Utilities
574
+
575
+ ```javascript
576
+ import {
577
+ serialize,
578
+ deserialize,
579
+ getTimeString,
580
+ isString,
581
+ toNumber,
582
+ toBase64,
583
+ getPN
584
+ } from '@shoru/kitten';
585
+ ```
586
+
587
+ ---
588
+
589
+ ## Complete Example
590
+
591
+ ```javascript
592
+ import { getClient, logger } from '@shoru/kitten';
593
+
594
+ const { sock, id } = await getClient({
595
+ id: 0,
596
+ maxRetries: 15,
597
+ onConnect: ({ client }) => {
598
+ logger.info(`Session ${client.id} connected`);
599
+ },
600
+ onDisconnect: ({ message, recoverable }) => {
601
+ logger.warn(`Disconnected: ${message} (recoverable: ${recoverable})`);
602
+ }
603
+ });
604
+
605
+ logger.info(`Running as session ${id}`);
606
+ ```
607
+
608
+ ---
19
609
 
20
610
  <div align="center">
21
611
 
612
+ </br>
613
+
614
+ **Made with ❤️ for Whatsapp Community by [Aymane Shoru](https://github.com/Mangaka-bot)**
22
615
 
23
- **Made with ❤️ for Whatsapp community**
24
616
  </div>
package/package.json CHANGED
@@ -1,41 +1,42 @@
1
- {
2
- "name": "@shoru/kitten",
3
- "type": "module",
4
- "version": "0.0.3",
5
- "description": "A powerful Node.js framework to simplify making WhatsApp Bots",
6
- "main": "src/index.js",
7
- "repository": {
8
- "url": "git+https://github.com/Mangaka-bot/kitten-wa.git"
9
- },
10
- "scripts": {
11
- "start": "node --no-warnings src/index.js",
12
- "test": "echo \"Error: no test specified\" && exit 1"
13
- },
14
- "imports": {
15
- "#utils.js": "./src/utils/index.js",
16
- "#auth.js": "./src/auth/index.js",
17
- "#internals.js": "./src/internals/index.js",
18
- "#client.js": "./src/client/index.js",
19
- "#formatter.js": "./src/formatter/index.js"
20
- },
21
- "exports": {
22
- ".": "./src/index.js"
23
- },
24
- "author": "Aymane Shoru",
25
- "license": "MIT",
26
- "dependencies": {
27
- "@hapi/boom": "^10.0.1",
28
- "@inquirer/prompts": "^8.1.0",
29
- "@shoru/spindle": "^1.0.6",
30
- "async-mutex": "^0.5.0",
31
- "baileys": "^7.0.0-rc.9",
32
- "chalk": "^5.6.2",
33
- "chokidar": "^5.0.0",
34
- "cosmiconfig": "^9.0.0",
35
- "defu": "^6.1.4",
36
- "lmdb": "^3.4.4",
37
- "pino": "^10.1.0",
38
- "pino-pretty": "^13.1.3",
39
- "qrcode-terminal": "^0.12.0"
40
- }
41
- }
1
+ {
2
+ "name": "@shoru/kitten",
3
+ "type": "module",
4
+ "version": "0.0.5",
5
+ "description": "A powerful Node.js framework to simplify making WhatsApp Bots",
6
+ "main": "src/index.js",
7
+ "repository": {
8
+ "url": "git+https://github.com/Mangaka-bot/kitten-wa.git"
9
+ },
10
+ "scripts": {
11
+ "start": "node --no-warnings src/index.js",
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "imports": {
15
+ "#utils.js": "./src/utils/index.js",
16
+ "#auth.js": "./src/auth/index.js",
17
+ "#internals.js": "./src/internals/index.js",
18
+ "#client.js": "./src/client/index.js",
19
+ "#formatter.js": "./src/formatter/index.js",
20
+ "#plugins.js": "./src/plugins/index.js"
21
+ },
22
+ "exports": {
23
+ ".": "./src/index.js"
24
+ },
25
+ "author": "Aymane Shoru",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "@hapi/boom": "^10.0.1",
29
+ "@inquirer/prompts": "^8.1.0",
30
+ "@shoru/spindle": "^1.0.6",
31
+ "async-mutex": "^0.5.0",
32
+ "baileys": "^7.0.0-rc.9",
33
+ "chalk": "^5.6.2",
34
+ "chokidar": "^5.0.0",
35
+ "cosmiconfig": "^9.0.0",
36
+ "defu": "^6.1.4",
37
+ "lmdb": "^3.4.4",
38
+ "pino": "^10.1.0",
39
+ "pino-pretty": "^13.1.3",
40
+ "qrcode-terminal": "^0.12.0"
41
+ }
42
+ }
@@ -2,7 +2,8 @@ import { DisconnectReason } from 'baileys';
2
2
  import { Boom } from '@hapi/boom';
3
3
  import qrcode from 'qrcode-terminal';
4
4
  import chalk from 'chalk';
5
- import { logger, pino, pluginManager } from '#internals.js';
5
+ import { pluginManager } from '#plugins.js';
6
+ import { logger, pino } from '#internals.js';
6
7
  import { initSession, listSessions } from '#auth.js';
7
8
  import { getConnectionConfig } from './getConnectionConfig.js';
8
9