rynkai 0.1.4 → 0.1.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 +33 -1
- package/dist/core/Middleware.test.d.ts +2 -0
- package/dist/core/Middleware.test.d.ts.map +1 -0
- package/dist/core/Middleware.test.js +60 -0
- package/dist/core/Middleware.test.js.map +1 -0
- package/dist/core/RateLimiter.test.d.ts +2 -0
- package/dist/core/RateLimiter.test.d.ts.map +1 -0
- package/dist/core/RateLimiter.test.js +52 -0
- package/dist/core/RateLimiter.test.js.map +1 -0
- package/dist/core/SendQueue.test.d.ts +2 -0
- package/dist/core/SendQueue.test.d.ts.map +1 -0
- package/dist/core/SendQueue.test.js +60 -0
- package/dist/core/SendQueue.test.js.map +1 -0
- package/dist/message/MessageParser.test.d.ts +2 -0
- package/dist/message/MessageParser.test.d.ts.map +1 -0
- package/dist/message/MessageParser.test.js +85 -0
- package/dist/message/MessageParser.test.js.map +1 -0
- package/dist/plugin/PluginLoader.test.d.ts +2 -0
- package/dist/plugin/PluginLoader.test.d.ts.map +1 -0
- package/dist/plugin/PluginLoader.test.js +82 -0
- package/dist/plugin/PluginLoader.test.js.map +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -38,6 +38,8 @@ await bot.connect();
|
|
|
38
38
|
|
|
39
39
|
## Plugin system
|
|
40
40
|
|
|
41
|
+
Plugin format-nya sengaja mirip gaya rynk4: `{ name, command, category, execute() }`.
|
|
42
|
+
|
|
41
43
|
```ts
|
|
42
44
|
// plugins/hello.ts
|
|
43
45
|
import type { Plugin } from 'rynkai';
|
|
@@ -210,4 +212,34 @@ const bot = new Client({
|
|
|
210
212
|
});
|
|
211
213
|
|
|
212
214
|
bot.on('pairingCode', (code) => console.log('Kode pairing:', code));
|
|
213
|
-
```
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Testing
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
npm test # run sekali
|
|
221
|
+
npm run test:watch # mode watch
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Test coverage saat ini: `MessageParser`, `PluginLoader` (termasuk cooldown), `RateLimiter`, `Middleware` (compose/onion), `SendQueue`.
|
|
225
|
+
|
|
226
|
+
## Status
|
|
227
|
+
|
|
228
|
+
Masih tahap awal (0.1.0). Yang sudah ada:
|
|
229
|
+
- [x] Core client (connect, reconnect, QR/pairing code)
|
|
230
|
+
- [x] Normalized message parsing (text, media, quoted)
|
|
231
|
+
- [x] Plugin loader + cooldown
|
|
232
|
+
- [x] FileSessionStore default
|
|
233
|
+
- [x] MessageBuilder helper
|
|
234
|
+
- [x] Middleware/hooks sebelum plugin dieksekusi
|
|
235
|
+
- [x] Rate limiter global (terpisah dari cooldown per-plugin)
|
|
236
|
+
- [x] Media downloader helper (`client.downloadMedia()`)
|
|
237
|
+
- [x] Message send queue dengan throttle (anti rate-limit WA)
|
|
238
|
+
- [x] Presence helper (`sendTyping`, `sendPresence`)
|
|
239
|
+
- [x] Group helpers (metadata, add/remove/promote/demote, join-leave event)
|
|
240
|
+
- [x] Interactive messages (button, list, poll)
|
|
241
|
+
- [x] Test suite (MessageParser, PluginLoader, RateLimiter, Middleware, SendQueue)
|
|
242
|
+
|
|
243
|
+
Belum ada (rencana selanjutnya):
|
|
244
|
+
- [ ] CLI scaffold (`npx create-rynkai-bot`)
|
|
245
|
+
- [ ] GitHub Actions CI (auto build+test tiap push)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Middleware.test.d.ts","sourceRoot":"","sources":["../../src/core/Middleware.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const Middleware_1 = require("./Middleware");
|
|
5
|
+
function fakeCtx() {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
(0, vitest_1.describe)('compose (middleware onion model)', () => {
|
|
9
|
+
(0, vitest_1.it)('menjalankan middleware sesuai urutan pendaftaran, lalu final handler', async () => {
|
|
10
|
+
const order = [];
|
|
11
|
+
const dispatch = (0, Middleware_1.compose)([
|
|
12
|
+
async (_ctx, next) => {
|
|
13
|
+
order.push('mw1-before');
|
|
14
|
+
await next();
|
|
15
|
+
order.push('mw1-after');
|
|
16
|
+
},
|
|
17
|
+
async (_ctx, next) => {
|
|
18
|
+
order.push('mw2-before');
|
|
19
|
+
await next();
|
|
20
|
+
order.push('mw2-after');
|
|
21
|
+
},
|
|
22
|
+
], async () => {
|
|
23
|
+
order.push('final');
|
|
24
|
+
});
|
|
25
|
+
await dispatch(fakeCtx());
|
|
26
|
+
(0, vitest_1.expect)(order).toEqual(['mw1-before', 'mw2-before', 'final', 'mw2-after', 'mw1-after']);
|
|
27
|
+
});
|
|
28
|
+
(0, vitest_1.it)('final handler tidak jalan kalau middleware tidak memanggil next()', async () => {
|
|
29
|
+
const final = vitest_1.vi.fn();
|
|
30
|
+
const dispatch = (0, Middleware_1.compose)([
|
|
31
|
+
async () => {
|
|
32
|
+
// sengaja tidak panggil next()
|
|
33
|
+
},
|
|
34
|
+
], final);
|
|
35
|
+
await dispatch(fakeCtx());
|
|
36
|
+
(0, vitest_1.expect)(final).not.toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.it)('jalan langsung ke final kalau tidak ada middleware', async () => {
|
|
39
|
+
const final = vitest_1.vi.fn();
|
|
40
|
+
const dispatch = (0, Middleware_1.compose)([], final);
|
|
41
|
+
await dispatch(fakeCtx());
|
|
42
|
+
(0, vitest_1.expect)(final).toHaveBeenCalledOnce();
|
|
43
|
+
});
|
|
44
|
+
(0, vitest_1.it)('melempar error kalau next() dipanggil lebih dari sekali oleh middleware yang sama', async () => {
|
|
45
|
+
const dispatch = (0, Middleware_1.compose)([
|
|
46
|
+
async (_ctx, next) => {
|
|
47
|
+
await next();
|
|
48
|
+
await next(); // ini harus error
|
|
49
|
+
},
|
|
50
|
+
], async () => { });
|
|
51
|
+
await (0, vitest_1.expect)(dispatch(fakeCtx())).rejects.toThrow('next() dipanggil lebih dari sekali');
|
|
52
|
+
});
|
|
53
|
+
(0, vitest_1.it)('error dari middleware/final ter-propagate ke pemanggil dispatch', async () => {
|
|
54
|
+
const dispatch = (0, Middleware_1.compose)([], async () => {
|
|
55
|
+
throw new Error('gagal di final');
|
|
56
|
+
});
|
|
57
|
+
await (0, vitest_1.expect)(dispatch(fakeCtx())).rejects.toThrow('gagal di final');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=Middleware.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Middleware.test.js","sourceRoot":"","sources":["../../src/core/Middleware.test.ts"],"names":[],"mappings":";;AAAA,mCAAkD;AAClD,6CAAuC;AAGvC,SAAS,OAAO;IACd,OAAO,EAAmB,CAAC;AAC7B,CAAC;AAED,IAAA,iBAAQ,EAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,IAAA,WAAE,EAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,MAAM,QAAQ,GAAG,IAAA,oBAAO,EACtB;YACE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;gBACnB,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,MAAM,IAAI,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,CAAC;YACD,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;gBACnB,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,MAAM,IAAI,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,CAAC;SACF,EACD,KAAK,IAAI,EAAE;YACT,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC,CACF,CAAC;QAEF,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAE1B,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,KAAK,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC;QAEtB,MAAM,QAAQ,GAAG,IAAA,oBAAO,EACtB;YACE,KAAK,IAAI,EAAE;gBACT,+BAA+B;YACjC,CAAC;SACF,EACD,KAAK,CACN,CAAC;QAEF,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAE1B,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,KAAK,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAA,oBAAO,EAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAEpC,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAE1B,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,oBAAoB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACjG,MAAM,QAAQ,GAAG,IAAA,oBAAO,EACtB;YACE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;gBACnB,MAAM,IAAI,EAAE,CAAC;gBACb,MAAM,IAAI,EAAE,CAAC,CAAC,kBAAkB;YAClC,CAAC;SACF,EACD,KAAK,IAAI,EAAE,GAAE,CAAC,CACf,CAAC;QAEF,MAAM,IAAA,eAAM,EAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,QAAQ,GAAG,IAAA,oBAAO,EAAC,EAAE,EAAE,KAAK,IAAI,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,MAAM,IAAA,eAAM,EAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RateLimiter.test.d.ts","sourceRoot":"","sources":["../../src/core/RateLimiter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const RateLimiter_1 = require("./RateLimiter");
|
|
5
|
+
(0, vitest_1.describe)('RateLimiter', () => {
|
|
6
|
+
(0, vitest_1.beforeEach)(() => {
|
|
7
|
+
vitest_1.vi.useFakeTimers();
|
|
8
|
+
});
|
|
9
|
+
(0, vitest_1.afterEach)(() => {
|
|
10
|
+
vitest_1.vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
(0, vitest_1.it)('mengizinkan request selama masih di bawah max', () => {
|
|
13
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 3, windowMs: 60_000 });
|
|
14
|
+
const user = 'userA';
|
|
15
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(true);
|
|
16
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(true);
|
|
17
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
(0, vitest_1.it)('menolak request setelah melebihi max dalam satu window', () => {
|
|
20
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 2, windowMs: 60_000 });
|
|
21
|
+
const user = 'userA';
|
|
22
|
+
limiter.consume(user);
|
|
23
|
+
limiter.consume(user);
|
|
24
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
(0, vitest_1.it)('reset otomatis setelah window berakhir', () => {
|
|
27
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 1, windowMs: 1000 });
|
|
28
|
+
const user = 'userA';
|
|
29
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(true);
|
|
30
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(false);
|
|
31
|
+
vitest_1.vi.advanceTimersByTime(1001);
|
|
32
|
+
(0, vitest_1.expect)(limiter.consume(user)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)('tidak saling mempengaruhi antar user berbeda', () => {
|
|
35
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 1, windowMs: 60_000 });
|
|
36
|
+
(0, vitest_1.expect)(limiter.consume('userA')).toBe(true);
|
|
37
|
+
(0, vitest_1.expect)(limiter.consume('userB')).toBe(true);
|
|
38
|
+
(0, vitest_1.expect)(limiter.consume('userA')).toBe(false);
|
|
39
|
+
(0, vitest_1.expect)(limiter.consume('userB')).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
(0, vitest_1.it)('retryAfter mengembalikan 0 kalau user belum pernah request', () => {
|
|
42
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 1, windowMs: 60_000 });
|
|
43
|
+
(0, vitest_1.expect)(limiter.retryAfter('userBaru')).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)('retryAfter mengembalikan sisa detik yang mendekati window', () => {
|
|
46
|
+
const limiter = new RateLimiter_1.RateLimiter({ max: 1, windowMs: 10_000 });
|
|
47
|
+
limiter.consume('userA');
|
|
48
|
+
vitest_1.vi.advanceTimersByTime(4000);
|
|
49
|
+
(0, vitest_1.expect)(limiter.retryAfter('userA')).toBe(6);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=RateLimiter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RateLimiter.test.js","sourceRoot":"","sources":["../../src/core/RateLimiter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SendQueue.test.d.ts","sourceRoot":"","sources":["../../src/core/SendQueue.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const SendQueue_1 = require("./SendQueue");
|
|
5
|
+
(0, vitest_1.describe)('SendQueue', () => {
|
|
6
|
+
(0, vitest_1.beforeEach)(() => {
|
|
7
|
+
vitest_1.vi.useFakeTimers();
|
|
8
|
+
});
|
|
9
|
+
(0, vitest_1.afterEach)(() => {
|
|
10
|
+
vitest_1.vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
(0, vitest_1.it)('menjalankan task dan resolve dengan hasilnya', async () => {
|
|
13
|
+
const queue = new SendQueue_1.SendQueue({ intervalMs: 100 });
|
|
14
|
+
const result = await queue.push(async () => 'hasil');
|
|
15
|
+
(0, vitest_1.expect)(result).toBe('hasil');
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('menjalankan task secara berurutan (FIFO) dengan jeda antar task', async () => {
|
|
18
|
+
const queue = new SendQueue_1.SendQueue({ intervalMs: 100 });
|
|
19
|
+
const order = [];
|
|
20
|
+
const p1 = queue.push(async () => {
|
|
21
|
+
order.push(1);
|
|
22
|
+
});
|
|
23
|
+
const p2 = queue.push(async () => {
|
|
24
|
+
order.push(2);
|
|
25
|
+
});
|
|
26
|
+
const p3 = queue.push(async () => {
|
|
27
|
+
order.push(3);
|
|
28
|
+
});
|
|
29
|
+
await vitest_1.vi.runAllTimersAsync();
|
|
30
|
+
await Promise.all([p1, p2, p3]);
|
|
31
|
+
(0, vitest_1.expect)(order).toEqual([1, 2, 3]);
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)('reject promise kalau task melempar error, tapi tidak menghentikan antrian', async () => {
|
|
34
|
+
const queue = new SendQueue_1.SendQueue({ intervalMs: 10 });
|
|
35
|
+
const order = [];
|
|
36
|
+
const p1 = queue.push(async () => {
|
|
37
|
+
throw new Error('task 1 gagal');
|
|
38
|
+
});
|
|
39
|
+
p1.catch(() => { }); // pasang handler segera, cegah warning unhandled rejection dari Node
|
|
40
|
+
const p2 = queue.push(async () => {
|
|
41
|
+
order.push('task2 jalan');
|
|
42
|
+
});
|
|
43
|
+
await vitest_1.vi.runAllTimersAsync();
|
|
44
|
+
await (0, vitest_1.expect)(p1).rejects.toThrow('task 1 gagal');
|
|
45
|
+
await (0, vitest_1.expect)(p2).resolves.toBeUndefined();
|
|
46
|
+
(0, vitest_1.expect)(order).toEqual(['task2 jalan']);
|
|
47
|
+
});
|
|
48
|
+
(0, vitest_1.it)('pending mencerminkan jumlah task yang masih menunggu', async () => {
|
|
49
|
+
const queue = new SendQueue_1.SendQueue({ intervalMs: 1000 });
|
|
50
|
+
queue.push(async () => { });
|
|
51
|
+
queue.push(async () => { });
|
|
52
|
+
const thirdTask = queue.push(async () => { });
|
|
53
|
+
// task pertama langsung mulai diproses, sisanya nunggu di antrian
|
|
54
|
+
(0, vitest_1.expect)(queue.pending).toBeGreaterThanOrEqual(1);
|
|
55
|
+
await vitest_1.vi.runAllTimersAsync();
|
|
56
|
+
await thirdTask;
|
|
57
|
+
(0, vitest_1.expect)(queue.pending).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=SendQueue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SendQueue.test.js","sourceRoot":"","sources":["../../src/core/SendQueue.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageParser.test.d.ts","sourceRoot":"","sources":["../../src/message/MessageParser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const MessageParser_1 = require("./MessageParser");
|
|
5
|
+
function fakeRaw(overrides = {}) {
|
|
6
|
+
return {
|
|
7
|
+
key: {
|
|
8
|
+
id: 'MSG1',
|
|
9
|
+
remoteJid: '628123456789@s.whatsapp.net',
|
|
10
|
+
fromMe: false,
|
|
11
|
+
},
|
|
12
|
+
message: {
|
|
13
|
+
conversation: 'halo bot',
|
|
14
|
+
},
|
|
15
|
+
messageTimestamp: 1700000000,
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
(0, vitest_1.describe)('parseMessage', () => {
|
|
20
|
+
(0, vitest_1.it)('parse pesan text biasa dari chat pribadi', () => {
|
|
21
|
+
const result = (0, MessageParser_1.parseMessage)(fakeRaw());
|
|
22
|
+
(0, vitest_1.expect)(result.id).toBe('MSG1');
|
|
23
|
+
(0, vitest_1.expect)(result.chatId).toBe('628123456789@s.whatsapp.net');
|
|
24
|
+
(0, vitest_1.expect)(result.sender).toBe('628123456789@s.whatsapp.net');
|
|
25
|
+
(0, vitest_1.expect)(result.isGroup).toBe(false);
|
|
26
|
+
(0, vitest_1.expect)(result.type).toBe('text');
|
|
27
|
+
(0, vitest_1.expect)(result.text).toBe('halo bot');
|
|
28
|
+
(0, vitest_1.expect)(result.fromMe).toBe(false);
|
|
29
|
+
(0, vitest_1.expect)(result.quoted).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.it)('deteksi pesan dari grup dan ambil sender dari participant', () => {
|
|
32
|
+
const raw = fakeRaw({
|
|
33
|
+
key: {
|
|
34
|
+
id: 'MSG2',
|
|
35
|
+
remoteJid: '120363000000000000@g.us',
|
|
36
|
+
participant: '628999999999@s.whatsapp.net',
|
|
37
|
+
fromMe: false,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const result = (0, MessageParser_1.parseMessage)(raw);
|
|
41
|
+
(0, vitest_1.expect)(result.isGroup).toBe(true);
|
|
42
|
+
(0, vitest_1.expect)(result.chatId).toBe('120363000000000000@g.us');
|
|
43
|
+
(0, vitest_1.expect)(result.sender).toBe('628999999999@s.whatsapp.net');
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)('ambil caption sebagai text untuk pesan image', () => {
|
|
46
|
+
const raw = fakeRaw({
|
|
47
|
+
message: { imageMessage: { caption: 'lihat ini' } },
|
|
48
|
+
});
|
|
49
|
+
const result = (0, MessageParser_1.parseMessage)(raw);
|
|
50
|
+
(0, vitest_1.expect)(result.type).toBe('image');
|
|
51
|
+
(0, vitest_1.expect)(result.text).toBe('lihat ini');
|
|
52
|
+
});
|
|
53
|
+
(0, vitest_1.it)('return type unknown kalau tidak ada field message yang dikenali', () => {
|
|
54
|
+
const raw = fakeRaw({ message: {} });
|
|
55
|
+
const result = (0, MessageParser_1.parseMessage)(raw);
|
|
56
|
+
(0, vitest_1.expect)(result.type).toBe('unknown');
|
|
57
|
+
(0, vitest_1.expect)(result.text).toBe('');
|
|
58
|
+
});
|
|
59
|
+
(0, vitest_1.it)('parse pesan reply/quoted dengan benar', () => {
|
|
60
|
+
const raw = fakeRaw({
|
|
61
|
+
message: {
|
|
62
|
+
extendedTextMessage: {
|
|
63
|
+
text: 'balasanku',
|
|
64
|
+
contextInfo: {
|
|
65
|
+
stanzaId: 'ORIGINAL1',
|
|
66
|
+
participant: '628111111111@s.whatsapp.net',
|
|
67
|
+
quotedMessage: { conversation: 'pesan asli' },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const result = (0, MessageParser_1.parseMessage)(raw);
|
|
73
|
+
(0, vitest_1.expect)(result.text).toBe('balasanku');
|
|
74
|
+
(0, vitest_1.expect)(result.quoted).not.toBeNull();
|
|
75
|
+
(0, vitest_1.expect)(result.quoted?.id).toBe('ORIGINAL1');
|
|
76
|
+
(0, vitest_1.expect)(result.quoted?.text).toBe('pesan asli');
|
|
77
|
+
});
|
|
78
|
+
(0, vitest_1.it)('fallback ke timestamp sekarang kalau messageTimestamp kosong', () => {
|
|
79
|
+
const raw = fakeRaw({ messageTimestamp: undefined });
|
|
80
|
+
const before = Math.floor(Date.now() / 1000);
|
|
81
|
+
const result = (0, MessageParser_1.parseMessage)(raw);
|
|
82
|
+
(0, vitest_1.expect)(result.timestamp).toBeGreaterThanOrEqual(before);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=MessageParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageParser.test.js","sourceRoot":"","sources":["../../src/message/MessageParser.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginLoader.test.d.ts","sourceRoot":"","sources":["../../src/plugin/PluginLoader.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const PluginLoader_1 = require("./PluginLoader");
|
|
5
|
+
function fakeContext(sender = 'user1@s.whatsapp.net') {
|
|
6
|
+
return {
|
|
7
|
+
client: {},
|
|
8
|
+
message: { sender },
|
|
9
|
+
args: [],
|
|
10
|
+
reply: vitest_1.vi.fn(async () => { }),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
(0, vitest_1.describe)('PluginLoader', () => {
|
|
14
|
+
let loader;
|
|
15
|
+
(0, vitest_1.beforeEach)(() => {
|
|
16
|
+
loader = new PluginLoader_1.PluginLoader();
|
|
17
|
+
});
|
|
18
|
+
(0, vitest_1.it)('register & find plugin berdasarkan command', () => {
|
|
19
|
+
const plugin = { name: 'ping', command: 'ping', execute: vitest_1.vi.fn() };
|
|
20
|
+
loader.register(plugin);
|
|
21
|
+
(0, vitest_1.expect)(loader.find('ping')).toBe(plugin);
|
|
22
|
+
(0, vitest_1.expect)(loader.find('PING')).toBe(plugin); // case-insensitive
|
|
23
|
+
(0, vitest_1.expect)(loader.find('tidak-ada')).toBeUndefined();
|
|
24
|
+
});
|
|
25
|
+
(0, vitest_1.it)('register plugin dengan multiple command alias', () => {
|
|
26
|
+
const plugin = { name: 'hello', command: ['hello', 'hi'], execute: vitest_1.vi.fn() };
|
|
27
|
+
loader.register(plugin);
|
|
28
|
+
(0, vitest_1.expect)(loader.find('hello')).toBe(plugin);
|
|
29
|
+
(0, vitest_1.expect)(loader.find('hi')).toBe(plugin);
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.it)('execute menjalankan plugin yang cocok dan return true', async () => {
|
|
32
|
+
const execute = vitest_1.vi.fn();
|
|
33
|
+
loader.register({ name: 'ping', command: 'ping', execute });
|
|
34
|
+
const ctx = fakeContext();
|
|
35
|
+
const handled = await loader.execute('ping', ctx);
|
|
36
|
+
(0, vitest_1.expect)(handled).toBe(true);
|
|
37
|
+
(0, vitest_1.expect)(execute).toHaveBeenCalledWith(ctx);
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)('execute return false kalau tidak ada plugin yang cocok', async () => {
|
|
40
|
+
const ctx = fakeContext();
|
|
41
|
+
const handled = await loader.execute('tidak-ada', ctx);
|
|
42
|
+
(0, vitest_1.expect)(handled).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
(0, vitest_1.it)('list() tidak duplikat walau plugin punya banyak alias command', () => {
|
|
45
|
+
loader.register({ name: 'hello', command: ['hello', 'hi', 'hey'], execute: vitest_1.vi.fn() });
|
|
46
|
+
(0, vitest_1.expect)(loader.list()).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
(0, vitest_1.describe)('cooldown', () => {
|
|
49
|
+
(0, vitest_1.beforeEach)(() => {
|
|
50
|
+
vitest_1.vi.useFakeTimers();
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.afterEach)(() => {
|
|
53
|
+
vitest_1.vi.useRealTimers();
|
|
54
|
+
});
|
|
55
|
+
(0, vitest_1.it)('blokir eksekusi kedua dalam window cooldown & kasih tau sisa waktu', async () => {
|
|
56
|
+
const execute = vitest_1.vi.fn();
|
|
57
|
+
loader.register({ name: 'heal', command: 'heal', cooldown: 10, execute });
|
|
58
|
+
const ctx = fakeContext('userA');
|
|
59
|
+
await loader.execute('heal', ctx); // panggilan pertama, lolos
|
|
60
|
+
await loader.execute('heal', ctx); // panggilan kedua, kena cooldown
|
|
61
|
+
(0, vitest_1.expect)(execute).toHaveBeenCalledTimes(1);
|
|
62
|
+
(0, vitest_1.expect)(ctx.reply).toHaveBeenCalledWith(vitest_1.expect.stringContaining('Tunggu'));
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.it)('boleh eksekusi lagi setelah cooldown habis', async () => {
|
|
65
|
+
const execute = vitest_1.vi.fn();
|
|
66
|
+
loader.register({ name: 'heal', command: 'heal', cooldown: 10, execute });
|
|
67
|
+
const ctx = fakeContext('userA');
|
|
68
|
+
await loader.execute('heal', ctx);
|
|
69
|
+
vitest_1.vi.advanceTimersByTime(10_001);
|
|
70
|
+
await loader.execute('heal', ctx);
|
|
71
|
+
(0, vitest_1.expect)(execute).toHaveBeenCalledTimes(2);
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)('cooldown per-user, tidak saling mempengaruhi antar user berbeda', async () => {
|
|
74
|
+
const execute = vitest_1.vi.fn();
|
|
75
|
+
loader.register({ name: 'heal', command: 'heal', cooldown: 10, execute });
|
|
76
|
+
await loader.execute('heal', fakeContext('userA'));
|
|
77
|
+
await loader.execute('heal', fakeContext('userB'));
|
|
78
|
+
(0, vitest_1.expect)(execute).toHaveBeenCalledTimes(2);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=PluginLoader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginLoader.test.js","sourceRoot":"","sources":["../../src/plugin/PluginLoader.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rynkai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Clean, typed wrapper around Baileys with built-in plugin system and pluggable session store — for building WhatsApp bots fast.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc -p tsconfig.json",
|
|
10
10
|
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
|
11
|
-
"
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"prepublishOnly": "npm run build && npm test"
|
|
12
14
|
},
|
|
13
15
|
"keywords": [
|
|
14
16
|
"whatsapp",
|
|
@@ -43,7 +45,8 @@
|
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"typescript": "^5.5.4",
|
|
45
47
|
"ts-node-dev": "^2.0.0",
|
|
46
|
-
"@types/node": "^20.14.0"
|
|
48
|
+
"@types/node": "^20.14.0",
|
|
49
|
+
"vitest": "^2.1.0"
|
|
47
50
|
},
|
|
48
51
|
"files": [
|
|
49
52
|
"dist"
|