@kangwifi-pro/waliwa 1.0.0

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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +600 -0
  3. package/dist/advanced/features.d.ts +211 -0
  4. package/dist/advanced/features.d.ts.map +1 -0
  5. package/dist/advanced/features.js +671 -0
  6. package/dist/advanced/features.js.map +1 -0
  7. package/dist/auth/auth-state.d.ts +89 -0
  8. package/dist/auth/auth-state.d.ts.map +1 -0
  9. package/dist/auth/auth-state.js +330 -0
  10. package/dist/auth/auth-state.js.map +1 -0
  11. package/dist/auth/pairing-code.d.ts +78 -0
  12. package/dist/auth/pairing-code.d.ts.map +1 -0
  13. package/dist/auth/pairing-code.js +191 -0
  14. package/dist/auth/pairing-code.js.map +1 -0
  15. package/dist/auth/qr-code.d.ts +60 -0
  16. package/dist/auth/qr-code.d.ts.map +1 -0
  17. package/dist/auth/qr-code.js +151 -0
  18. package/dist/auth/qr-code.js.map +1 -0
  19. package/dist/calls/handler.d.ts +61 -0
  20. package/dist/calls/handler.d.ts.map +1 -0
  21. package/dist/calls/handler.js +117 -0
  22. package/dist/calls/handler.js.map +1 -0
  23. package/dist/core/binary.d.ts +74 -0
  24. package/dist/core/binary.d.ts.map +1 -0
  25. package/dist/core/binary.js +448 -0
  26. package/dist/core/binary.js.map +1 -0
  27. package/dist/core/crypto.d.ts +60 -0
  28. package/dist/core/crypto.d.ts.map +1 -0
  29. package/dist/core/crypto.js +170 -0
  30. package/dist/core/crypto.js.map +1 -0
  31. package/dist/core/noise.d.ts +106 -0
  32. package/dist/core/noise.d.ts.map +1 -0
  33. package/dist/core/noise.js +307 -0
  34. package/dist/core/noise.js.map +1 -0
  35. package/dist/events/emitter.d.ts +44 -0
  36. package/dist/events/emitter.d.ts.map +1 -0
  37. package/dist/events/emitter.js +79 -0
  38. package/dist/events/emitter.js.map +1 -0
  39. package/dist/features/index.d.ts +342 -0
  40. package/dist/features/index.d.ts.map +1 -0
  41. package/dist/features/index.js +755 -0
  42. package/dist/features/index.js.map +1 -0
  43. package/dist/fixes/index.d.ts +333 -0
  44. package/dist/fixes/index.d.ts.map +1 -0
  45. package/dist/fixes/index.js +762 -0
  46. package/dist/fixes/index.js.map +1 -0
  47. package/dist/groups/management.d.ts +86 -0
  48. package/dist/groups/management.d.ts.map +1 -0
  49. package/dist/groups/management.js +443 -0
  50. package/dist/groups/management.js.map +1 -0
  51. package/dist/index.d.ts +65 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +236 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/messages/media.d.ts +93 -0
  56. package/dist/messages/media.d.ts.map +1 -0
  57. package/dist/messages/media.js +252 -0
  58. package/dist/messages/media.js.map +1 -0
  59. package/dist/messages/message-encoder.d.ts +70 -0
  60. package/dist/messages/message-encoder.d.ts.map +1 -0
  61. package/dist/messages/message-encoder.js +453 -0
  62. package/dist/messages/message-encoder.js.map +1 -0
  63. package/dist/messages/send.d.ts +101 -0
  64. package/dist/messages/send.d.ts.map +1 -0
  65. package/dist/messages/send.js +409 -0
  66. package/dist/messages/send.js.map +1 -0
  67. package/dist/recovery/index.d.ts +125 -0
  68. package/dist/recovery/index.d.ts.map +1 -0
  69. package/dist/recovery/index.js +584 -0
  70. package/dist/recovery/index.js.map +1 -0
  71. package/dist/skdm/index.d.ts +220 -0
  72. package/dist/skdm/index.d.ts.map +1 -0
  73. package/dist/skdm/index.js +600 -0
  74. package/dist/skdm/index.js.map +1 -0
  75. package/dist/socket/hybrid.d.ts +118 -0
  76. package/dist/socket/hybrid.d.ts.map +1 -0
  77. package/dist/socket/hybrid.js +352 -0
  78. package/dist/socket/hybrid.js.map +1 -0
  79. package/dist/socket/wa-socket.d.ts +300 -0
  80. package/dist/socket/wa-socket.d.ts.map +1 -0
  81. package/dist/socket/wa-socket.js +1094 -0
  82. package/dist/socket/wa-socket.js.map +1 -0
  83. package/dist/socket/ws-socket.d.ts +96 -0
  84. package/dist/socket/ws-socket.d.ts.map +1 -0
  85. package/dist/socket/ws-socket.js +302 -0
  86. package/dist/socket/ws-socket.js.map +1 -0
  87. package/dist/types/index.d.ts +444 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +7 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/utils/jid.d.ts +46 -0
  92. package/dist/utils/jid.d.ts.map +1 -0
  93. package/dist/utils/jid.js +152 -0
  94. package/dist/utils/jid.js.map +1 -0
  95. package/dist/utils/logger.d.ts +45 -0
  96. package/dist/utils/logger.d.ts.map +1 -0
  97. package/dist/utils/logger.js +79 -0
  98. package/dist/utils/logger.js.map +1 -0
  99. package/dist/utils/retry.d.ts +43 -0
  100. package/dist/utils/retry.d.ts.map +1 -0
  101. package/dist/utils/retry.js +175 -0
  102. package/dist/utils/retry.js.map +1 -0
  103. package/docs/API.md +745 -0
  104. package/docs/ARCHITECTURE.md +307 -0
  105. package/docs/BAILEYS_FIXES.md +360 -0
  106. package/docs/FEATURES.md +532 -0
  107. package/docs/PROTOCOL.md +489 -0
  108. package/docs/RECOVERY.md +409 -0
  109. package/docs/SKDM.md +233 -0
  110. package/examples/ai-bot/index.ts +479 -0
  111. package/examples/ai-bot/package.json +17 -0
  112. package/examples/echo-bot/index.ts +171 -0
  113. package/examples/echo-bot/package.json +18 -0
  114. package/examples/group-bot/index.ts +557 -0
  115. package/examples/group-bot/package.json +17 -0
  116. package/examples/rest-gateway/index.ts +499 -0
  117. package/examples/rest-gateway/package.json +19 -0
  118. package/package.json +75 -0
  119. package/src/advanced/features.ts +817 -0
  120. package/src/auth/auth-state.ts +342 -0
  121. package/src/auth/pairing-code.ts +246 -0
  122. package/src/auth/qr-code.ts +191 -0
  123. package/src/calls/handler.ts +153 -0
  124. package/src/core/binary.ts +464 -0
  125. package/src/core/crypto.ts +189 -0
  126. package/src/core/noise.ts +406 -0
  127. package/src/events/emitter.ts +88 -0
  128. package/src/features/index.ts +921 -0
  129. package/src/fixes/index.ts +882 -0
  130. package/src/groups/management.ts +497 -0
  131. package/src/index.ts +274 -0
  132. package/src/messages/media.ts +372 -0
  133. package/src/messages/message-encoder.ts +520 -0
  134. package/src/messages/send.ts +521 -0
  135. package/src/recovery/index.ts +704 -0
  136. package/src/skdm/index.ts +693 -0
  137. package/src/socket/hybrid.ts +414 -0
  138. package/src/socket/wa-socket.ts +1347 -0
  139. package/src/socket/ws-socket.ts +355 -0
  140. package/src/types/index.ts +457 -0
  141. package/src/utils/jid.ts +156 -0
  142. package/src/utils/logger.ts +84 -0
  143. package/src/utils/retry.ts +205 -0
  144. package/tsconfig.json +30 -0
