waengine 2.3.9 → 2.3.10

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
@@ -100,7 +100,7 @@ client.on('truly_connected', (data) => {
100
100
  - **Button Messages** - Interactive buttons with callbacks
101
101
  - **List Messages** - Organized lists with sections
102
102
  - **Template Messages** - Reusable message templates
103
- - **Carousel Messages** - Swipeable card carousels with VIDEO support! 🎬
103
+ - **Interactive Carousel** - REAL swipeable carousel in ONE message with VIDEO support! 🎬
104
104
 
105
105
  ### 👥 **Advanced Group Features**
106
106
  - **Group Settings** - Control who can send messages
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waengine",
3
- "version": "2.3.9",
3
+ "version": "2.3.10",
4
4
  "description": "🚀 WAEngine - The most powerful WhatsApp Bot Library with 860+ Working Features, Complete Baileys Integration & Production-Ready Stability",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -237,73 +237,109 @@ export class AdvancedMessage {
237
237
 
238
238
  async sendCarouselMessage(cards) {
239
239
  try {
240
- // WhatsApp Carousel mit Video & Image Support
241
- const results = [];
240
+ // REAL WhatsApp Interactive Carousel - ONE message with swipeable cards!
241
+ const carouselMessage = {
242
+ interactiveMessage: {
243
+ header: {
244
+ title: 'Carousel',
245
+ hasMediaAttachment: false
246
+ },
247
+ body: {
248
+ text: 'Swipe to see all options'
249
+ },
250
+ carouselMessage: {
251
+ cards: cards.map((card, index) => {
252
+ const carouselCard = {
253
+ body: {
254
+ text: card.body || ''
255
+ },
256
+ footer: {
257
+ text: card.footer || `${index + 1}/${cards.length}`
258
+ },
259
+ nativeFlowMessage: {
260
+ buttons: []
261
+ }
262
+ };
263
+
264
+ // Add header with media (video or image)
265
+ if (card.video) {
266
+ carouselCard.header = {
267
+ title: card.title,
268
+ subtitle: card.subtitle || '',
269
+ hasMediaAttachment: true,
270
+ videoMessage: {
271
+ url: card.video,
272
+ gifPlayback: card.gifPlayback || false
273
+ }
274
+ };
275
+ } else if (card.image) {
276
+ carouselCard.header = {
277
+ title: card.title,
278
+ subtitle: card.subtitle || '',
279
+ hasMediaAttachment: true,
280
+ imageMessage: {
281
+ url: card.image
282
+ }
283
+ };
284
+ } else {
285
+ carouselCard.header = {
286
+ title: card.title,
287
+ subtitle: card.subtitle || '',
288
+ hasMediaAttachment: false
289
+ };
290
+ }
291
+
292
+ // Add buttons
293
+ if (card.buttons && card.buttons.length > 0) {
294
+ carouselCard.nativeFlowMessage.buttons = card.buttons.map(btn => ({
295
+ name: 'quick_reply',
296
+ buttonParamsJson: JSON.stringify({
297
+ display_text: btn.text || btn.title,
298
+ id: btn.id || `btn_${index}`
299
+ })
300
+ }));
301
+ }
302
+
303
+ return carouselCard;
304
+ })
305
+ }
306
+ }
307
+ };
242
308
 
243
- for (let i = 0; i < cards.length; i++) {
244
- const card = cards[i];
245
-
246
- // Build card text
247
- let cardText = `🎴 **${i + 1}/${cards.length}: ${card.title}**\n`;
248
- if (card.subtitle) cardText += `${card.subtitle}\n\n`;
249
- if (card.body) cardText += `${card.body}`;
250
- if (card.footer) cardText += `\n\n_${card.footer}_`;
309
+ return await this.socket.sendMessage(this.msg.from, carouselMessage);
310
+
311
+ } catch (error) {
312
+ console.error('❌ Interactive Carousel fehlgeschlagen:', error);
313
+
314
+ // Fallback: List Message (closest to carousel)
315
+ try {
316
+ const listMessage = {
317
+ text: 'Select an option',
318
+ footer: `${cards.length} options available`,
319
+ title: 'Carousel',
320
+ buttonText: 'View Options',
321
+ sections: [{
322
+ title: 'All Options',
323
+ rows: cards.map((card, i) => ({
324
+ title: card.title,
325
+ description: card.body || card.subtitle || '',
326
+ rowId: `carousel_${i}`
327
+ }))
328
+ }]
329
+ };
251
330
 
252
- // Add buttons as text
253
- if (card.buttons && card.buttons.length > 0) {
254
- cardText += `\n\n**Options:**\n`;
255
- card.buttons.forEach((btn, btnIndex) => {
256
- cardText += `${btnIndex + 1}. ${btn.text || btn.title}\n`;
257
- });
258
- }
331
+ return await this.socket.sendMessage(this.msg.from, listMessage);
259
332
 
260
- // Send with appropriate media type
261
- let result;
262
- if (card.video) {
263
- // Video card
264
- result = await this.socket.sendMessage(this.msg.from, {
265
- video: { url: card.video },
266
- caption: cardText,
267
- gifPlayback: card.gifPlayback || false
268
- });
269
- } else if (card.image) {
270
- // Image card
271
- result = await this.socket.sendMessage(this.msg.from, {
272
- image: { url: card.image },
273
- caption: cardText
274
- });
275
- } else {
276
- // Text-only card
277
- result = await this.msg.reply(cardText);
278
- }
333
+ } catch (listError) {
334
+ console.error('❌ List fallback fehlgeschlagen:', listError);
279
335
 
280
- results.push(result);
336
+ // Final fallback: Text
337
+ const fallbackText = cards.map((card, i) =>
338
+ `${i + 1}. **${card.title}**\n${card.body || ''}`
339
+ ).join('\n\n');
281
340
 
282
- // Small delay between cards
283
- if (i < cards.length - 1) {
284
- await new Promise(resolve => setTimeout(resolve, 500));
285
- }
286
- }
287
-
288
- return results;
289
- } catch (error) {
290
- console.error('❌ Carousel Message fehlgeschlagen:', error);
291
- // Fallback: Sende Cards als Text
292
- const results = [];
293
- for (const card of cards) {
294
- try {
295
- let cardText = `🎴 **${card.title}**\n`;
296
- if (card.subtitle) cardText += `${card.subtitle}\n\n`;
297
- cardText += card.body;
298
- if (card.footer) cardText += `\n\n_${card.footer}_`;
299
-
300
- const result = await this.msg.reply(cardText);
301
- results.push(result);
302
- } catch (cardError) {
303
- console.error('❌ Card fehlgeschlagen:', cardError);
304
- }
341
+ return await this.msg.reply(fallbackText);
305
342
  }
306
- return results;
307
343
  }
308
344
  }
309
345
  }
@@ -6,88 +6,91 @@ export class UIComponents {
6
6
  // ===== CAROUSEL MESSAGES =====
7
7
 
8
8
  /**
9
- * Send carousel message with multiple cards
10
- * Supports: images, videos, and text-only cards
9
+ * Send REAL carousel message with swipeable cards in ONE message
10
+ * Uses WhatsApp Interactive Message API
11
11
  */
12
12
  async sendCarousel(chatId, cards, options = {}) {
13
13
  try {
14
- // WhatsApp doesn't support native carousels, so we create a rich media alternative
15
- let carouselText = `🎠 **${options.title || 'Carousel'}**\n\n`;
16
-
17
- cards.forEach((card, index) => {
18
- carouselText += `**${index + 1}. ${card.title}**\n`;
19
- if (card.subtitle) carouselText += `${card.subtitle}\n`;
20
- if (card.body) carouselText += `${card.body}\n`;
21
- if (card.price) carouselText += `💰 ${card.price}\n`;
22
-
23
- // Add buttons as text options
24
- if (card.buttons && card.buttons.length > 0) {
25
- carouselText += `Options: `;
26
- card.buttons.forEach((btn, btnIndex) => {
27
- carouselText += `[${btnIndex + 1}] ${btn.title} `;
28
- });
29
- carouselText += `\n`;
14
+ // Build proper WhatsApp Interactive Carousel Message
15
+ const carouselMessage = {
16
+ interactiveMessage: {
17
+ header: {
18
+ title: options.title || 'Carousel',
19
+ hasMediaAttachment: false
20
+ },
21
+ body: {
22
+ text: options.body || 'Swipe to see all options'
23
+ },
24
+ footer: {
25
+ text: options.footer || ''
26
+ },
27
+ carouselMessage: {
28
+ cards: cards.map((card, index) => {
29
+ const carouselCard = {
30
+ body: {
31
+ text: card.body || card.description || ''
32
+ },
33
+ footer: {
34
+ text: card.footer || `${index + 1}/${cards.length}`
35
+ },
36
+ nativeFlowMessage: {
37
+ buttons: []
38
+ }
39
+ };
40
+
41
+ // Add header with media
42
+ if (card.video) {
43
+ carouselCard.header = {
44
+ title: card.title,
45
+ subtitle: card.subtitle || '',
46
+ hasMediaAttachment: true,
47
+ videoMessage: {
48
+ url: card.video,
49
+ gifPlayback: card.gifPlayback || false
50
+ }
51
+ };
52
+ } else if (card.image) {
53
+ carouselCard.header = {
54
+ title: card.title,
55
+ subtitle: card.subtitle || '',
56
+ hasMediaAttachment: true,
57
+ imageMessage: {
58
+ url: card.image
59
+ }
60
+ };
61
+ } else {
62
+ carouselCard.header = {
63
+ title: card.title,
64
+ subtitle: card.subtitle || '',
65
+ hasMediaAttachment: false
66
+ };
67
+ }
68
+
69
+ // Add buttons
70
+ if (card.buttons && card.buttons.length > 0) {
71
+ carouselCard.nativeFlowMessage.buttons = card.buttons.map(btn => ({
72
+ name: 'quick_reply',
73
+ buttonParamsJson: JSON.stringify({
74
+ display_text: btn.title || btn.text,
75
+ id: btn.id || `btn_${index}_${btn.title}`
76
+ })
77
+ }));
78
+ }
79
+
80
+ return carouselCard;
81
+ })
82
+ }
30
83
  }
31
-
32
- carouselText += `\n`;
33
- });
34
-
35
- if (options.footer) {
36
- carouselText += `\n_${options.footer}_`;
37
- }
84
+ };
38
85
 
39
- // Send each card with its media (image or video)
40
- for (let i = 0; i < cards.length; i++) {
41
- const card = cards[i];
42
-
43
- // Build card text
44
- let cardText = `**${i + 1}/${cards.length}: ${card.title}**\n`;
45
- if (card.subtitle) cardText += `${card.subtitle}\n`;
46
- if (card.body) cardText += `${card.body}\n`;
47
- if (card.price) cardText += `💰 ${card.price}\n`;
48
-
49
- if (card.buttons && card.buttons.length > 0) {
50
- cardText += `\nOptions: `;
51
- card.buttons.forEach((btn, btnIndex) => {
52
- cardText += `[${btnIndex + 1}] ${btn.title} `;
53
- });
54
- }
55
-
56
- if (options.footer) {
57
- cardText += `\n\n_${options.footer}_`;
58
- }
59
-
60
- // Send with appropriate media type
61
- if (card.video) {
62
- // Video card
63
- await this.client.socket.sendMessage(chatId, {
64
- video: { url: card.video },
65
- caption: cardText,
66
- gifPlayback: card.gifPlayback || false
67
- });
68
- } else if (card.image) {
69
- // Image card
70
- await this.client.socket.sendMessage(chatId, {
71
- image: { url: card.image },
72
- caption: cardText
73
- });
74
- } else {
75
- // Text-only card
76
- await this.client.socket.sendMessage(chatId, {
77
- text: cardText
78
- });
79
- }
80
-
81
- // Small delay between cards to prevent spam
82
- if (i < cards.length - 1) {
83
- await new Promise(resolve => setTimeout(resolve, 500));
84
- }
85
- }
86
+ // Send the carousel
87
+ const result = await this.client.socket.sendMessage(chatId, carouselMessage);
86
88
 
87
89
  return {
88
90
  success: true,
89
- type: 'carousel',
91
+ type: 'interactive_carousel',
90
92
  cards: cards.length,
93
+ messageId: result.key.id,
91
94
  mediaTypes: {
92
95
  videos: cards.filter(c => c.video).length,
93
96
  images: cards.filter(c => c.image).length,
@@ -96,14 +99,80 @@ export class UIComponents {
96
99
  };
97
100
 
98
101
  } catch (error) {
99
- console.error('❌ Carousel error:', error);
102
+ console.error('❌ Interactive Carousel failed, trying fallback:', error);
100
103
 
101
- // Fallback to simple text
102
- const fallbackText = `📋 **${options.title || 'Options'}**\n\n` +
103
- cards.map((card, i) => `${i + 1}. ${card.title}`).join('\n');
104
-
105
- await this.client.socket.sendMessage(chatId, { text: fallbackText });
106
- return { success: true, type: 'fallback', cards: cards.length };
104
+ // Fallback 1: Try native carousel format
105
+ try {
106
+ const nativeCarousel = {
107
+ viewOnceMessage: {
108
+ message: {
109
+ messageContextInfo: {
110
+ deviceListMetadata: {},
111
+ deviceListMetadataVersion: 2
112
+ },
113
+ interactiveMessage: {
114
+ nativeFlowMessage: {
115
+ buttons: cards.map((card, i) => ({
116
+ name: 'single_select',
117
+ buttonParamsJson: JSON.stringify({
118
+ title: card.title,
119
+ sections: [{
120
+ title: card.subtitle || '',
121
+ rows: [{
122
+ title: card.title,
123
+ description: card.body,
124
+ id: `card_${i}`
125
+ }]
126
+ }]
127
+ })
128
+ }))
129
+ }
130
+ }
131
+ }
132
+ }
133
+ };
134
+
135
+ const result = await this.client.socket.sendMessage(chatId, nativeCarousel);
136
+ return { success: true, type: 'native_carousel', cards: cards.length };
137
+
138
+ } catch (nativeError) {
139
+ console.error('❌ Native carousel also failed, using list fallback:', nativeError);
140
+
141
+ // Fallback 2: Use List Message (closest to carousel)
142
+ try {
143
+ const listMessage = {
144
+ text: options.title || 'Select an option',
145
+ footer: options.footer || `${cards.length} options available`,
146
+ title: options.title || 'Carousel',
147
+ buttonText: 'View Options',
148
+ sections: [{
149
+ title: 'All Options',
150
+ rows: cards.map((card, i) => ({
151
+ title: card.title,
152
+ description: card.body || card.subtitle || '',
153
+ rowId: `carousel_${i}`
154
+ }))
155
+ }]
156
+ };
157
+
158
+ const result = await this.client.socket.sendMessage(chatId, listMessage);
159
+ return { success: true, type: 'list_fallback', cards: cards.length };
160
+
161
+ } catch (listError) {
162
+ console.error('❌ List fallback failed, using text:', listError);
163
+
164
+ // Fallback 3: Simple text with emojis
165
+ const fallbackText = `🎠 **${options.title || 'Carousel'}**\n\n` +
166
+ cards.map((card, i) =>
167
+ `${i + 1}️⃣ **${card.title}**\n` +
168
+ `${card.subtitle ? ` ${card.subtitle}\n` : ''}` +
169
+ ` ${card.body || ''}\n`
170
+ ).join('\n');
171
+
172
+ await this.client.socket.sendMessage(chatId, { text: fallbackText });
173
+ return { success: true, type: 'text_fallback', cards: cards.length };
174
+ }
175
+ }
107
176
  }
108
177
  }
109
178