@gamastudio/sendwave-provider 0.0.24 → 1.0.1

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,4 +1,4 @@
1
- # 📖 Sendwave Provider
1
+ # 📖 Sendwaves Provider
2
2
 
3
3
  **A dynamic library for integrating and managing Sendwave instances for WhatsApp bot development.**
4
4
 
@@ -23,80 +23,90 @@ const provider = createSendWaveProvider({
23
23
 
24
24
  // Initialize the provider
25
25
  await provider.initAll(provider.globalVendorArgs.port);
26
- await provider.initVendor();
27
26
  ```
28
27
 
29
- ### Complete Configuration Options
28
+ ## 📤 Sending Messages
29
+
30
+ ### Text Messages
30
31
 
31
32
  ```typescript
32
- import { createSendWaveProvider, type GlobalVendorArgs } from "@gamastudio/sendwave-provider";
33
-
34
- const config: Partial<GlobalVendorArgs> = {
35
- name: "MyBot", // Instance identifier
36
- apiKey: "your-api-key", // Sendwave API key
37
- port: 3000, // HTTP server port
38
- delay: 1000, // Default delay between messages (ms)
39
- linkPreview: true, // Enable/disable link previews
40
- message: {
41
- mergeMessage: false, // Enable message buffering
42
- timeMergeMessage: 3, // Buffer timeout in seconds
43
- },
44
- queueFlow: {
45
- enabled: true, // Enable queue flow system
46
- warningTimeout: 30 * 60 * 1000, // 30 minutes before warning
47
- endTimeout: 2 * 60 * 1000, // 2 minutes after warning to end session
48
- warningMessage: "⏳ You seem to be inactive. Are you still there?",
49
- },
50
- };
33
+ await provider.sendText({
34
+ from: "573123456789",
35
+ text: "Hello! Welcome to our service.",
36
+ });
37
+ ```
38
+
39
+ ### Interactive Polls (New! 📊)
51
40
 
52
- const provider = createSendWaveProvider(config);
41
+ ```typescript
42
+ await provider.sendPoll({
43
+ from: "573123456789",
44
+ poll: {
45
+ name: "¿Qué te parece el nuevo proveedor?",
46
+ values: ["Excelente", "Bueno", "Mejorable"],
47
+ selectableCount: 1 // Optional, default: 1
48
+ }
49
+ });
53
50
  ```
54
51
 
55
- ## 📤 Sending Messages
52
+ ### Intelligent Buttons (Easier! 🔘)
56
53
 
57
- ### Text Messages
54
+ The provider is now "intelligent" and accepts multiple formats for buttons.
58
55
 
59
56
  ```typescript
60
- await provider.sendText({
57
+ // 1. Super Simple (Quick Replies)
58
+ await provider.sendButton({
61
59
  from: "573123456789",
62
- text: "Hello! Welcome to our service.",
60
+ title: "Main Menu",
61
+ buttons: ["🛒 Catalog", "🎁 Promotions", "📞 Support"]
62
+ });
63
+
64
+ // 2. Mixed Format (Text & Functional buttons)
65
+ await provider.sendButton({
66
+ from: "573123456789",
67
+ title: "Payment Options",
68
+ body: "Select your preferred method",
69
+ buttons: [
70
+ { type: "reply", displayText: "Cash" },
71
+ { type: "url", displayText: "Visit Web", url: "https://example.com" },
72
+ { type: "copy", displayText: "Copy Coupon", copyText: "SAVE20" },
73
+ { type: "pix", displayText: "Pay with Pix", keyType: "email", key: "pix@bot.com" }
74
+ ]
63
75
  });
64
76
  ```
65
77
 
66
- ### Interactive Lists
78
+ **Supported Button Types:**
79
+ - `reply`: Standard quick reply button.
80
+ - `url`: Opens a website.
81
+ - `call`: Triggers a phone call.
82
+ - `copy`: Copies text or code to clipboard.
83
+ - `pix`: Brazilian payment system integration.
84
+
85
+ ### Intelligent Lists (Simple & Advanced 📜)
67
86
 
68
87
  ```typescript
88
+ // Simple List (Array of strings)
69
89
  await provider.sendList({
70
90
  from: "573123456789",
71
91
  list: {
72
92
  title: "Select an option",
73
- description: "Choose from the following options:",
74
- button: "View Menu",
75
- content: ["🛒 Catalog", "🎁 Promotions", "📞 Support"],
76
- footerText: "Powered by Sendwave", // Optional
93
+ button: "View Options",
94
+ content: ["Option 1", "Option 2", "Option 3"],
77
95
  },
78
96
  });
79
97
 
80
- // Advanced list with sections
98
+ // Advanced List with sections
81
99
  await provider.sendList({
82
100
  from: "573123456789",
83
101
  list: {
84
102
  title: "Product Categories",
85
- description: "Browse our products by category",
86
103
  button: "Select Category",
87
104
  content: [
88
105
  {
89
106
  title: "Electronics",
90
107
  rows: [
91
- { title: "Smartphones", description: "Latest models available" },
92
- { title: "Laptops", description: "Gaming and office laptops" },
93
- ],
94
- },
95
- {
96
- title: "Clothing",
97
- rows: [
98
- { title: "Men's Fashion" },
99
- { title: "Women's Fashion" },
108
+ { title: "Smartphones", description: "Latest models" },
109
+ { title: "Laptops", description: "Gaming and office" },
100
110
  ],
101
111
  },
102
112
  ],
@@ -107,268 +117,43 @@ await provider.sendList({
107
117
  ### Media Messages
108
118
 
109
119
  ```typescript
110
- // Images
111
- await provider.sendImage({
112
- from: "573123456789",
113
- url: "https://example.com/image.jpg",
114
- text: "Check out this product!",
115
- });
116
-
117
- // Videos
118
- await provider.sendVideo({
119
- from: "573123456789",
120
- url: "https://example.com/video.mp4",
121
- text: "Product demonstration",
122
- });
123
-
124
- // Documents
125
- await provider.sendFile({
126
- from: "573123456789",
127
- url: "https://example.com/catalog.pdf",
128
- fileName: "Product Catalog 2024.pdf",
129
- text: "Here's our complete catalog",
130
- });
131
-
132
- // Voice Messages
133
- await provider.sendVoice({
134
- from: "573123456789",
135
- url: "https://example.com/audio.mp3",
136
- });
137
- ```
138
-
139
- ### Button Messages
140
-
141
- ```typescript
142
- await provider.sendButton({
143
- from: "573123456789",
144
- title: "Customer Support",
145
- body: "How can we help you today?",
146
- description: "Choose one of the options below",
147
- footer: "We're here to help!",
148
- buttons: [
149
- { type: "reply", text: "Technical Support" },
150
- { type: "reply", text: "Billing Questions" },
151
- { type: "url", text: "Visit Website" },
152
- ],
153
- });
120
+ // Images, Videos, Documents, and Voice
121
+ await provider.sendImage({ from: "573123456789", url: "https://...", text: "Caption" });
122
+ await provider.sendVideo({ from: "573123456789", url: "https://...", text: "Caption" });
123
+ await provider.sendFile({ from: "573123456789", url: "https://...", fileName: "file.pdf" });
124
+ await provider.sendVoice({ from: "573123456789", url: "https://..." });
154
125
  ```
155
126
 
156
- ## 🔄 Queue Flow System
157
-
158
- The Queue Flow system **automatically** manages user sessions, handling timeouts and inactive users to optimize bot performance. Just enable it in the configuration and everything works automatically.
127
+ ## 🔄 Queue Flow System (100% Automatic)
159
128
 
160
- ### Enable Queue Flow (100% Automatic)
129
+ The Queue Flow system manages user sessions, handling timeouts and inactive users automatically.
161
130
 
162
131
  ```typescript
163
132
  const provider = createSendWaveProvider({
164
133
  name: "MyBot",
165
134
  apiKey: "your-key",
166
135
  queueFlow: {
167
- enabled: true, // 🚀 That's it! Everything else is automatic
168
- warningTimeout: 30 * 60 * 1000, // 30 minutes (optional)
169
- endTimeout: 2 * 60 * 1000, // 2 minutes (optional)
170
- warningMessage: "⏳ You seem inactive. Still there?", // (optional)
136
+ enabled: true,
137
+ warningTimeout: 30 * 60 * 1000, // 30 mins
138
+ endTimeout: 2 * 60 * 1000, // 2 mins after warning
139
+ warningMessage: "⏳ You seem inactive. Still there?",
171
140
  },
172
141
  });
173
142
  ```
174
143
 
175
- **✨ What happens automatically when enabled:**
176
- - User timeouts are reset on every message (no code needed)
177
- - Warning messages are sent after inactivity period
178
- - Users are automatically removed from queue after timeout
179
- - `END_FLOW` event is triggered for BuilderBot integration
180
- - No additional code required in your flows!
181
-
182
- ### Queue Flow Methods
183
-
184
- ```typescript
185
- // Reset user timeout (called automatically on user messages)
186
- provider.resetUserTimeout("573123456789", "John Doe");
187
-
188
- // Clear user timeout manually
189
- provider.clearUserTimeout("573123456789");
190
-
191
- // Force remove user from system
192
- provider.forceClearUser("573123456789");
193
-
194
- // Check if user is ignored
195
- const isIgnored = provider.isUserIgnored("573123456789");
196
-
197
- // Get system statistics
198
- const stats = provider.getQueueFlowStats();
199
- console.log(stats); // { activeTimeouts: 5, ignoredUsers: 2, config: {...} }
200
- ```
201
-
202
- ### Queue Flow Events
203
-
204
- ```typescript
205
- // Listen to user inactivity events
206
- provider.onQueueFlowEvent('userInactive-573123456789', (data) => {
207
- if (data.isActive) {
208
- console.log(`Warning sent to ${data.from}`);
209
- } else {
210
- console.log(`User ${data.from} removed due to inactivity`);
211
- }
212
- });
213
-
214
- // Remove event listeners
215
- provider.offQueueFlowEvent('userInactive-573123456789');
216
- ```
217
-
218
- ## 🤖 BuilderBot Integration
219
-
220
- ### Flow Implementation Examples
221
-
222
- ```typescript
223
- import { addKeyword, EVENTS, utils } from "@builderbot/bot";
224
- import { SendWaveProvider as Provider } from "@gamastudio/sendwave-provider";
225
-
226
- // Welcome Flow
227
- export const flowWelcome = addKeyword<Provider>(EVENTS.WELCOME)
228
- .addAnswer([
229
- "¡Hello! I'm *Max*, your virtual assistant. 😊",
230
- "Welcome to our service.",
231
- ])
232
- .addAction(async (_, { gotoFlow }) => await gotoFlow(flowMainMenu));
233
-
234
- // End Flow (integrates with Queue Flow system)
235
- export const flowEnd = addKeyword<Provider>(utils.setEvent("END_FLOW"))
236
- .addAction(async (ctx, { endFlow, provider }) => {
237
- // Clean up user from queue flow system
238
- provider.forceClearUser(ctx.from);
239
- endFlow("Chat closed due to inactivity. Write again if you need help!");
240
- });
241
-
242
- // Customer Support Flow
243
- export const flowSupport = addKeyword<Provider>("SUPPORT")
244
- .addAction(async (ctx, { blacklist, endFlow, provider }) => {
245
- // Add to blacklist and clean queue
246
- blacklist.add(ctx.from);
247
- provider.forceClearUser(ctx.from);
248
-
249
- return endFlow(
250
- "An agent will contact you shortly. Thanks for your patience! 👨‍💻"
251
- );
252
- });
253
-
254
- // Interactive Menu Flow
255
- export const flowMainMenu = addKeyword<Provider>("MAIN_MENU")
256
- .addAction(async (ctx, { provider }) => {
257
- await provider.sendList({
258
- from: ctx.from,
259
- list: {
260
- button: "View Menu",
261
- title: "",
262
- description: "Select an option from the menu. Let's get started! 👇",
263
- content: ["🛒 Catalog", "🎁 Promotions", "📞 Support"],
264
- },
265
- });
266
- })
267
- .addAction({ capture: true }, async (ctx, { gotoFlow, fallBack }) => {
268
- const option = ctx.body.trim();
269
-
270
- switch (option) {
271
- case "🛒 Catalog":
272
- await gotoFlow(flowCatalog);
273
- break;
274
- case "🎁 Promotions":
275
- await gotoFlow(flowPromotions);
276
- break;
277
- case "📞 Support":
278
- await gotoFlow(flowSupport);
279
- break;
280
- default:
281
- fallBack("I didn't understand that option. Please select from the list.");
282
- break;
283
- }
284
- });
285
- ```
286
-
287
- ### Delayed Response Handling (100% Automatic)
288
-
289
- The Queue Flow system automatically handles users who take time to respond with **zero configuration needed**:
290
-
291
- ```typescript
292
- // ✅ AUTOMATIC PROCESS:
293
- // 1. User sends message -> timeout resets automatically
294
- // 2. After 30 minutes of inactivity -> warning message sent automatically
295
- // 3. After 2 more minutes -> user session ends with END_FLOW event automatically
296
- // 4. User is cleaned from queue automatically
297
-
298
- // 🎯 ONLY THING YOU NEED: Handle the END_FLOW event in your flows
299
- export const flowEnd = addKeyword<Provider>(utils.setEvent("END_FLOW"))
300
- .addAction(async (ctx, { endFlow }) => {
301
- // This runs automatically when user is inactive for too long
302
- endFlow("Session ended due to inactivity. Contact us again anytime!");
303
- // ✨ Queue cleanup happens automatically, no code needed!
304
- });
305
- ```
306
-
307
144
  ## 🎯 Event System
308
145
 
309
146
  ```typescript
310
- // Connection events
311
- provider.on('ready', (isReady: boolean) => {
312
- console.log('Connection status:', isReady ? 'Connected' : 'Disconnected');
313
- });
314
-
315
- provider.on('auth_failure', (error) => {
316
- console.error('Authentication failed:', error);
317
- });
318
-
319
- // Message events
320
- provider.on('message', (ctx) => {
321
- console.log(`Message from ${ctx.from}: ${ctx.body}`);
322
- });
323
-
324
- provider.on('user-message', (data) => {
325
- console.log(`User message: ${data.body}`);
326
- // Timeout automatically resets here if queue flow is enabled
327
- });
147
+ provider.on('ready', (isReady) => console.log('Connected:', isReady));
148
+ provider.on('message', (ctx) => console.log(`From ${ctx.from}: ${ctx.body}`));
328
149
  ```
329
150
 
330
- ## 🛠️ Development Scripts
331
-
332
- | Script | Description |
333
- | --------------- | ------------------------------------------------ |
334
- | `npm run dev` | Run library in development mode with auto-reload |
335
- | `npm run build` | Compile TypeScript and resolve path aliases |
336
- | `npm run test` | Run basic test file |
337
-
338
- ## 📦 Package Structure
339
-
340
- When you install `@gamastudio/sendwave-provider`:
341
-
342
- - Compiled code in `/build` directory
343
- - TypeScript declarations (`.d.ts`) included
344
- - Main entry point: `build/index.js`
345
- - Full TypeScript support with path aliases resolved
346
-
347
- ## 📋 Requirements
348
-
349
- - **Node.js** 18+
350
- - **npm** or **pnpm**
351
- - **TypeScript** 5+
352
- - Valid Sendwave API credentials
353
-
354
- ## 🔥 Important Notes
355
-
356
- This library is in **beta** state and subject to internal changes.
357
-
358
- **Recommendations:**
359
- - Use in testing or controlled environments initially
360
- - Update beta versions as new releases become available
361
- - Monitor the queue flow system performance with your user base
362
-
363
151
  ## 🏗️ Architecture
364
152
 
365
- - **Provider Layer**: Main SendWaveProvider class extending BuilderBot
366
- - **Core Layer**: Message processing and webhook handling
367
- - **Sender Layer**: Message sending functionality with media support
368
- - **Queue Flow**: Advanced user session management with timeout handling
369
- - **Utils**: Media detection, message parsing, and flow utilities
153
+ - **Provider Layer**: Main SendWaveProvider class.
154
+ - **Sender Layer**: Intelligent message transformation.
155
+ - **Queue Flow**: Automatic session management.
370
156
 
371
157
  ## 📜 License
372
158
 
373
- ISC License © 2025 - Ameth Galarcio
374
-
159
+ ISC License © 2026 - Ameth Galarcio
@@ -0,0 +1 @@
1
+ export declare const htmlConnect = "\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n <meta charset=\"UTF-8\" />\n <title>Conectar - Sendwave Bot</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <script src=\"https://cdn.tailwindcss.com\"></script>\n</head>\n<body class=\"bg-white text-slate-800 flex items-center justify-center min-h-screen relative overflow-hidden\">\n\n <div class=\"text-center bg-white/20 backdrop-blur-sm p-8 rounded-2xl z-10 shadow-lg max-w-sm w-full flex flex-col items-center gap-4 relative z-10\">\n\n <h1 class=\"text-2xl font-bold text-cyan-700 flex items-center justify-center gap-2\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 80 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_649_78)\"><circle cx=\"40\" cy=\"40\" r=\"40\" fill=\"url(#paint0_linear_649_78)\"></circle><circle cx=\"40\" cy=\"40\" r=\"32\" fill=\"white\"></circle><path d=\"M8.32007 44.16C9.20007 28 44.6401 14.7999 45.6801 32.9599C42.9601 36.0799 40.2667 28.8246 34.8001 31.2799C21.2692 37.3573 40.5601 65.52 66.2401 58.3999C46.8001 85.1999 11.1201 70.32 8.32007 44.16Z\" fill=\"url(#paint1_linear_649_78)\"></path><path d=\"M16.9602 48.5601L10.1602 51.8401C16.9429 70.014 38.3202 78.0001 54.1602 69.2001C36.1602 68.8801 20.9602 61.0401 16.9602 48.5601Z\" fill=\"#1E52CD\"></path><path d=\"M27.5201 15.92C17.52 18 2.40007 34.4 10.1601 51.84L16.9601 48.56C7.60006 19.76 49.2079 23.76 44.182 31.8014C43.782 32.4414 42.742 31.8014 41.382 31.8014C37.9571 31.8014 39.6119 38.1895 45.702 38.6013C61.062 38.6013 53.5997 10.4953 27.5201 15.92Z\" fill=\"#019DF9\"></path><path d=\"M51.28 23.6479C44.08 11.2694 18.48 10.56 10.24 31.84C17.92 17.92 52 10.72 52 36C54.4 33.12 54.8 28.96 51.28 23.6479Z\" fill=\"url(#paint2_linear_649_78)\"></path><path d=\"M68.56 54.4801C69.0879 53.4766 69.382 52.7921 69.92 51.3601L63.04 49.5129C53.3714 42.098 44.9762 42.1171 37.76 47.5129C41.36 54.8801 56 61.1201 68.56 54.4801Z\" fill=\"url(#paint3_linear_649_78)\"></path><path d=\"M71.12 47.36C54.0422 46.2732 46.9167 47.3149 40 50.64C43.1238 53.6518 46.16 55.2001 51.36 56.615C56.1255 54.3514 60.0087 53.2658 69.92 51.36C70.516 49.9742 70.7842 49.1058 71.12 47.36Z\" fill=\"url(#paint4_linear_649_78)\"></path></g><defs><linearGradient id=\"paint0_linear_649_78\" x1=\"40\" y1=\"0\" x2=\"40\" y2=\"80\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#019DF9\"></stop><stop offset=\"1\" stop-color=\"#12298B\"></stop></linearGradient><linearGradient id=\"paint1_linear_649_78\" x1=\"20.7201\" y1=\"23.9199\" x2=\"52.1601\" y2=\"70.9599\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0.00661977\" stop-color=\"#012A6B\"></stop><stop offset=\"1\" stop-color=\"#1E52CD\"></stop></linearGradient><linearGradient id=\"paint2_linear_649_78\" x1=\"51.68\" y1=\"23.5679\" x2=\"9.91999\" y2=\"29.6479\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#B7E8FF\"></stop><stop offset=\"0.526042\" stop-color=\"#97DEFF\"></stop><stop offset=\"1\" stop-color=\"#32B3FC\"></stop></linearGradient><linearGradient id=\"paint3_linear_649_78\" x1=\"40.32\" y1=\"44.8801\" x2=\"71.68\" y2=\"55.8401\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#12298B\"></stop><stop offset=\"1\" stop-color=\"#008CDC\"></stop></linearGradient><linearGradient id=\"paint4_linear_649_78\" x1=\"55.48\" y1=\"47.335\" x2=\"55.48\" y2=\"56.615\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#019DF9\"></stop><stop offset=\"1\" stop-color=\"#34B5FF\"></stop></linearGradient><clipPath id=\"clip0_649_78\"><rect width=\"80\" height=\"80\" fill=\"white\"></rect></clipPath></defs></svg> Conectar instancia\n </h1>\n\n <p class=\"text-slate-500 text-sm\">Escanea el c\u00F3digo QR con WhatsApp o usa el c\u00F3digo de emparejamiento.</p>\n\n <!-- QR image -->\n <div id=\"qr-wrapper\" class=\"flex items-center justify-center w-56 h-56 rounded-xl border-2 border-cyan-200 bg-white shadow-inner overflow-hidden\">\n <div id=\"qr-spinner\" class=\"flex flex-col items-center gap-2 text-slate-400\">\n <svg class=\"animate-spin h-8 w-8 text-cyan-400\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v8H4z\"></path>\n </svg>\n <span class=\"text-xs\">Cargando...</span>\n </div>\n <img id=\"qr-img\" class=\"hidden w-full h-full object-contain\" alt=\"QR Code\" />\n </div>\n\n <!-- Pairing code -->\n <div id=\"pairing-section\" class=\"hidden flex-col items-center gap-1 w-full\">\n <p class=\"text-xs text-slate-400 uppercase tracking-widest font-semibold\">C\u00F3digo de emparejamiento</p>\n <div id=\"pairing-code\" class=\"font-mono text-3xl font-bold tracking-widest text-cyan-700 bg-cyan-50 border border-cyan-200 rounded-xl px-6 py-3 select-all w-full text-center\"></div>\n </div>\n\n <!-- Error -->\n <div id=\"error-section\" class=\"hidden text-red-500 text-sm bg-red-50 border border-red-200 rounded-lg px-4 py-2 w-full text-center\"></div>\n\n <!-- Countdown -->\n <div id=\"countdown-wrapper\" class=\"hidden w-full\">\n <div class=\"flex justify-between text-xs text-slate-400 mb-1\">\n <span>Actualizaci\u00F3n autom\u00E1tica</span>\n <span id=\"countdown-text\">30s</span>\n </div>\n <div class=\"w-full bg-slate-100 rounded-full h-1.5\">\n <div id=\"countdown-bar\" class=\"bg-cyan-500 h-1.5 rounded-full\" style=\"width:100%;transition:width 1s linear\"></div>\n </div>\n </div>\n\n <button onclick=\"fetchQR()\" class=\"text-xs text-cyan-600 hover:text-cyan-800 underline transition-colors\">\n Actualizar ahora\n </button>\n\n </div>\n\n <div class=\"absolute bottom-0 left-0 w-full\">\n <svg width=\"100%\" height=\"100%\" id=\"svg\" viewBox=\"0 0 1440 390\" xmlns=\"http://www.w3.org/2000/svg\" class=\"transition duration-300 ease-in-out delay-150\"><style>\n .path-0{\n animation:pathAnim-0 4s;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n }\n @keyframes pathAnim-0{\n 0%{\n d: path(\"M 0,400 L 0,60 C 71.35843151693669,59.23030191458027 142.71686303387338,58.460603829160526 206,60 C 269.2831369661266,61.539396170839474 324.49097938144325,65.38788659793815 375,61 C 425.50902061855675,56.61211340206185 471.31921944035344,43.987849779086886 537,36 C 602.6807805596466,28.012150220913114 688.232142857143,24.660714285714292 751,32 C 813.767857142857,39.33928571428571 853.7522091310749,57.36929307805596 898,62 C 942.2477908689251,66.63070692194404 990.7590206185569,57.86211340206186 1055,56 C 1119.2409793814431,54.13788659793814 1199.211708394698,59.18225331369662 1266,61 C 1332.788291605302,62.81774668630338 1386.394145802651,61.40887334315169 1440,60 L 1440,400 L 0,400 Z\");\n }\n 25%{\n d: path(\"M 0,400 L 0,60 C 62.18096465390279,54.449189985272454 124.36192930780558,48.898379970544916 187,56 C 249.63807069219442,63.101620029455084 312.73324742268045,82.85567010309279 366,88 C 419.26675257731955,93.14432989690721 462.70508100147276,83.67893961708396 523,76 C 583.2949189985272,68.32106038291604 660.4464285714287,62.428571428571416 723,51 C 785.5535714285713,39.571428571428584 833.509204712813,22.606774668630344 885,32 C 936.490795287187,41.393225331369656 991.5167525773193,77.1443298969072 1056,78 C 1120.4832474226807,78.8556701030928 1194.423784977909,44.8159057437408 1260,36 C 1325.576215022091,27.184094256259204 1382.7881075110454,43.5920471281296 1440,60 L 1440,400 L 0,400 Z\");\n }\n 50%{\n d: path(\"M 0,400 L 0,60 C 79.00460235640648,54.72717231222386 158.00920471281296,49.45434462444772 207,52 C 255.99079528718704,54.54565537555228 274.96778350515467,64.90979381443299 336,68 C 397.03221649484533,71.09020618556701 500.1196612665685,66.90648011782032 561,63 C 621.8803387334315,59.09351988217968 640.5535714285716,55.46428571428572 693,49 C 745.4464285714284,42.53571428571428 831.6660530191457,33.236377025036816 908,30 C 984.3339469808543,26.763622974963184 1050.7822164948454,29.590206185567013 1100,35 C 1149.2177835051546,40.40979381443299 1181.2050810014728,48.40279823269515 1235,53 C 1288.7949189985272,57.59720176730485 1364.3974594992637,58.798600883652426 1440,60 L 1440,400 L 0,400 Z\");\n }\n 75%{\n d: path(\"M 0,400 L 0,60 C 68.61487481590575,59.48784977908689 137.2297496318115,58.97569955817379 200,66 C 262.7702503681885,73.02430044182621 319.69587628865975,87.58505154639175 370,86 C 420.30412371134025,84.41494845360825 463.9867452135493,66.6840942562592 528,54 C 592.0132547864507,41.3159057437408 676.357142857143,33.67857142857143 746,43 C 815.642857142857,52.32142857142857 870.584683357879,78.60162002945508 921,81 C 971.415316642121,83.39837997054492 1017.3041237113403,61.914948453608254 1075,54 C 1132.6958762886597,46.085051546391746 1202.19882179676,51.738586156111936 1265,55 C 1327.80117820324,58.261413843888064 1383.90058910162,59.13070692194403 1440,60 L 1440,400 L 0,400 Z\");\n }\n 100%{\n d: path(\"M 0,400 L 0,60 C 71.35843151693669,59.23030191458027 142.71686303387338,58.460603829160526 206,60 C 269.2831369661266,61.539396170839474 324.49097938144325,65.38788659793815 375,61 C 425.50902061855675,56.61211340206185 471.31921944035344,43.987849779086886 537,36 C 602.6807805596466,28.012150220913114 688.232142857143,24.660714285714292 751,32 C 813.767857142857,39.33928571428571 853.7522091310749,57.36929307805596 898,62 C 942.2477908689251,66.63070692194404 990.7590206185569,57.86211340206186 1055,56 C 1119.2409793814431,54.13788659793814 1199.211708394698,59.18225331369662 1266,61 C 1332.788291605302,62.81774668630338 1386.394145802651,61.40887334315169 1440,60 L 1440,400 L 0,400 Z\");\n }\n }</style><defs><linearGradient id=\"gradient\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\"><stop offset=\"5%\" stop-color=\"#0693e3\"></stop><stop offset=\"95%\" stop-color=\"#8ED1FC\"></stop></linearGradient></defs><path d=\"M 0,400 L 0,60 C 71.35843151693669,59.23030191458027 142.71686303387338,58.460603829160526 206,60 C 269.2831369661266,61.539396170839474 324.49097938144325,65.38788659793815 375,61 C 425.50902061855675,56.61211340206185 471.31921944035344,43.987849779086886 537,36 C 602.6807805596466,28.012150220913114 688.232142857143,24.660714285714292 751,32 C 813.767857142857,39.33928571428571 853.7522091310749,57.36929307805596 898,62 C 942.2477908689251,66.63070692194404 990.7590206185569,57.86211340206186 1055,56 C 1119.2409793814431,54.13788659793814 1199.211708394698,59.18225331369662 1266,61 C 1332.788291605302,62.81774668630338 1386.394145802651,61.40887334315169 1440,60 L 1440,400 L 0,400 Z\" stroke=\"none\" stroke-width=\"0\" fill=\"url(#gradient)\" fill-opacity=\"0.265\" class=\"transition-all duration-300 ease-in-out delay-150 path-0\"></path><style>\n .path-1{\n animation:pathAnim-1 4s;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n }\n @keyframes pathAnim-1{\n 0%{\n d: path(\"M 0,400 L 0,140 C 44.29988954344624,122.8668998527246 88.59977908689248,105.7337997054492 153,113 C 217.40022091310752,120.2662002945508 301.9007731958763,151.93170103092783 368,153 C 434.0992268041237,154.06829896907217 481.79712812960247,124.53939617083947 534,127 C 586.2028718703975,129.46060382916053 642.9107142857142,163.91071428571428 701,160 C 759.0892857142858,156.08928571428572 818.5600147275406,113.81774668630338 881,115 C 943.4399852724594,116.18225331369662 1008.8492268041236,160.81829896907217 1068,166 C 1127.1507731958764,171.18170103092783 1180.0430780559648,136.90905743740797 1241,126 C 1301.9569219440352,115.09094256259205 1370.9784609720177,127.54547128129602 1440,140 L 1440,400 L 0,400 Z\");\n }\n 25%{\n d: path(\"M 0,400 L 0,140 C 44.95968335787923,123.41991899852724 89.91936671575846,106.83983799705449 146,111 C 202.08063328424154,115.16016200294551 269.2822164948454,140.06056701030928 345,154 C 420.7177835051546,167.93943298969072 504.95176730486,170.9178939617084 562,167 C 619.04823269514,163.0821060382916 648.9107142857143,152.26785714285714 705,145 C 761.0892857142857,137.73214285714286 843.4053755522827,134.01067746686306 906,142 C 968.5946244477173,149.98932253313694 1011.4677835051546,169.68943298969072 1061,167 C 1110.5322164948454,164.31056701030928 1166.7234904270988,139.23159057437408 1231,131 C 1295.2765095729012,122.76840942562592 1367.6382547864505,131.38420471281296 1440,140 L 1440,400 L 0,400 Z\");\n }\n 50%{\n d: path(\"M 0,400 L 0,140 C 61.1502209131075,134.90150957290132 122.300441826215,129.80301914580264 175,123 C 227.699558173785,116.19698085419735 271.9484536082475,107.68943298969072 342,112 C 412.0515463917525,116.31056701030928 507.9057437407952,133.43924889543447 571,136 C 634.0942562592048,138.56075110456553 664.4285714285716,126.5535714285714 719,124 C 773.5714285714284,121.4464285714286 852.3799705449188,128.34646539027986 916,134 C 979.6200294550812,139.65353460972014 1028.0515463917527,144.06056701030928 1088,142 C 1147.9484536082473,139.93943298969072 1219.4138438880707,131.41126656848306 1280,130 C 1340.5861561119293,128.58873343151694 1390.2930780559645,134.29436671575849 1440,140 L 1440,400 L 0,400 Z\");\n }\n 75%{\n d: path(\"M 0,400 L 0,140 C 55.14764359351989,147.99815905743742 110.29528718703978,155.9963181148748 175,159 C 239.70471281296022,162.0036818851252 313.96649484536084,160.01288659793815 372,153 C 430.03350515463916,145.98711340206185 471.83873343151686,133.9521354933726 535,133 C 598.1612665684831,132.0478645066274 682.6785714285713,142.17857142857144 737,137 C 791.3214285714287,131.82142857142856 815.4469808541976,111.33357879234168 869,116 C 922.5530191458024,120.66642120765832 1005.533505154639,150.48711340206185 1076,154 C 1146.466494845361,157.51288659793815 1204.4189985272462,134.71796759941088 1263,128 C 1321.5810014727538,121.2820324005891 1380.790500736377,130.64101620029456 1440,140 L 1440,400 L 0,400 Z\");\n }\n 100%{\n d: path(\"M 0,400 L 0,140 C 44.29988954344624,122.8668998527246 88.59977908689248,105.7337997054492 153,113 C 217.40022091310752,120.2662002945508 301.9007731958763,151.93170103092783 368,153 C 434.0992268041237,154.06829896907217 481.79712812960247,124.53939617083947 534,127 C 586.2028718703975,129.46060382916053 642.9107142857142,163.91071428571428 701,160 C 759.0892857142858,156.08928571428572 818.5600147275406,113.81774668630338 881,115 C 943.4399852724594,116.18225331369662 1008.8492268041236,160.81829896907217 1068,166 C 1127.1507731958764,171.18170103092783 1180.0430780559648,136.90905743740797 1241,126 C 1301.9569219440352,115.09094256259205 1370.9784609720177,127.54547128129602 1440,140 L 1440,400 L 0,400 Z\");\n }\n }</style><defs><linearGradient id=\"gradient\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\"><stop offset=\"5%\" stop-color=\"#0693e3\"></stop><stop offset=\"95%\" stop-color=\"#8ED1FC\"></stop></linearGradient></defs><path d=\"M 0,400 L 0,140 C 44.29988954344624,122.8668998527246 88.59977908689248,105.7337997054492 153,113 C 217.40022091310752,120.2662002945508 301.9007731958763,151.93170103092783 368,153 C 434.0992268041237,154.06829896907217 481.79712812960247,124.53939617083947 534,127 C 586.2028718703975,129.46060382916053 642.9107142857142,163.91071428571428 701,160 C 759.0892857142858,156.08928571428572 818.5600147275406,113.81774668630338 881,115 C 943.4399852724594,116.18225331369662 1008.8492268041236,160.81829896907217 1068,166 C 1127.1507731958764,171.18170103092783 1180.0430780559648,136.90905743740797 1241,126 C 1301.9569219440352,115.09094256259205 1370.9784609720177,127.54547128129602 1440,140 L 1440,400 L 0,400 Z\" stroke=\"none\" stroke-width=\"0\" fill=\"url(#gradient)\" fill-opacity=\"0.4\" class=\"transition-all duration-300 ease-in-out delay-150 path-1\"></path><style>\n .path-2{\n animation:pathAnim-2 4s;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n }\n @keyframes pathAnim-2{\n 0%{\n d: path(\"M 0,400 L 0,220 C 72.70416053019147,222.98030191458025 145.40832106038295,225.96060382916053 207,231 C 268.59167893961705,236.03939617083947 319.0708762886598,243.13788659793815 364,244 C 408.9291237113402,244.86211340206185 448.30817378497795,239.48784977908693 507,239 C 565.691826215022,238.51215022091307 643.6964285714286,242.91071428571425 707,238 C 770.3035714285714,233.08928571428575 818.9061119293077,218.869293078056 872,209 C 925.0938880706923,199.130706921944 982.6791237113403,193.61211340206185 1049,191 C 1115.3208762886597,188.38788659793815 1190.3773932253314,188.6822533136966 1257,194 C 1323.6226067746686,199.3177466863034 1381.8113033873342,209.6588733431517 1440,220 L 1440,400 L 0,400 Z\");\n }\n 25%{\n d: path(\"M 0,400 L 0,220 C 65.79270986745213,211.94164212076583 131.58541973490426,203.88328424153164 191,206 C 250.41458026509574,208.11671575846836 303.451030927835,220.40850515463922 363,220 C 422.548969072165,219.59149484536078 488.6104565537555,206.4826951399116 557,205 C 625.3895434462445,203.5173048600884 696.1071428571429,213.66071428571428 751,216 C 805.8928571428571,218.33928571428572 844.9609720176732,212.87444771723125 907,205 C 969.0390279823268,197.12555228276875 1054.048969072165,186.8414948453608 1108,198 C 1161.951030927835,209.1585051546392 1184.8431516936673,241.7595729013255 1235,249 C 1285.1568483063327,256.2404270986745 1362.5784241531665,238.12021354933725 1440,220 L 1440,400 L 0,400 Z\");\n }\n 50%{\n d: path(\"M 0,400 L 0,220 C 71.7255154639175,216.840206185567 143.451030927835,213.68041237113403 211,215 C 278.548969072165,216.31958762886597 341.92139175257734,222.11855670103088 391,218 C 440.07860824742266,213.88144329896912 474.8634020618556,199.84536082474227 533,206 C 591.1365979381444,212.15463917525773 672.625,238.5 726,243 C 779.375,247.5 804.6365979381443,230.15463917525773 870,219 C 935.3634020618557,207.84536082474227 1040.8286082474226,202.8814432989691 1105,203 C 1169.1713917525774,203.1185567010309 1192.048969072165,208.319587628866 1241,212 C 1289.951030927835,215.680412371134 1364.9755154639174,217.840206185567 1440,220 L 1440,400 L 0,400 Z\");\n }\n 75%{\n d: path(\"M 0,400 L 0,220 C 65.31940353460973,231.92765095729013 130.63880706921947,243.85530191458025 195,244 C 259.36119293078053,244.14469808541975 322.7641752577319,232.5064432989691 381,221 C 439.2358247422681,209.4935567010309 492.3044918998528,198.11892488954345 547,198 C 601.6955081001472,197.88107511045655 658.0178571428572,209.01785714285714 707,209 C 755.9821428571428,208.98214285714286 797.6240795287188,197.809646539028 867,201 C 936.3759204712812,204.190353460972 1033.4858247422678,221.7435567010309 1103,221 C 1172.5141752577322,220.2564432989691 1214.432621502209,201.21612665684833 1266,198 C 1317.567378497791,194.78387334315167 1378.7836892488954,207.39193667157582 1440,220 L 1440,400 L 0,400 Z\");\n }\n 100%{\n d: path(\"M 0,400 L 0,220 C 72.70416053019147,222.98030191458025 145.40832106038295,225.96060382916053 207,231 C 268.59167893961705,236.03939617083947 319.0708762886598,243.13788659793815 364,244 C 408.9291237113402,244.86211340206185 448.30817378497795,239.48784977908693 507,239 C 565.691826215022,238.51215022091307 643.6964285714286,242.91071428571425 707,238 C 770.3035714285714,233.08928571428575 818.9061119293077,218.869293078056 872,209 C 925.0938880706923,199.130706921944 982.6791237113403,193.61211340206185 1049,191 C 1115.3208762886597,188.38788659793815 1190.3773932253314,188.6822533136966 1257,194 C 1323.6226067746686,199.3177466863034 1381.8113033873342,209.6588733431517 1440,220 L 1440,400 L 0,400 Z\");\n }\n }</style><defs><linearGradient id=\"gradient\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\"><stop offset=\"5%\" stop-color=\"#0693e3\"></stop><stop offset=\"95%\" stop-color=\"#8ED1FC\"></stop></linearGradient></defs><path d=\"M 0,400 L 0,220 C 72.70416053019147,222.98030191458025 145.40832106038295,225.96060382916053 207,231 C 268.59167893961705,236.03939617083947 319.0708762886598,243.13788659793815 364,244 C 408.9291237113402,244.86211340206185 448.30817378497795,239.48784977908693 507,239 C 565.691826215022,238.51215022091307 643.6964285714286,242.91071428571425 707,238 C 770.3035714285714,233.08928571428575 818.9061119293077,218.869293078056 872,209 C 925.0938880706923,199.130706921944 982.6791237113403,193.61211340206185 1049,191 C 1115.3208762886597,188.38788659793815 1190.3773932253314,188.6822533136966 1257,194 C 1323.6226067746686,199.3177466863034 1381.8113033873342,209.6588733431517 1440,220 L 1440,400 L 0,400 Z\" stroke=\"none\" stroke-width=\"0\" fill=\"url(#gradient)\" fill-opacity=\"0.53\" class=\"transition-all duration-300 ease-in-out delay-150 path-2\"></path><style>\n .path-3{\n animation:pathAnim-3 4s;\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n }\n @keyframes pathAnim-3{\n 0%{\n d: path(\"M 0,400 L 0,300 C 48.40813696612666,306.5460235640648 96.81627393225332,313.0920471281296 166,316 C 235.18372606774668,318.9079528718704 325.1430412371134,318.1778350515464 385,306 C 444.8569587628866,293.8221649484536 474.611561119293,270.1966126656849 533,273 C 591.388438880707,275.8033873343151 678.4107142857142,305.0357142857143 740,306 C 801.5892857142858,306.9642857142857 837.74558173785,279.66053019145807 893,273 C 948.25441826215,266.33946980854193 1022.6069587628863,280.3221649484536 1085,281 C 1147.3930412371137,281.6778350515464 1197.826583210604,269.0508100147276 1255,270 C 1312.173416789396,270.9491899852724 1376.086708394698,285.4745949926362 1440,300 L 1440,400 L 0,400 Z\");\n }\n 25%{\n d: path(\"M 0,400 L 0,300 C 42.53424153166419,313.16402798232696 85.06848306332839,326.32805596465397 151,320 C 216.9315169366716,313.67194403534603 306.26030927835063,287.8518041237113 371,286 C 435.73969072164937,284.1481958762887 475.89027982326945,306.26472754050076 524,315 C 572.1097201767305,323.73527245949924 628.1785714285714,319.08928571428567 689,322 C 749.8214285714286,324.91071428571433 815.3954344624448,335.37812960235647 876,327 C 936.6045655375552,318.62187039764353 992.2396907216496,291.39819587628864 1051,287 C 1109.7603092783504,282.60180412371136 1171.6458026509574,301.029086892489 1237,307 C 1302.3541973490426,312.970913107511 1371.1770986745214,306.4854565537555 1440,300 L 1440,400 L 0,400 Z\");\n }\n 50%{\n d: path(\"M 0,400 L 0,300 C 71.48011782032401,293.10217231222384 142.96023564064802,286.20434462444774 206,280 C 269.039764359352,273.79565537555226 323.6391752577319,268.28479381443293 372,272 C 420.3608247422681,275.71520618556707 462.4830633284241,288.6564801178203 524,304 C 585.5169366715759,319.3435198821797 666.4285714285714,337.0892857142858 735,328 C 803.5714285714286,318.9107142857142 859.8026509572901,282.9863770250368 905,278 C 950.1973490427099,273.0136229749632 984.360824742268,298.965206185567 1052,301 C 1119.639175257732,303.034793814433 1220.754050073638,281.1527982326951 1291,277 C 1361.245949926362,272.8472017673049 1400.6229749631812,286.42360088365245 1440,300 L 1440,400 L 0,400 Z\");\n }\n 75%{\n d: path(\"M 0,400 L 0,300 C 70.41623711340208,306.8939617083947 140.83247422680415,313.7879234167894 197,314 C 253.16752577319585,314.2120765832106 295.0863402061855,307.7422680412371 357,306 C 418.9136597938145,304.2577319587629 500.82216494845363,307.2430044182622 561,306 C 621.1778350515464,304.7569955817378 659.6249999999999,299.28571428571433 719,303 C 778.3750000000001,306.71428571428567 858.6778350515466,319.6141384388807 930,323 C 1001.3221649484534,326.3858615611193 1063.6636597938143,320.25773195876286 1113,320 C 1162.3363402061857,319.74226804123714 1198.667525773196,325.35493372606777 1251,323 C 1303.332474226804,320.64506627393223 1371.666237113402,310.3225331369661 1440,300 L 1440,400 L 0,400 Z\");\n }\n 100%{\n d: path(\"M 0,400 L 0,300 C 48.40813696612666,306.5460235640648 96.81627393225332,313.0920471281296 166,316 C 235.18372606774668,318.9079528718704 325.1430412371134,318.1778350515464 385,306 C 444.8569587628866,293.8221649484536 474.611561119293,270.1966126656849 533,273 C 591.388438880707,275.8033873343151 678.4107142857142,305.0357142857143 740,306 C 801.5892857142858,306.9642857142857 837.74558173785,279.66053019145807 893,273 C 948.25441826215,266.33946980854193 1022.6069587628863,280.3221649484536 1085,281 C 1147.3930412371137,281.6778350515464 1197.826583210604,269.0508100147276 1255,270 C 1312.173416789396,270.9491899852724 1376.086708394698,285.4745949926362 1440,300 L 1440,400 L 0,400 Z\");\n }\n }</style><defs><linearGradient id=\"gradient\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\"><stop offset=\"5%\" stop-color=\"#0693e3\"></stop><stop offset=\"95%\" stop-color=\"#8ED1FC\"></stop></linearGradient></defs><path d=\"M 0,400 L 0,300 C 48.40813696612666,306.5460235640648 96.81627393225332,313.0920471281296 166,316 C 235.18372606774668,318.9079528718704 325.1430412371134,318.1778350515464 385,306 C 444.8569587628866,293.8221649484536 474.611561119293,270.1966126656849 533,273 C 591.388438880707,275.8033873343151 678.4107142857142,305.0357142857143 740,306 C 801.5892857142858,306.9642857142857 837.74558173785,279.66053019145807 893,273 C 948.25441826215,266.33946980854193 1022.6069587628863,280.3221649484536 1085,281 C 1147.3930412371137,281.6778350515464 1197.826583210604,269.0508100147276 1255,270 C 1312.173416789396,270.9491899852724 1376.086708394698,285.4745949926362 1440,300 L 1440,400 L 0,400 Z\" stroke=\"none\" stroke-width=\"0\" fill=\"url(#gradient)\" fill-opacity=\"1\" class=\"transition-all duration-300 ease-in-out delay-150 path-3\"></path></svg>\n </div>\n\n <script>\n const REFRESH_INTERVAL = 28;\n let countdownTimer = null;\n let retryTimer = null;\n let secondsLeft = REFRESH_INTERVAL;\n\n async function fetchQR() {\n clearTimers();\n showSpinner();\n hideError();\n\n try {\n const res = await fetch('/qr');\n const data = await res.json();\n\n if (!res.ok || data.error) {\n showError(data.error || 'Error al obtener el c\u00F3digo QR');\n retryTimer = setTimeout(fetchQR, 5000);\n return;\n }\n\n // Show QR image \u2014 prefer base64 (pre-rendered), fallback to raw code\n if (data.base64) {\n showQR(data.base64);\n } else if (data.code) {\n // Use Google Charts API to render QR from raw code\n const encoded = encodeURIComponent(data.code);\n showQR('https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=' + encoded);\n } else {\n showError('No se recibi\u00F3 imagen del c\u00F3digo QR');\n retryTimer = setTimeout(fetchQR, 5000);\n return;\n }\n\n if (data.pairingCode) {\n document.getElementById('pairing-code').textContent = data.pairingCode;\n document.getElementById('pairing-section').classList.remove('hidden');\n document.getElementById('pairing-section').classList.add('flex');\n }\n\n startCountdown();\n } catch (err) {\n showError('Sin conexi\u00F3n con el servidor. Reintentando...');\n retryTimer = setTimeout(fetchQR, 5000);\n }\n }\n\n function showQR(src) {\n const img = document.getElementById('qr-img');\n document.getElementById('qr-spinner').classList.add('hidden');\n img.src = src;\n img.classList.remove('hidden');\n }\n\n function showSpinner() {\n document.getElementById('qr-spinner').classList.remove('hidden');\n document.getElementById('qr-img').classList.add('hidden');\n document.getElementById('qr-img').src = '';\n document.getElementById('countdown-wrapper').classList.add('hidden');\n }\n\n function showError(msg) {\n const el = document.getElementById('error-section');\n el.textContent = msg;\n el.classList.remove('hidden');\n }\n\n function hideError() {\n document.getElementById('error-section').classList.add('hidden');\n }\n\n function startCountdown() {\n secondsLeft = REFRESH_INTERVAL;\n document.getElementById('countdown-wrapper').classList.remove('hidden');\n updateCountdownUI();\n countdownTimer = setInterval(() => {\n secondsLeft--;\n updateCountdownUI();\n if (secondsLeft <= 0) {\n clearTimers();\n fetchQR();\n }\n }, 1000);\n }\n\n function updateCountdownUI() {\n document.getElementById('countdown-text').textContent = secondsLeft + 's';\n const pct = (secondsLeft / REFRESH_INTERVAL) * 100;\n document.getElementById('countdown-bar').style.width = pct + '%';\n }\n\n function clearTimers() {\n if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }\n if (retryTimer) { clearTimeout(retryTimer); retryTimer = null; }\n }\n\n fetchQR();\n </script>\n\n</body>\n</html>\n";
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.htmlConnect = void 0;
4
+ const logo_1 = require("../../constants/svg/logo");
5
+ const wave_1 = require("../../constants/svg/wave");
6
+ exports.htmlConnect = `
7
+ <!DOCTYPE html>
8
+ <html lang="es">
9
+ <head>
10
+ <meta charset="UTF-8" />
11
+ <title>Conectar - Sendwave Bot</title>
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
13
+ <script src="https://cdn.tailwindcss.com"></script>
14
+ </head>
15
+ <body class="bg-white text-slate-800 flex items-center justify-center min-h-screen relative overflow-hidden">
16
+
17
+ <div class="text-center bg-white/20 backdrop-blur-sm p-8 rounded-2xl z-10 shadow-lg max-w-sm w-full flex flex-col items-center gap-4 relative z-10">
18
+
19
+ <h1 class="text-2xl font-bold text-cyan-700 flex items-center justify-center gap-2">
20
+ ${logo_1.logosvg} Conectar instancia
21
+ </h1>
22
+
23
+ <p class="text-slate-500 text-sm">Escanea el código QR con WhatsApp o usa el código de emparejamiento.</p>
24
+
25
+ <!-- QR image -->
26
+ <div id="qr-wrapper" class="flex items-center justify-center w-56 h-56 rounded-xl border-2 border-cyan-200 bg-white shadow-inner overflow-hidden">
27
+ <div id="qr-spinner" class="flex flex-col items-center gap-2 text-slate-400">
28
+ <svg class="animate-spin h-8 w-8 text-cyan-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
29
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
30
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
31
+ </svg>
32
+ <span class="text-xs">Cargando...</span>
33
+ </div>
34
+ <img id="qr-img" class="hidden w-full h-full object-contain" alt="QR Code" />
35
+ </div>
36
+
37
+ <!-- Pairing code -->
38
+ <div id="pairing-section" class="hidden flex-col items-center gap-1 w-full">
39
+ <p class="text-xs text-slate-400 uppercase tracking-widest font-semibold">Código de emparejamiento</p>
40
+ <div id="pairing-code" class="font-mono text-3xl font-bold tracking-widest text-cyan-700 bg-cyan-50 border border-cyan-200 rounded-xl px-6 py-3 select-all w-full text-center"></div>
41
+ </div>
42
+
43
+ <!-- Error -->
44
+ <div id="error-section" class="hidden text-red-500 text-sm bg-red-50 border border-red-200 rounded-lg px-4 py-2 w-full text-center"></div>
45
+
46
+ <!-- Countdown -->
47
+ <div id="countdown-wrapper" class="hidden w-full">
48
+ <div class="flex justify-between text-xs text-slate-400 mb-1">
49
+ <span>Actualización automática</span>
50
+ <span id="countdown-text">30s</span>
51
+ </div>
52
+ <div class="w-full bg-slate-100 rounded-full h-1.5">
53
+ <div id="countdown-bar" class="bg-cyan-500 h-1.5 rounded-full" style="width:100%;transition:width 1s linear"></div>
54
+ </div>
55
+ </div>
56
+
57
+ <button onclick="fetchQR()" class="text-xs text-cyan-600 hover:text-cyan-800 underline transition-colors">
58
+ Actualizar ahora
59
+ </button>
60
+
61
+ </div>
62
+
63
+ <div class="absolute bottom-0 left-0 w-full">
64
+ ${wave_1.waveSvg}
65
+ </div>
66
+
67
+ <script>
68
+ const REFRESH_INTERVAL = 28;
69
+ let countdownTimer = null;
70
+ let retryTimer = null;
71
+ let secondsLeft = REFRESH_INTERVAL;
72
+
73
+ async function fetchQR() {
74
+ clearTimers();
75
+ showSpinner();
76
+ hideError();
77
+
78
+ try {
79
+ const res = await fetch('/qr');
80
+ const data = await res.json();
81
+
82
+ if (!res.ok || data.error) {
83
+ showError(data.error || 'Error al obtener el código QR');
84
+ retryTimer = setTimeout(fetchQR, 5000);
85
+ return;
86
+ }
87
+
88
+ // Show QR image — prefer base64 (pre-rendered), fallback to raw code
89
+ if (data.base64) {
90
+ showQR(data.base64);
91
+ } else if (data.code) {
92
+ // Use Google Charts API to render QR from raw code
93
+ const encoded = encodeURIComponent(data.code);
94
+ showQR('https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=' + encoded);
95
+ } else {
96
+ showError('No se recibió imagen del código QR');
97
+ retryTimer = setTimeout(fetchQR, 5000);
98
+ return;
99
+ }
100
+
101
+ if (data.pairingCode) {
102
+ document.getElementById('pairing-code').textContent = data.pairingCode;
103
+ document.getElementById('pairing-section').classList.remove('hidden');
104
+ document.getElementById('pairing-section').classList.add('flex');
105
+ }
106
+
107
+ startCountdown();
108
+ } catch (err) {
109
+ showError('Sin conexión con el servidor. Reintentando...');
110
+ retryTimer = setTimeout(fetchQR, 5000);
111
+ }
112
+ }
113
+
114
+ function showQR(src) {
115
+ const img = document.getElementById('qr-img');
116
+ document.getElementById('qr-spinner').classList.add('hidden');
117
+ img.src = src;
118
+ img.classList.remove('hidden');
119
+ }
120
+
121
+ function showSpinner() {
122
+ document.getElementById('qr-spinner').classList.remove('hidden');
123
+ document.getElementById('qr-img').classList.add('hidden');
124
+ document.getElementById('qr-img').src = '';
125
+ document.getElementById('countdown-wrapper').classList.add('hidden');
126
+ }
127
+
128
+ function showError(msg) {
129
+ const el = document.getElementById('error-section');
130
+ el.textContent = msg;
131
+ el.classList.remove('hidden');
132
+ }
133
+
134
+ function hideError() {
135
+ document.getElementById('error-section').classList.add('hidden');
136
+ }
137
+
138
+ function startCountdown() {
139
+ secondsLeft = REFRESH_INTERVAL;
140
+ document.getElementById('countdown-wrapper').classList.remove('hidden');
141
+ updateCountdownUI();
142
+ countdownTimer = setInterval(() => {
143
+ secondsLeft--;
144
+ updateCountdownUI();
145
+ if (secondsLeft <= 0) {
146
+ clearTimers();
147
+ fetchQR();
148
+ }
149
+ }, 1000);
150
+ }
151
+
152
+ function updateCountdownUI() {
153
+ document.getElementById('countdown-text').textContent = secondsLeft + 's';
154
+ const pct = (secondsLeft / REFRESH_INTERVAL) * 100;
155
+ document.getElementById('countdown-bar').style.width = pct + '%';
156
+ }
157
+
158
+ function clearTimers() {
159
+ if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }
160
+ if (retryTimer) { clearTimeout(retryTimer); retryTimer = null; }
161
+ }
162
+
163
+ fetchQR();
164
+ </script>
165
+
166
+ </body>
167
+ </html>
168
+ `;
@@ -1,5 +1,5 @@
1
1
  import { SendOptions } from "@builderbot/bot/dist/types";
2
- import { ReadMessage, SendButton, SendList, SendLocation, SendMedia, SendMessage, SendPresence, SendReaction } from "../interface/types";
2
+ import { ReadMessage, SendButton, SendList, SendLocation, SendMedia, SendMessage, SendPoll, SendPresence, SendReaction } from "../interface/types";
3
3
  export interface ProviderInterface {
4
4
  sendMessage?: (number: string, message: string, options?: SendOptions) => Promise<any>;
5
5
  sendText: (data: SendMessage) => Promise<any>;
@@ -13,6 +13,7 @@ export interface ProviderInterface {
13
13
  sendList: (data: SendList) => Promise<any>;
14
14
  sendPresence: (data: SendPresence) => Promise<any>;
15
15
  sendButton: (data: SendButton) => Promise<any>;
16
+ sendPoll: (data: SendPoll) => Promise<any>;
16
17
  sendLocation: (data: SendLocation) => Promise<any>;
17
18
  sendReaction: (data: SendReaction) => Promise<any>;
18
19
  readMessages: (data: ReadMessage) => Promise<any>;
@@ -80,6 +80,9 @@ export interface SendMessage {
80
80
  from: string;
81
81
  delay?: number;
82
82
  text?: string;
83
+ quoted?: any;
84
+ everyOne?: boolean;
85
+ mentioned?: string[];
83
86
  }
84
87
  export interface SendMedia extends SendMessage {
85
88
  mediaType?: "image" | "video" | "document" | "audio";
@@ -90,15 +93,54 @@ export interface SendPresence extends SendMessage {
90
93
  delay?: number;
91
94
  presence?: "composing" | "recording";
92
95
  }
96
+ export type TypeButton = 'reply' | 'copy' | 'url' | 'call' | 'pix';
97
+ export type BaseButton<T> = T & {
98
+ type: TypeButton;
99
+ id?: string;
100
+ displayText: string;
101
+ };
102
+ export type Button = BaseButton<{
103
+ type: 'reply';
104
+ displayText: string;
105
+ }> | BaseButton<{
106
+ type: 'url';
107
+ displayText: string;
108
+ url: string;
109
+ }> | BaseButton<{
110
+ type: 'call';
111
+ displayText: string;
112
+ phoneNumber: string;
113
+ }> | BaseButton<{
114
+ type: 'copy';
115
+ displayText: string;
116
+ copyText: string;
117
+ copyCode?: string;
118
+ }> | BaseButton<{
119
+ type: 'pix';
120
+ displayText: string;
121
+ keyType: KeyType;
122
+ key: string;
123
+ currency?: string;
124
+ name?: string;
125
+ }>;
93
126
  export interface SendButton extends SendMessage {
94
127
  title: string;
95
- body: string;
96
- description: string;
97
- footer: string;
98
- buttons: {
99
- type: "reply" | "url" | "call";
100
- text: string;
101
- }[];
128
+ thumbnailUrl?: string;
129
+ body?: string;
130
+ description?: string;
131
+ footer?: string;
132
+ buttons: (string | {
133
+ body: string;
134
+ } | {
135
+ reply: string;
136
+ } | Button)[];
137
+ }
138
+ export interface SendPoll extends SendMessage {
139
+ poll: {
140
+ name: string;
141
+ values: string[];
142
+ selectableCount?: number;
143
+ };
102
144
  }
103
145
  export interface SendLocation extends SendMessage {
104
146
  name: string;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamastudio/sendwave-provider",
3
- "version": "0.0.24",
3
+ "version": "1.0.1",
4
4
  "description": "Librería para interactuar con Sendwave usando configuración dinámica.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -4,6 +4,7 @@ exports.SendWaveCore = void 0;
4
4
  const node_events_1 = require("node:events");
5
5
  const parserMsg_1 = require("../utils/parserMsg");
6
6
  const home_1 = require("../constants/html/home");
7
+ const connect_1 = require("../constants/html/connect");
7
8
  const error_1 = require("../constants/html/error");
8
9
  const sender_1 = require("./sender");
9
10
  class SendWaveCore extends node_events_1.EventEmitter {
@@ -14,10 +15,8 @@ class SendWaveCore extends node_events_1.EventEmitter {
14
15
  res.end(home_1.htmlHome);
15
16
  };
16
17
  this.indexConnect = (_, res) => {
17
- const url = `https://app.sendwaves.cloud/instance/${this.globalVendorArgs.name}/settings`;
18
- res.statusCode = 302;
19
- res.setHeader("Location", url);
20
- res.end();
18
+ res.setHeader("Content-Type", "text/html");
19
+ res.end(connect_1.htmlConnect);
21
20
  };
22
21
  this.indexError = (_, res) => {
23
22
  res.setHeader("Content-Type", "text/html");
@@ -2,7 +2,7 @@ import { ProviderClass } from "@builderbot/bot";
2
2
  import Queue from "queue-promise";
3
3
  import { SendWaveCore } from "./core";
4
4
  import { BotContext, BotCtxMiddlewareOptions } from "@builderbot/bot/dist/types";
5
- import { GlobalVendorArgs, ReadMessage, SendButton, SendList, SendLocation, SendMedia, SendMessage, SendPresence, SendReaction } from "../core/interface/types";
5
+ import { GlobalVendorArgs, ReadMessage, SendButton, SendList, SendLocation, SendMedia, SendMessage, SendPoll, SendPresence, SendReaction } from "../core/interface/types";
6
6
  import { SenderMessage } from "./sender";
7
7
  import { ParsedMessage } from "../core/interface";
8
8
  export declare class SendWaveProvider extends ProviderClass<SendWaveCore> {
@@ -55,6 +55,7 @@ export declare class SendWaveProvider extends ProviderClass<SendWaveCore> {
55
55
  sendPresence(data: SendPresence): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
56
56
  sendVoice(data: SendMedia): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
57
57
  sendButton(data: SendButton): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
58
+ sendPoll(data: SendPoll): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
58
59
  sendReaction(data: SendReaction): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
59
60
  sendLocation(data: SendLocation): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
60
61
  sendMedia(data: SendMedia): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
@@ -117,6 +117,19 @@ class SendWaveProvider extends bot_1.ProviderClass {
117
117
  else {
118
118
  }
119
119
  return this.vendor.indexError(req, res);
120
+ })
121
+ .get("/qr", async (_, res) => {
122
+ var _a, _b;
123
+ try {
124
+ const response = await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.get(`/instance/connect/${this.globalVendorArgs.name}`));
125
+ res.setHeader("Content-Type", "application/json");
126
+ res.end(JSON.stringify((_b = response === null || response === void 0 ? void 0 : response.data) !== null && _b !== void 0 ? _b : {}));
127
+ }
128
+ catch (err) {
129
+ res.statusCode = 502;
130
+ res.setHeader("Content-Type", "application/json");
131
+ res.end(JSON.stringify({ error: "No se pudo obtener el código de conexión" }));
132
+ }
120
133
  })
121
134
  .post("/webhook", this.vendor.incomingMsg);
122
135
  };
@@ -182,6 +195,7 @@ class SendWaveProvider extends bot_1.ProviderClass {
182
195
  sendVoice: this.sendVoice,
183
196
  sendPresence: this.sendPresence,
184
197
  sendButton: this.sendButton,
198
+ sendPoll: this.sendPoll,
185
199
  readMessages: this.readMessages,
186
200
  provider: this,
187
201
  blacklist: opts === null || opts === void 0 ? void 0 : opts.blacklist,
@@ -482,6 +496,9 @@ class SendWaveProvider extends bot_1.ProviderClass {
482
496
  sendButton(data) {
483
497
  return this.sender.sendButton(data);
484
498
  }
499
+ sendPoll(data) {
500
+ return this.sender.sendPoll(data);
501
+ }
485
502
  sendReaction(data) {
486
503
  return this.sender.sendReaction(data);
487
504
  }
@@ -1,4 +1,4 @@
1
- import { GlobalVendorArgs, ProviderInterface, SendButton, SendList, SendMedia, SendMessage, SendPresence, SendLocation, SendReaction, ReadMessage } from "../core/interface";
1
+ import { GlobalVendorArgs, ProviderInterface, SendButton, SendList, SendMedia, SendMessage, SendPresence, SendPoll, SendLocation, SendReaction, ReadMessage } from "../core/interface";
2
2
  export declare class SenderMessage implements ProviderInterface {
3
3
  private sendwaveApi?;
4
4
  private globalVendorArgs?;
@@ -15,6 +15,7 @@ export declare class SenderMessage implements ProviderInterface {
15
15
  sendVideo(data: SendMedia): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
16
16
  sendVoice(data: SendMedia): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
17
17
  sendButton(data: SendButton): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
18
+ sendPoll(data: SendPoll): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
18
19
  sendLocation(data: SendLocation): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
19
20
  sendReaction(data: SendReaction): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
20
21
  readMessages(data: ReadMessage): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
@@ -89,7 +89,7 @@ class SenderMessage {
89
89
  }
90
90
  }
91
91
  async sendList(data) {
92
- var _a, _b, _c, _d, _e;
92
+ var _a, _b, _c, _d, _e, _f;
93
93
  try {
94
94
  if (!(data === null || data === void 0 ? void 0 : data.from))
95
95
  throw new Error("sendList: falta 'from'");
@@ -126,11 +126,15 @@ class SenderMessage {
126
126
  footerText,
127
127
  buttonText: button,
128
128
  sections,
129
+ delay: data.delay || 2000,
130
+ quoted: data.quoted,
131
+ everyOne: data.everyOne || false,
132
+ mentioned: ((_c = data.mentioned) === null || _c === void 0 ? void 0 : _c.length) ? data.mentioned : [data.from],
129
133
  ...this.globalVendorArgs,
130
134
  }));
131
135
  }
132
136
  catch (err) {
133
- console.error("[sendList Error]", ((_e = (_d = (_c = err.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.response) === null || _e === void 0 ? void 0 : _e.message) || err.message);
137
+ console.error("[sendList Error]", ((_f = (_e = (_d = err.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.message) || err.message);
134
138
  }
135
139
  }
136
140
  async sendMedia(data) {
@@ -280,12 +284,6 @@ class SenderMessage {
280
284
  detectorMedia_1.detectorMedia.updateLimits(this.globalVendorArgs.payloadLimits.media);
281
285
  }
282
286
  const { media } = await detectorMedia_1.detectorMedia.processMedia(data.url);
283
- // try {
284
- // await this.sendPresence({
285
- // from: data.from,
286
- // presence: "recording",
287
- // });
288
- // } catch (error) {}
289
287
  return await ((_c = this.sendwaveApi) === null || _c === void 0 ? void 0 : _c.post(`/message/sendWhatsAppAudio/${(_d = this.globalVendorArgs) === null || _d === void 0 ? void 0 : _d.name}`, {
290
288
  number: data.from,
291
289
  audio: media,
@@ -315,31 +313,85 @@ class SenderMessage {
315
313
  }
316
314
  }
317
315
  async sendButton(data) {
318
- var _a, _b, _c, _d, _e;
316
+ var _a, _b, _c, _d, _e, _f;
319
317
  try {
320
318
  return await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.post(`/message/sendButtons/${(_b = this.globalVendorArgs) === null || _b === void 0 ? void 0 : _b.name}`, {
321
319
  number: data.from,
320
+ thumbnailUrl: data.thumbnailUrl,
322
321
  title: data.title,
323
322
  body: data.body,
324
323
  description: data.description,
325
324
  footer: data.footer,
326
- buttons: data.buttons.map((button, index) => ({
327
- type: button.type,
328
- title: button.text,
329
- displayText: button.text,
330
- id: `${index + 1}`,
331
- })),
325
+ buttons: data.buttons.map((button, index) => {
326
+ let type = "reply";
327
+ let displayText = "";
328
+ let rest = {};
329
+ if (typeof button === "string") {
330
+ displayText = button;
331
+ }
332
+ else if (button.body) {
333
+ displayText = button.body;
334
+ type = button.type || "reply";
335
+ rest = button;
336
+ }
337
+ else if (button.reply) {
338
+ displayText = button.reply;
339
+ type = "reply";
340
+ rest = button;
341
+ }
342
+ else {
343
+ displayText = button.displayText || button.text || "";
344
+ type = button.type || "reply";
345
+ rest = button;
346
+ }
347
+ const base = {
348
+ type,
349
+ displayText,
350
+ id: button.id || `${index + 1}`,
351
+ ...rest
352
+ };
353
+ // Clean up unwanted properties
354
+ delete base.text;
355
+ delete base.body;
356
+ delete base.reply;
357
+ return base;
358
+ }),
359
+ delay: data.delay || 2000,
360
+ quoted: data.quoted,
361
+ everyOne: data.everyOne || false,
362
+ mentioned: ((_c = data.mentioned) === null || _c === void 0 ? void 0 : _c.length) ? data.mentioned : [data.from],
332
363
  ...this.globalVendorArgs,
333
364
  }));
334
365
  }
335
366
  catch (error) {
336
- const msg = ((_e = (_d = (_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.response) === null || _e === void 0 ? void 0 : _e.message) ||
367
+ const msg = ((_f = (_e = (_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.message) ||
337
368
  (error === null || error === void 0 ? void 0 : error.message) ||
338
369
  "Unknown error";
339
370
  console.error(`[sendButton Error] ${msg}`);
340
371
  throw new Error(msg);
341
372
  }
342
373
  }
374
+ async sendPoll(data) {
375
+ var _a, _b, _c, _d, _e, _f;
376
+ try {
377
+ return await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.post(`/message/sendPoll/${(_b = this.globalVendorArgs) === null || _b === void 0 ? void 0 : _b.name}`, {
378
+ number: data.from,
379
+ name: data.poll.name,
380
+ selectableCount: data.poll.selectableCount || 1,
381
+ values: data.poll.values,
382
+ delay: data.delay || 2000,
383
+ quoted: data.quoted,
384
+ everyOne: data.everyOne || false,
385
+ mentioned: ((_c = data.mentioned) === null || _c === void 0 ? void 0 : _c.length) ? data.mentioned : [data.from],
386
+ ...this.globalVendorArgs,
387
+ }));
388
+ }
389
+ catch (e) {
390
+ const msg = ((_f = (_e = (_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.message) || (e === null || e === void 0 ? void 0 : e.message) || "Unknown error";
391
+ console.error(`[sendPoll Error] ${msg}`);
392
+ throw new Error(msg);
393
+ }
394
+ }
343
395
  async sendLocation(data) {
344
396
  var _a, _b, _c, _d, _e;
345
397
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamastudio/sendwave-provider",
3
- "version": "0.0.24",
3
+ "version": "1.0.1",
4
4
  "description": "Librería para interactuar con Sendwave usando configuración dinámica.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",