@@ -0,0 +1,497 @@
1
+ /**
2
+ * Waliwa - Group management
3
+ * API untuk create, modify, invite, kick, promote, demote group members
4
+ */
5
+
6
+ import type {
7
+ WAGroupMetadata,
8
+ WAGroupParticipant,
9
+ GroupCreateOptions,
10
+ JIDLike,
11
+ WALogger
12
+ } from '../types/index.js';
13
+ import { encodeBinaryNode, BinaryNode } from '../core/binary.js';
14
+ import { jidToString, parseJid } from '../utils/jid.js';
15
+ import { generateStanzaId } from '../messages/message-encoder.js';
16
+ import { SilentLogger } from '../utils/logger.js';
17
+
18
+ // ============== Group Sender ==============
19
+
20
+ export interface GroupManagerOptions {
21
+ /** Send raw bytes via WebSocket */
22
+ send: (data: Uint8Array) => void;
23
+ logger?: WALogger;
24
+ /** IQ response handler registry */
25
+ registerIqHandler: (id: string, handler: (node: BinaryNode) => void) => void;
26
+ }
27
+
28
+ export class GroupManager {
29
+ private send: (data: Uint8Array) => void;
30
+ private logger: WALogger;
31
+ private registerIqHandler: (id: string, handler: (node: BinaryNode) => void) => void;
32
+
33
+ constructor(opts: GroupManagerOptions) {
34
+ this.send = opts.send;
35
+ this.logger = opts.logger ?? new SilentLogger();
36
+ this.registerIqHandler = opts.registerIqHandler;
37
+ }
38
+
39
+ /**
40
+ * Create new group
41
+ */
42
+ async createGroup(opts: GroupCreateOptions): Promise<WAGroupMetadata> {
43
+ const id = generateStanzaId();
44
+ this.logger.debug(`Creating group "${opts.subject}" with ${opts.participants.length} participants`);
45
+
46
+ const participantNodes = opts.participants.map(p => ({
47
+ tag: 'participant',
48
+ attrs: { jid: jidToString(p) }
49
+ }));
50
+
51
+ const node: BinaryNode = {
52
+ tag: 'iq',
53
+ attrs: {
54
+ type: 'set',
55
+ to: 'g.us',
56
+ id
57
+ },
58
+ content: [
59
+ {
60
+ tag: 'create',
61
+ attrs: {
62
+ subject: opts.subject
63
+ },
64
+ content: participantNodes
65
+ }
66
+ ]
67
+ };
68
+
69
+ return this.sendIqAndWait(node, id, 30000).then(response => this.parseGroupMetadata(response));
70
+ }
71
+
72
+ /**
73
+ * Get group metadata
74
+ */
75
+ async getGroupMetadata(groupJid: JIDLike): Promise<WAGroupMetadata> {
76
+ const id = generateStanzaId();
77
+ const node: BinaryNode = {
78
+ tag: 'iq',
79
+ attrs: {
80
+ type: 'get',
81
+ to: jidToString(groupJid),
82
+ id
83
+ },
84
+ content: [
85
+ {
86
+ tag: 'query',
87
+ attrs: { request: 'interactive' }
88
+ }
89
+ ]
90
+ };
91
+
92
+ return this.sendIqAndWait(node, id, 15000).then(response => this.parseGroupMetadata(response));
93
+ }
94
+
95
+ /**
96
+ * Update group subject (name)
97
+ */
98
+ async updateGroupSubject(groupJid: JIDLike, subject: string): Promise<void> {
99
+ const id = generateStanzaId();
100
+ const node: BinaryNode = {
101
+ tag: 'iq',
102
+ attrs: {
103
+ type: 'set',
104
+ to: jidToString(groupJid),
105
+ id
106
+ },
107
+ content: [
108
+ {
109
+ tag: 'subject',
110
+ attrs: { subject }
111
+ }
112
+ ]
113
+ };
114
+
115
+ await this.sendIqAndWait(node, id, 10000);
116
+ }
117
+
118
+ /**
119
+ * Update group description
120
+ */
121
+ async updateGroupDescription(groupJid: JIDLike, desc: string): Promise<void> {
122
+ const id = generateStanzaId();
123
+ const node: BinaryNode = {
124
+ tag: 'iq',
125
+ attrs: {
126
+ type: 'set',
127
+ to: jidToString(groupJid),
128
+ id
129
+ },
130
+ content: [
131
+ {
132
+ tag: 'description',
133
+ attrs: {},
134
+ content: desc
135
+ }
136
+ ]
137
+ };
138
+
139
+ await this.sendIqAndWait(node, id, 10000);
140
+ }
141
+
142
+ /**
143
+ * Add participants to group
144
+ */
145
+ async addParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
146
+ const id = generateStanzaId();
147
+ const node: BinaryNode = {
148
+ tag: 'iq',
149
+ attrs: {
150
+ type: 'set',
151
+ to: jidToString(groupJid),
152
+ id
153
+ },
154
+ content: [
155
+ {
156
+ tag: 'add',
157
+ attrs: {},
158
+ content: participants.map(p => ({
159
+ tag: 'participant',
160
+ attrs: { jid: jidToString(p) }
161
+ }))
162
+ }
163
+ ]
164
+ };
165
+
166
+ await this.sendIqAndWait(node, id, 15000);
167
+ }
168
+
169
+ /**
170
+ * Remove participants from group
171
+ */
172
+ async removeParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
173
+ const id = generateStanzaId();
174
+ const node: BinaryNode = {
175
+ tag: 'iq',
176
+ attrs: {
177
+ type: 'set',
178
+ to: jidToString(groupJid),
179
+ id
180
+ },
181
+ content: [
182
+ {
183
+ tag: 'remove',
184
+ attrs: {},
185
+ content: participants.map(p => ({
186
+ tag: 'participant',
187
+ attrs: { jid: jidToString(p) }
188
+ }))
189
+ }
190
+ ]
191
+ };
192
+
193
+ await this.sendIqAndWait(node, id, 15000);
194
+ }
195
+
196
+ /**
197
+ * Promote participants jadi admin
198
+ */
199
+ async promoteParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
200
+ const id = generateStanzaId();
201
+ const node: BinaryNode = {
202
+ tag: 'iq',
203
+ attrs: {
204
+ type: 'set',
205
+ to: jidToString(groupJid),
206
+ id
207
+ },
208
+ content: [
209
+ {
210
+ tag: 'promote',
211
+ attrs: {},
212
+ content: participants.map(p => ({
213
+ tag: 'participant',
214
+ attrs: { jid: jidToString(p) }
215
+ }))
216
+ }
217
+ ]
218
+ };
219
+
220
+ await this.sendIqAndWait(node, id, 15000);
221
+ }
222
+
223
+ /**
224
+ * Demote admin kembali jadi member
225
+ */
226
+ async demoteParticipants(groupJid: JIDLike, participants: JIDLike[]): Promise<void> {
227
+ const id = generateStanzaId();
228
+ const node: BinaryNode = {
229
+ tag: 'iq',
230
+ attrs: {
231
+ type: 'set',
232
+ to: jidToString(groupJid),
233
+ id
234
+ },
235
+ content: [
236
+ {
237
+ tag: 'demote',
238
+ attrs: {},
239
+ content: participants.map(p => ({
240
+ tag: 'participant',
241
+ attrs: { jid: jidToString(p) }
242
+ }))
243
+ }
244
+ ]
245
+ };
246
+
247
+ await this.sendIqAndWait(node, id, 15000);
248
+ }
249
+
250
+ /**
251
+ * Leave group
252
+ */
253
+ async leaveGroup(groupJid: JIDLike): Promise<void> {
254
+ const id = generateStanzaId();
255
+ const node: BinaryNode = {
256
+ tag: 'iq',
257
+ attrs: {
258
+ type: 'set',
259
+ to: 'g.us',
260
+ id
261
+ },
262
+ content: [
263
+ {
264
+ tag: 'leave',
265
+ attrs: {},
266
+ content: [{
267
+ tag: 'group',
268
+ attrs: { id: jidToString(groupJid) }
269
+ }]
270
+ }
271
+ ]
272
+ };
273
+
274
+ await this.sendIqAndWait(node, id, 10000);
275
+ }
276
+
277
+ /**
278
+ * Get invite code untuk group
279
+ */
280
+ async getInviteCode(groupJid: JIDLike): Promise<string> {
281
+ const id = generateStanzaId();
282
+ const node: BinaryNode = {
283
+ tag: 'iq',
284
+ attrs: {
285
+ type: 'get',
286
+ to: jidToString(groupJid),
287
+ id
288
+ },
289
+ content: [
290
+ {
291
+ tag: 'invite',
292
+ attrs: {}
293
+ }
294
+ ]
295
+ };
296
+
297
+ const response = await this.sendIqAndWait(node, id, 10000);
298
+ const inviteNode = response.content as any[];
299
+ const invite = inviteNode?.find(c => c.tag === 'invite');
300
+ return invite?.attrs?.code ?? '';
301
+ }
302
+
303
+ /**
304
+ * Revoke invite code (generate new)
305
+ */
306
+ async revokeInviteCode(groupJid: JIDLike): Promise<string> {
307
+ const id = generateStanzaId();
308
+ const node: BinaryNode = {
309
+ tag: 'iq',
310
+ attrs: {
311
+ type: 'set',
312
+ to: jidToString(groupJid),
313
+ id
314
+ },
315
+ content: [
316
+ {
317
+ tag: 'invite',
318
+ attrs: { action: 'revoke' }
319
+ }
320
+ ]
321
+ };
322
+
323
+ const response = await this.sendIqAndWait(node, id, 10000);
324
+ const inviteNode = (response.content as any[])?.find(c => c.tag === 'invite');
325
+ return inviteNode?.attrs?.code ?? '';
326
+ }
327
+
328
+ /**
329
+ * Join group via invite code
330
+ */
331
+ async joinGroupViaCode(code: string): Promise<JIDLike> {
332
+ const id = generateStanzaId();
333
+ const node: BinaryNode = {
334
+ tag: 'iq',
335
+ attrs: {
336
+ type: 'set',
337
+ to: 'g.us',
338
+ id
339
+ },
340
+ content: [
341
+ {
342
+ tag: 'join',
343
+ attrs: {
344
+ code,
345
+ reason: 'invite'
346
+ }
347
+ }
348
+ ]
349
+ };
350
+
351
+ const response = await this.sendIqAndWait(node, id, 15000);
352
+ const groupNode = (response.content as any[])?.find(c => c.tag === 'group');
353
+ return groupNode?.attrs?.jid ?? '';
354
+ }
355
+
356
+ /**
357
+ * Set group settings: restrict (only admins can send messages)
358
+ */
359
+ async setGroupRestrict(groupJid: JIDLike, restrict: boolean): Promise<void> {
360
+ const id = generateStanzaId();
361
+ const node: BinaryNode = {
362
+ tag: 'iq',
363
+ attrs: {
364
+ type: 'set',
365
+ to: jidToString(groupJid),
366
+ id
367
+ },
368
+ content: [
369
+ {
370
+ tag: 'announce',
371
+ attrs: {
372
+ type: restrict ? 'restrict' : 'unrestrict'
373
+ }
374
+ }
375
+ ]
376
+ };
377
+
378
+ await this.sendIqAndWait(node, id, 10000);
379
+ }
380
+
381
+ /**
382
+ * Set group settings: announce mode (only admins can edit info)
383
+ */
384
+ async setGroupAnnounce(groupJid: JIDLike, announce: boolean): Promise<void> {
385
+ const id = generateStanzaId();
386
+ const node: BinaryNode = {
387
+ tag: 'iq',
388
+ attrs: {
389
+ type: 'set',
390
+ to: jidToString(groupJid),
391
+ id
392
+ },
393
+ content: [
394
+ {
395
+ tag: 'info',
396
+ attrs: {
397
+ type: announce ? 'announce' : 'unannounce'
398
+ }
399
+ }
400
+ ]
401
+ };
402
+
403
+ await this.sendIqAndWait(node, id, 10000);
404
+ }
405
+
406
+ // ============== Internal: Send IQ + Wait ==============
407
+
408
+ private sendIqAndWait(node: BinaryNode, id: string, timeoutMs: number): Promise<BinaryNode> {
409
+ return new Promise<BinaryNode>((resolve, reject) => {
410
+ const timeout = setTimeout(() => {
411
+ reject(new Error(`IQ ${id} timeout after ${timeoutMs}ms`));
412
+ }, timeoutMs);
413
+
414
+ this.registerIqHandler(id, (response: BinaryNode) => {
415
+ clearTimeout(timeout);
416
+ const type = response.attrs?.type;
417
+ if (type === 'error') {
418
+ const errorNode = (response.content as any[])?.find(c => c.tag === 'error');
419
+ reject(new Error(errorNode?.attrs?.text ?? 'Unknown group operation error'));
420
+ } else {
421
+ resolve(response);
422
+ }
423
+ });
424
+
425
+ try {
426
+ this.send(encodeBinaryNode(node));
427
+ } catch (err: any) {
428
+ clearTimeout(timeout);
429
+ reject(err);
430
+ }
431
+ });
432
+ }
433
+
434
+ // ============== Parser ==============
435
+
436
+ private parseGroupMetadata(node: BinaryNode): WAGroupMetadata {
437
+ const groupNode = node.tag === 'group' ? node :
438
+ (Array.isArray(node.content) ? node.content.find(c => c?.tag === 'group') : null);
439
+
440
+ if (!groupNode) {
441
+ throw new Error('Group node not found in response');
442
+ }
443
+
444
+ const attrs = groupNode.attrs || {};
445
+ const participants: WAGroupParticipant[] = [];
446
+
447
+ if (Array.isArray(groupNode.content)) {
448
+ for (const child of groupNode.content) {
449
+ if (child?.tag === 'participant') {
450
+ const pAttrs = child.attrs || {};
451
+ participants.push({
452
+ id: pAttrs.jid,
453
+ isAdmin: pAttrs.type === 'admin',
454
+ isSuperAdmin: pAttrs.type === 'superadmin',
455
+ role: pAttrs.type
456
+ });
457
+ }
458
+ }
459
+ }
460
+
461
+ return {
462
+ id: attrs.id,
463
+ subject: attrs.subject,
464
+ subjectOwner: attrs.subject_owner,
465
+ subjectTime: attrs.subject_time ? parseInt(attrs.subject_time, 10) : undefined,
466
+ desc: attrs.desc,
467
+ descOwner: attrs.desc_owner,
468
+ descTime: attrs.desc_time ? parseInt(attrs.desc_time, 10) : undefined,
469
+ creation: attrs.creation ? parseInt(attrs.creation, 10) : undefined,
470
+ owner: attrs.owner,
471
+ participants,
472
+ restrict: attrs.restrict === 'true',
473
+ announce: attrs.announce === 'true',
474
+ isCommunity: attrs.is_community === 'true',
475
+ isParentGroup: attrs.is_parent_group === 'true',
476
+ linkedParent: attrs.linked_parent,
477
+ ephemeralSetting: attrs.ephemeral ? parseInt(attrs.ephemeral, 10) : undefined
478
+ };
479
+ }
480
+ }
481
+
482
+ // ============== Helper: Build invite link ==============
483
+
484
+ /**
485
+ * Build WhatsApp invite URL dari code
486
+ */
487
+ export function buildInviteLink(code: string): string {
488
+ return `https://chat.whatsapp.com/${code}`;
489
+ }
490
+
491
+ /**
492
+ * Parse invite code dari URL
493
+ */
494
+ export function parseInviteLink(url: string): string | null {
495
+ const match = url.match(/chat\.whatsapp\.com\/([A-Za-z0-9]+)/);
496
+ return match ? match[1] : null;
497
+ }