@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,671 @@
1
+ "use strict";
2
+ /**
3
+ * Waliwa - Advanced features
4
+ * Polls, Newsletter, Status/Stories, Business Profile, Labels, Catalog
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.LabelsManager = exports.CatalogManager = exports.BusinessProfileManager = exports.StatusManager = exports.NewsletterManager = exports.PollManager = void 0;
8
+ const binary_js_1 = require("../core/binary.js");
9
+ const jid_js_1 = require("../utils/jid.js");
10
+ const message_encoder_js_1 = require("../messages/message-encoder.js");
11
+ const logger_js_1 = require("../utils/logger.js");
12
+ class PollManager {
13
+ send;
14
+ registerIqHandler;
15
+ logger;
16
+ voteListeners = new Map();
17
+ constructor(opts) {
18
+ this.send = opts.send;
19
+ this.registerIqHandler = opts.registerIqHandler;
20
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
21
+ }
22
+ /**
23
+ * Create poll message
24
+ */
25
+ async createPoll(jid, opts) {
26
+ const messageId = (0, message_encoder_js_1.generateMessageId)();
27
+ const selectableCounts = opts.selectableCounts ?? [opts.multipleAnswers ? opts.values.length : 1];
28
+ const pollNode = {
29
+ tag: 'message',
30
+ attrs: {
31
+ to: (0, jid_js_1.jidToString)(jid),
32
+ type: 'poll',
33
+ id: messageId
34
+ },
35
+ content: [
36
+ {
37
+ tag: 'pollCreationMessage',
38
+ attrs: {
39
+ name: opts.name,
40
+ selectableOptionsCount: JSON.stringify(selectableCounts)
41
+ },
42
+ content: opts.values.map((value, idx) => ({
43
+ tag: 'option',
44
+ attrs: {
45
+ index: idx.toString(),
46
+ name: value
47
+ }
48
+ }))
49
+ }
50
+ ]
51
+ };
52
+ const data = (0, binary_js_1.encodeBinaryNode)(pollNode);
53
+ this.send(data);
54
+ return {
55
+ remoteJid: jid,
56
+ fromMe: true,
57
+ id: messageId
58
+ };
59
+ }
60
+ /**
61
+ * Vote on existing poll
62
+ */
63
+ async voteOnPoll(pollKey, selectedOptions) {
64
+ const messageId = (0, message_encoder_js_1.generateMessageId)();
65
+ const voteNode = {
66
+ tag: 'message',
67
+ attrs: {
68
+ to: (0, jid_js_1.jidToString)(pollKey.remoteJid),
69
+ type: 'poll_vote',
70
+ id: messageId
71
+ },
72
+ content: [
73
+ {
74
+ tag: 'pollVoteMessage',
75
+ attrs: {
76
+ pollCreationMessageKey: JSON.stringify({
77
+ remoteJid: (0, jid_js_1.jidToString)(pollKey.remoteJid),
78
+ fromMe: pollKey.fromMe,
79
+ id: pollKey.id
80
+ })
81
+ },
82
+ content: selectedOptions.map(idx => ({
83
+ tag: 'selectedOption',
84
+ attrs: { index: idx.toString() }
85
+ }))
86
+ }
87
+ ]
88
+ };
89
+ this.send((0, binary_js_1.encodeBinaryNode)(voteNode));
90
+ }
91
+ /**
92
+ * Listen for poll votes
93
+ */
94
+ onVote(pollMessageId, handler) {
95
+ this.voteListeners.set(pollMessageId, handler);
96
+ }
97
+ /**
98
+ * Handle incoming poll vote
99
+ */
100
+ handlePollVote(node) {
101
+ if (node.tag !== 'message')
102
+ return;
103
+ const attrs = node.attrs || {};
104
+ const from = attrs.from;
105
+ if (Array.isArray(node.content)) {
106
+ for (const child of node.content) {
107
+ if (child?.tag === 'pollVoteMessage') {
108
+ const pollKeyJson = child.attrs?.pollCreationMessageKey;
109
+ if (!pollKeyJson)
110
+ continue;
111
+ try {
112
+ const pollKey = JSON.parse(pollKeyJson);
113
+ const listener = this.voteListeners.get(pollKey.id);
114
+ if (!listener)
115
+ continue;
116
+ const selectedOptions = Array.isArray(child.content)
117
+ ? child.content.filter(c => c?.tag === 'selectedOption').map(c => parseInt(c.attrs?.index ?? '0', 10))
118
+ : [];
119
+ listener({
120
+ voter: from,
121
+ selectedOptions,
122
+ timestamp: Date.now()
123
+ });
124
+ }
125
+ catch (err) {
126
+ this.logger.warn(`Failed to parse poll vote: ${err.message}`);
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ exports.PollManager = PollManager;
134
+ class NewsletterManager {
135
+ send;
136
+ registerIqHandler;
137
+ logger;
138
+ constructor(opts) {
139
+ this.send = opts.send;
140
+ this.registerIqHandler = opts.registerIqHandler;
141
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
142
+ }
143
+ /**
144
+ * Create new newsletter
145
+ */
146
+ async create(name, description) {
147
+ const id = (0, message_encoder_js_1.generateStanzaId)();
148
+ const node = {
149
+ tag: 'iq',
150
+ attrs: {
151
+ type: 'set',
152
+ to: 's.whatsapp.net',
153
+ id
154
+ },
155
+ content: [
156
+ {
157
+ tag: 'newsletter',
158
+ attrs: {
159
+ name,
160
+ description: description ?? ''
161
+ }
162
+ }
163
+ ]
164
+ };
165
+ const response = await this.sendIqAndWait(node, id);
166
+ const nlNode = response.content?.find(c => c.tag === 'newsletter');
167
+ return {
168
+ id: nlNode?.attrs?.id,
169
+ name,
170
+ description,
171
+ subscribersCount: 0,
172
+ state: 'active',
173
+ verification: 'unverified'
174
+ };
175
+ }
176
+ /**
177
+ * Subscribe ke newsletter
178
+ */
179
+ async subscribe(newsletterId) {
180
+ const id = (0, message_encoder_js_1.generateStanzaId)();
181
+ const node = {
182
+ tag: 'iq',
183
+ attrs: {
184
+ type: 'set',
185
+ to: (0, jid_js_1.jidToString)(newsletterId),
186
+ id
187
+ },
188
+ content: [{
189
+ tag: 'subscribe',
190
+ attrs: { action: 'subscribe' }
191
+ }]
192
+ };
193
+ await this.sendIqAndWait(node, id);
194
+ }
195
+ /**
196
+ * Unsubscribe dari newsletter
197
+ */
198
+ async unsubscribe(newsletterId) {
199
+ const id = (0, message_encoder_js_1.generateStanzaId)();
200
+ const node = {
201
+ tag: 'iq',
202
+ attrs: {
203
+ type: 'set',
204
+ to: (0, jid_js_1.jidToString)(newsletterId),
205
+ id
206
+ },
207
+ content: [{
208
+ tag: 'subscribe',
209
+ attrs: { action: 'unsubscribe' }
210
+ }]
211
+ };
212
+ await this.sendIqAndWait(node, id);
213
+ }
214
+ /**
215
+ * Mute newsletter
216
+ */
217
+ async mute(newsletterId) {
218
+ const id = (0, message_encoder_js_1.generateStanzaId)();
219
+ const node = {
220
+ tag: 'iq',
221
+ attrs: {
222
+ type: 'set',
223
+ to: (0, jid_js_1.jidToString)(newsletterId),
224
+ id
225
+ },
226
+ content: [{
227
+ tag: 'mute',
228
+ attrs: { action: 'mute' }
229
+ }]
230
+ };
231
+ await this.sendIqAndWait(node, id);
232
+ }
233
+ sendIqAndWait(node, id, timeoutMs = 15000) {
234
+ return new Promise((resolve, reject) => {
235
+ const timeout = setTimeout(() => reject(new Error(`IQ ${id} timeout`)), timeoutMs);
236
+ this.registerIqHandler(id, (response) => {
237
+ clearTimeout(timeout);
238
+ resolve(response);
239
+ });
240
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
241
+ });
242
+ }
243
+ }
244
+ exports.NewsletterManager = NewsletterManager;
245
+ class StatusManager {
246
+ send;
247
+ logger;
248
+ constructor(opts) {
249
+ this.send = opts.send;
250
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
251
+ }
252
+ /**
253
+ * Upload text status
254
+ */
255
+ async sendTextStatus(text, opts = {}) {
256
+ const messageId = (0, message_encoder_js_1.generateMessageId)();
257
+ const node = {
258
+ tag: 'message',
259
+ attrs: {
260
+ to: 'status@broadcast',
261
+ type: 'text',
262
+ id: messageId
263
+ },
264
+ content: [
265
+ {
266
+ tag: 'textStatusMessage',
267
+ attrs: {
268
+ text,
269
+ backgroundColor: opts.backgroundColor ?? '#000000',
270
+ font: (opts.font ?? 0).toString(),
271
+ expiration: ((opts.expiration ?? 86400) * 1000).toString()
272
+ }
273
+ }
274
+ ]
275
+ };
276
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
277
+ return {
278
+ remoteJid: 'status@broadcast',
279
+ fromMe: true,
280
+ id: messageId
281
+ };
282
+ }
283
+ /**
284
+ * Upload media status
285
+ */
286
+ async sendMediaStatus(mediaType, mediaUrl, opts = {}) {
287
+ const messageId = (0, message_encoder_js_1.generateMessageId)();
288
+ const tag = mediaType === 'image' ? 'imageStatusMessage' : 'videoStatusMessage';
289
+ const node = {
290
+ tag: 'message',
291
+ attrs: {
292
+ to: 'status@broadcast',
293
+ type: mediaType,
294
+ id: messageId
295
+ },
296
+ content: [
297
+ {
298
+ tag,
299
+ attrs: {
300
+ url: mediaUrl,
301
+ caption: opts.caption ?? '',
302
+ expiration: ((opts.expiration ?? 86400) * 1000).toString()
303
+ }
304
+ }
305
+ ]
306
+ };
307
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
308
+ return {
309
+ remoteJid: 'status@broadcast',
310
+ fromMe: true,
311
+ id: messageId
312
+ };
313
+ }
314
+ /**
315
+ * Delete own status
316
+ */
317
+ async deleteStatus(statusId) {
318
+ const node = {
319
+ tag: 'iq',
320
+ attrs: {
321
+ type: 'set',
322
+ to: 'status@broadcast',
323
+ id: `delstatus-${Date.now()}`
324
+ },
325
+ content: [{
326
+ tag: 'delete',
327
+ attrs: { id: statusId }
328
+ }]
329
+ };
330
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
331
+ }
332
+ /**
333
+ * Get status views
334
+ */
335
+ async getStatusViews(statusId) {
336
+ const id = (0, message_encoder_js_1.generateStanzaId)();
337
+ const node = {
338
+ tag: 'iq',
339
+ attrs: {
340
+ type: 'get',
341
+ to: 'status@broadcast',
342
+ id
343
+ },
344
+ content: [{
345
+ tag: 'views',
346
+ attrs: { id: statusId }
347
+ }]
348
+ };
349
+ // Send and wait for response (simplified - real impl uses IQ handler registry)
350
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
351
+ return [];
352
+ }
353
+ }
354
+ exports.StatusManager = StatusManager;
355
+ class BusinessProfileManager {
356
+ send;
357
+ registerIqHandler;
358
+ logger;
359
+ constructor(opts) {
360
+ this.send = opts.send;
361
+ this.registerIqHandler = opts.registerIqHandler;
362
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
363
+ }
364
+ /**
365
+ * Update business profile
366
+ */
367
+ async update(opts) {
368
+ const id = (0, message_encoder_js_1.generateStanzaId)();
369
+ const content = [];
370
+ if (opts.description) {
371
+ content.push({ tag: 'description', attrs: {}, content: opts.description });
372
+ }
373
+ if (opts.email) {
374
+ content.push({ tag: 'email', attrs: {}, content: opts.email });
375
+ }
376
+ if (opts.websites?.length) {
377
+ content.push({
378
+ tag: 'websites',
379
+ attrs: {},
380
+ content: opts.websites.map(w => ({ tag: 'website', attrs: { url: w } }))
381
+ });
382
+ }
383
+ if (opts.category) {
384
+ content.push({ tag: 'category', attrs: { name: opts.category } });
385
+ }
386
+ if (opts.address) {
387
+ content.push({ tag: 'address', attrs: {}, content: opts.address });
388
+ }
389
+ if (opts.businessHours?.length) {
390
+ content.push({
391
+ tag: 'business_hours',
392
+ attrs: {},
393
+ content: opts.businessHours.map(h => ({
394
+ tag: 'business_hours_config',
395
+ attrs: {
396
+ day_of_week: h.day,
397
+ mode: h.mode,
398
+ open_time: h.openTime,
399
+ close_time: h.closeTime
400
+ }
401
+ }))
402
+ });
403
+ }
404
+ const node = {
405
+ tag: 'iq',
406
+ attrs: {
407
+ type: 'set',
408
+ to: 's.whatsapp.net',
409
+ id
410
+ },
411
+ content: [
412
+ {
413
+ tag: 'business_profile',
414
+ attrs: {},
415
+ content
416
+ }
417
+ ]
418
+ };
419
+ await this.sendIqAndWait(node, id);
420
+ }
421
+ /**
422
+ * Get business profile
423
+ */
424
+ async get(jid) {
425
+ const id = (0, message_encoder_js_1.generateStanzaId)();
426
+ const node = {
427
+ tag: 'iq',
428
+ attrs: {
429
+ type: 'get',
430
+ to: (0, jid_js_1.jidToString)(jid),
431
+ id
432
+ },
433
+ content: [{
434
+ tag: 'business_profile',
435
+ attrs: {}
436
+ }]
437
+ };
438
+ const response = await this.sendIqAndWait(node, id);
439
+ const profileNode = response.content?.find(c => c.tag === 'business_profile');
440
+ if (!profileNode)
441
+ return null;
442
+ const result = {};
443
+ if (Array.isArray(profileNode.content)) {
444
+ for (const child of profileNode.content) {
445
+ switch (child.tag) {
446
+ case 'description':
447
+ result.description = typeof child.content === 'string' ? child.content : '';
448
+ break;
449
+ case 'email':
450
+ result.email = typeof child.content === 'string' ? child.content : '';
451
+ break;
452
+ case 'category':
453
+ result.category = child.attrs?.name;
454
+ break;
455
+ case 'address':
456
+ result.address = typeof child.content === 'string' ? child.content : '';
457
+ break;
458
+ }
459
+ }
460
+ }
461
+ return result;
462
+ }
463
+ sendIqAndWait(node, id, timeoutMs = 15000) {
464
+ return new Promise((resolve, reject) => {
465
+ const timeout = setTimeout(() => reject(new Error(`IQ ${id} timeout`)), timeoutMs);
466
+ this.registerIqHandler(id, (response) => {
467
+ clearTimeout(timeout);
468
+ resolve(response);
469
+ });
470
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
471
+ });
472
+ }
473
+ }
474
+ exports.BusinessProfileManager = BusinessProfileManager;
475
+ class CatalogManager {
476
+ send;
477
+ registerIqHandler;
478
+ logger;
479
+ constructor(opts) {
480
+ this.send = opts.send;
481
+ this.registerIqHandler = opts.registerIqHandler;
482
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
483
+ }
484
+ /**
485
+ * Add product to catalog
486
+ */
487
+ async addProduct(item) {
488
+ const id = (0, message_encoder_js_1.generateStanzaId)();
489
+ const productId = `prod-${Date.now()}`;
490
+ const node = {
491
+ tag: 'iq',
492
+ attrs: {
493
+ type: 'set',
494
+ to: 's.whatsapp.net',
495
+ id
496
+ },
497
+ content: [{
498
+ tag: 'product',
499
+ attrs: {
500
+ id: productId,
501
+ name: item.name,
502
+ price: item.price.toString(),
503
+ currency: item.currency
504
+ },
505
+ content: [
506
+ item.description ? { tag: 'description', attrs: {}, content: item.description } : null,
507
+ item.url ? { tag: 'url', attrs: {}, content: item.url } : null
508
+ ].filter(Boolean)
509
+ }]
510
+ };
511
+ await this.sendIqAndWait(node, id);
512
+ return productId;
513
+ }
514
+ /**
515
+ * Get catalog for business account
516
+ */
517
+ async getCatalog(jid) {
518
+ const id = (0, message_encoder_js_1.generateStanzaId)();
519
+ const node = {
520
+ tag: 'iq',
521
+ attrs: {
522
+ type: 'get',
523
+ to: (0, jid_js_1.jidToString)(jid),
524
+ id
525
+ },
526
+ content: [{
527
+ tag: 'catalog',
528
+ attrs: {}
529
+ }]
530
+ };
531
+ const response = await this.sendIqAndWait(node, id);
532
+ const catalogNode = response.content?.find(c => c.tag === 'catalog');
533
+ if (!catalogNode)
534
+ return [];
535
+ const items = [];
536
+ if (Array.isArray(catalogNode.content)) {
537
+ for (const child of catalogNode.content) {
538
+ if (child.tag === 'product') {
539
+ items.push({
540
+ id: child.attrs?.id,
541
+ name: child.attrs?.name,
542
+ description: typeof child.content === 'string' ? child.content : '',
543
+ price: parseFloat(child.attrs?.price ?? '0'),
544
+ currency: child.attrs?.currency ?? 'USD',
545
+ url: child.attrs?.url,
546
+ imageUrl: child.attrs?.imageUrl
547
+ });
548
+ }
549
+ }
550
+ }
551
+ return items;
552
+ }
553
+ sendIqAndWait(node, id, timeoutMs = 15000) {
554
+ return new Promise((resolve, reject) => {
555
+ const timeout = setTimeout(() => reject(new Error(`IQ ${id} timeout`)), timeoutMs);
556
+ this.registerIqHandler(id, (response) => {
557
+ clearTimeout(timeout);
558
+ resolve(response);
559
+ });
560
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
561
+ });
562
+ }
563
+ }
564
+ exports.CatalogManager = CatalogManager;
565
+ class LabelsManager {
566
+ send;
567
+ registerIqHandler;
568
+ logger;
569
+ constructor(opts) {
570
+ this.send = opts.send;
571
+ this.registerIqHandler = opts.registerIqHandler;
572
+ this.logger = opts.logger ?? new logger_js_1.SilentLogger();
573
+ }
574
+ /**
575
+ * Create label
576
+ */
577
+ async create(name, color) {
578
+ const id = (0, message_encoder_js_1.generateStanzaId)();
579
+ const labelId = `label-${Date.now()}`;
580
+ const node = {
581
+ tag: 'iq',
582
+ attrs: {
583
+ type: 'set',
584
+ to: 's.whatsapp.net',
585
+ id
586
+ },
587
+ content: [{
588
+ tag: 'label',
589
+ attrs: {
590
+ id: labelId,
591
+ name,
592
+ color: color.toString()
593
+ }
594
+ }]
595
+ };
596
+ await this.sendIqAndWait(node, id);
597
+ return { id: labelId, name, color };
598
+ }
599
+ /**
600
+ * Assign label ke chat
601
+ */
602
+ async assignToChat(labelId, chatJid) {
603
+ const id = (0, message_encoder_js_1.generateStanzaId)();
604
+ const node = {
605
+ tag: 'iq',
606
+ attrs: {
607
+ type: 'set',
608
+ to: 's.whatsapp.net',
609
+ id
610
+ },
611
+ content: [{
612
+ tag: 'label',
613
+ attrs: {
614
+ id: labelId,
615
+ action: 'assign',
616
+ chat: (0, jid_js_1.jidToString)(chatJid)
617
+ }
618
+ }]
619
+ };
620
+ await this.sendIqAndWait(node, id);
621
+ }
622
+ /**
623
+ * Get all labels
624
+ */
625
+ async list() {
626
+ const id = (0, message_encoder_js_1.generateStanzaId)();
627
+ const node = {
628
+ tag: 'iq',
629
+ attrs: {
630
+ type: 'get',
631
+ to: 's.whatsapp.net',
632
+ id
633
+ },
634
+ content: [{
635
+ tag: 'labels',
636
+ attrs: {}
637
+ }]
638
+ };
639
+ const response = await this.sendIqAndWait(node, id);
640
+ const labelsNode = response.content?.find(c => c.tag === 'labels');
641
+ if (!labelsNode)
642
+ return [];
643
+ const labels = [];
644
+ if (Array.isArray(labelsNode.content)) {
645
+ for (const child of labelsNode.content) {
646
+ if (child.tag === 'label') {
647
+ labels.push({
648
+ id: child.attrs?.id,
649
+ name: child.attrs?.name,
650
+ color: parseInt(child.attrs?.color ?? '0', 10),
651
+ priority: child.attrs?.priority ? parseInt(child.attrs.priority, 10) : undefined,
652
+ deleted: child.attrs?.deleted === 'true'
653
+ });
654
+ }
655
+ }
656
+ }
657
+ return labels;
658
+ }
659
+ sendIqAndWait(node, id, timeoutMs = 15000) {
660
+ return new Promise((resolve, reject) => {
661
+ const timeout = setTimeout(() => reject(new Error(`IQ ${id} timeout`)), timeoutMs);
662
+ this.registerIqHandler(id, (response) => {
663
+ clearTimeout(timeout);
664
+ resolve(response);
665
+ });
666
+ this.send((0, binary_js_1.encodeBinaryNode)(node));
667
+ });
668
+ }
669
+ }
670
+ exports.LabelsManager = LabelsManager;
671
+ //# sourceMappingURL=features.js.map