@gamastudio/sendwave-provider 0.0.5 → 0.0.7-dev

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.
@@ -7,5 +7,7 @@ export interface ParsedMessage {
7
7
  type: string;
8
8
  media?: string;
9
9
  caption?: string;
10
- originalPayload: any;
10
+ originalPayload: {
11
+ key: any;
12
+ };
11
13
  }
@@ -10,6 +10,7 @@ export interface GlobalVendorArgs {
10
10
  url?: string;
11
11
  apiKey: string;
12
12
  port?: number;
13
+ host?: string;
13
14
  delay?: number;
14
15
  linkPreview?: boolean;
15
16
  message?: {
@@ -25,6 +26,17 @@ export interface GlobalVendorArgs {
25
26
  endTimeout?: number;
26
27
  warningMessage?: string;
27
28
  };
29
+ payloadLimits?: {
30
+ json?: string;
31
+ urlencoded?: string;
32
+ text?: string;
33
+ media?: {
34
+ image?: string;
35
+ video?: string;
36
+ audio?: string;
37
+ document?: string;
38
+ };
39
+ };
28
40
  }
29
41
  export interface Message {
30
42
  type: string;
@@ -93,7 +105,7 @@ export interface SendLocation extends SendMessage {
93
105
  latitude: number;
94
106
  longitude: number;
95
107
  }
96
- export interface SendReaction extends SendMessage {
108
+ export interface SendReaction {
97
109
  key: {
98
110
  remoteJid: string;
99
111
  fromMe: boolean;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamastudio/sendwave-provider",
3
- "version": "0.0.5",
3
+ "version": "0.0.7-dev",
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",
@@ -22,7 +22,7 @@
22
22
  "author": "Ameth Galarcio <amethgm@gmail.com>",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@builderbot/bot": "^1.3.1",
25
+ "@builderbot/bot": "^1.2.9",
26
26
  "@gamastudio/colorslog": "^0.1.6",
27
27
  "@types/mime-types": "^2.1.4",
28
28
  "axios": "^1.11.0",
@@ -54,8 +54,35 @@ class SendWaveCore extends node_events_1.EventEmitter {
54
54
  }
55
55
  catch (e) {
56
56
  console.error("[IncomingMsg Error]:", e);
57
+ // Handle PayloadTooLargeError specifically
58
+ if (e.name === 'PayloadTooLargeError' || e.type === 'entity.too.large') {
59
+ console.error(`[PayloadTooLarge] Request from ${req.ip || 'unknown'} exceeded size limit`);
60
+ res.statusCode = 413;
61
+ res.setHeader('Content-Type', 'application/json');
62
+ return res.end(JSON.stringify({
63
+ error: 'Payload too large',
64
+ message: 'El archivo o mensaje es demasiado grande',
65
+ code: 'PAYLOAD_TOO_LARGE'
66
+ }));
67
+ }
68
+ // Handle timeout errors
69
+ if (e.type === 'request.timeout') {
70
+ console.error(`[RequestTimeout] Request from ${req.ip || 'unknown'} timed out`);
71
+ res.statusCode = 408;
72
+ res.setHeader('Content-Type', 'application/json');
73
+ return res.end(JSON.stringify({
74
+ error: 'Request timeout',
75
+ message: 'La solicitud ha tardado demasiado',
76
+ code: 'REQUEST_TIMEOUT'
77
+ }));
78
+ }
57
79
  res.statusCode = 500;
58
- return res.end("Internal Server Error");
80
+ res.setHeader('Content-Type', 'application/json');
81
+ return res.end(JSON.stringify({
82
+ error: 'Internal Server Error',
83
+ message: 'Error interno del servidor',
84
+ code: 'INTERNAL_ERROR'
85
+ }));
59
86
  }
60
87
  };
61
88
  this.messageBuffers = {};
@@ -14,6 +14,7 @@ export declare class SendWaveProvider extends ProviderClass<SendWaveCore> {
14
14
  sender: SenderMessage;
15
15
  globalVendorArgs: GlobalVendorArgs;
16
16
  constructor(args: Partial<GlobalVendorArgs>);
17
+ private setupGlobalErrorHandlers;
17
18
  protected initVendor(): Promise<any>;
18
19
  protected beforeHttpServerInit: () => Promise<void>;
19
20
  protected getApiQrConnect: () => Promise<void>;
@@ -22,6 +23,7 @@ export declare class SendWaveProvider extends ProviderClass<SendWaveCore> {
22
23
  path: string;
23
24
  }): Promise<string>;
24
25
  createInstance(): void;
26
+ private parseSize;
25
27
  busEvents: () => ({
26
28
  event: string;
27
29
  func: (payload: BotContext) => void;
@@ -48,6 +50,7 @@ export declare class SendWaveProvider extends ProviderClass<SendWaveCore> {
48
50
  sendButton(...args: Parameters<SenderMessage["sendButton"]>): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
49
51
  sendReaction(...args: Parameters<SenderMessage["sendReaction"]>): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
50
52
  sendLocation(...args: Parameters<SenderMessage["sendLocation"]>): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
53
+ sendMedia(...args: Parameters<SenderMessage["sendMedia"]>): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
51
54
  /**
52
55
  * Reset user timeout for queue flow system
53
56
  */
@@ -27,6 +27,7 @@ class SendWaveProvider extends bot_1.ProviderClass {
27
27
  name: "bot",
28
28
  url: "https://sendwave-api.gamastudio.co",
29
29
  port: 3000,
30
+ host: "0.0.0.0", // Listen on all interfaces by default
30
31
  apiKey: "",
31
32
  delay: 0,
32
33
  linkPreview: true,
@@ -40,10 +41,38 @@ class SendWaveProvider extends bot_1.ProviderClass {
40
41
  endTimeout: 2 * 60 * 1000, // 2 minutes
41
42
  warningMessage: "⏳ Parece que has estado inactivo. ¿Sigues ahí?",
42
43
  },
44
+ payloadLimits: {
45
+ json: '50mb',
46
+ urlencoded: '50mb',
47
+ text: '10mb',
48
+ media: {
49
+ image: '10mb',
50
+ video: '100mb',
51
+ audio: '50mb',
52
+ document: '25mb',
53
+ },
54
+ },
43
55
  };
44
56
  this.beforeHttpServerInit = async () => {
57
+ // Configure body parser with large payload limits
58
+ const payloadLimits = this.globalVendorArgs.payloadLimits || {};
45
59
  this.server = this.server
46
- .use(body_parser_1.default.json())
60
+ .use(body_parser_1.default.json({
61
+ limit: payloadLimits.json || '50mb',
62
+ type: 'application/json'
63
+ }))
64
+ .use(body_parser_1.default.urlencoded({
65
+ limit: payloadLimits.urlencoded || '50mb',
66
+ extended: true
67
+ }))
68
+ .use(body_parser_1.default.text({
69
+ limit: payloadLimits.text || '10mb',
70
+ type: 'text/*'
71
+ }))
72
+ .use(body_parser_1.default.raw({
73
+ limit: payloadLimits.json || '50mb',
74
+ type: 'application/octet-stream'
75
+ }))
47
76
  .use((req, _, next) => {
48
77
  req["globalVendorArgs"] = this.globalVendorArgs;
49
78
  return next();
@@ -117,6 +146,11 @@ class SendWaveProvider extends bot_1.ProviderClass {
117
146
  ];
118
147
  this.initAll = (port, opts) => {
119
148
  this.globalVendorArgs.port = port;
149
+ // Override the server binding to listen on all interfaces
150
+ if (this.globalVendorArgs.host === "0.0.0.0") {
151
+ process.env.HOST = "0.0.0.0";
152
+ process.env.HOSTNAME = "0.0.0.0";
153
+ }
120
154
  const methods = {
121
155
  sendMessage: this.sendMessage,
122
156
  sendList: this.sendList,
@@ -144,12 +178,24 @@ class SendWaveProvider extends bot_1.ProviderClass {
144
178
  .then(() => {
145
179
  this.beforeHttpServerInit();
146
180
  this.start(methods, (routes) => {
181
+ // Log the actual server binding info
182
+ console.log(`🌐 Server binding: ${this.globalVendorArgs.host || 'localhost'}:${this.globalVendorArgs.port}`);
183
+ if (this.globalVendorArgs.host === "0.0.0.0") {
184
+ console.log(`🔗 External access: Server accessible from all network interfaces`);
185
+ console.log(`📡 Webhook URL for external services: http://<your-ip>:${this.globalVendorArgs.port}/webhook`);
186
+ }
147
187
  this.emit("notice", {
148
188
  title: "🛜 HTTP Server ON ",
149
189
  instructions: routes,
150
190
  });
151
191
  this.afterHttpServerInit();
152
192
  });
193
+ })
194
+ .catch((error) => {
195
+ console.error("[Provider Error]:", error);
196
+ if (error.type === 'entity.too.large') {
197
+ console.error("[Provider] PayloadTooLarge error caught at provider level");
198
+ }
153
199
  });
154
200
  return;
155
201
  };
@@ -174,6 +220,27 @@ class SendWaveProvider extends bot_1.ProviderClass {
174
220
  // Puedes emitir eventos a websockets aquí, notificar a frontends, etc.
175
221
  });
176
222
  this.sender = new sender_1.SenderMessage(this.globalVendorArgs);
223
+ // Add global error handlers for payload errors
224
+ this.setupGlobalErrorHandlers();
225
+ }
226
+ setupGlobalErrorHandlers() {
227
+ // Handle uncaught PayloadTooLarge errors
228
+ process.on('uncaughtException', (error) => {
229
+ if (error.name === 'PayloadTooLargeError' || error.type === 'entity.too.large') {
230
+ console.error('[Global] PayloadTooLarge error intercepted:', error.message);
231
+ // Don't exit process for this type of error
232
+ return;
233
+ }
234
+ // For other uncaught exceptions, log and continue
235
+ console.error('[Global] Uncaught exception:', error);
236
+ });
237
+ process.on('unhandledRejection', (reason) => {
238
+ if (reason && (reason.name === 'PayloadTooLargeError' || reason.type === 'entity.too.large')) {
239
+ console.error('[Global] PayloadTooLarge rejection intercepted:', reason.message);
240
+ return;
241
+ }
242
+ console.error('[Global] Unhandled rejection:', reason);
243
+ });
177
244
  }
178
245
  initVendor() {
179
246
  const vendor = new core_1.SendWaveCore(this.globalVendorArgs.port, this.queue, this.globalVendorArgs);
@@ -223,6 +290,8 @@ class SendWaveProvider extends bot_1.ProviderClass {
223
290
  this.globalVendorArgs.url.trim()
224
291
  ? this.globalVendorArgs.url.trim()
225
292
  : "https://sendwave-api.gamastudio.co";
293
+ const payloadLimits = this.globalVendorArgs.payloadLimits || {};
294
+ const maxContentLength = this.parseSize(payloadLimits.json || '50mb');
226
295
  this.sendwaveApi = axios_1.default.create({
227
296
  baseURL: url,
228
297
  httpsAgent: new https_1.Agent({ rejectUnauthorized: false }),
@@ -230,8 +299,46 @@ class SendWaveProvider extends bot_1.ProviderClass {
230
299
  "Content-Type": "application/json",
231
300
  apikey: this.globalVendorArgs.apiKey,
232
301
  },
302
+ // Large file support configuration
303
+ maxContentLength: maxContentLength,
304
+ maxBodyLength: maxContentLength,
305
+ timeout: 300000, // 5 minutes timeout for large files
306
+ });
307
+ // Add request interceptor for large file logging
308
+ this.sendwaveApi.interceptors.request.use((config) => {
309
+ const contentLength = config.headers['content-length'] ||
310
+ (config.data ? JSON.stringify(config.data).length : 0);
311
+ if (contentLength > 1024 * 1024) { // Log requests larger than 1MB
312
+ console.log(`[LargeRequest] Sending ${Math.round(contentLength / 1024 / 1024 * 100) / 100}MB to ${config.url}`);
313
+ }
314
+ return config;
315
+ }, (error) => Promise.reject(error));
316
+ // Add response interceptor for error handling
317
+ this.sendwaveApi.interceptors.response.use((response) => response, (error) => {
318
+ var _a;
319
+ if (error.code === 'ECONNABORTED') {
320
+ console.error('[RequestTimeout] Large file upload timed out');
321
+ }
322
+ else if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 413) {
323
+ console.error('[PayloadTooLarge] Server rejected large payload');
324
+ }
325
+ return Promise.reject(error);
233
326
  });
234
327
  }
328
+ parseSize(size) {
329
+ const units = {
330
+ 'b': 1,
331
+ 'kb': 1024,
332
+ 'mb': 1024 * 1024,
333
+ 'gb': 1024 * 1024 * 1024
334
+ };
335
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
336
+ if (!match)
337
+ return 50 * 1024 * 1024; // Default 50MB
338
+ const value = parseFloat(match[1]);
339
+ const unit = match[2] || 'b';
340
+ return Math.floor(value * units[unit]);
341
+ }
235
342
  async listenOnEvents(vendor) {
236
343
  const listEvents = this.busEvents();
237
344
  for (const { event, func } of listEvents) {
@@ -246,38 +353,65 @@ class SendWaveProvider extends bot_1.ProviderClass {
246
353
  });
247
354
  }
248
355
  async sendMessage(from, text, options) {
249
- options = { ...options["options"] };
250
- let mss = { from, text, options };
251
- if (options.media) {
252
- const { media, mediaType, fileName } = await detectorMedia_1.detectorMedia.processMedia(options.media);
253
- switch (mediaType) {
254
- case "image":
255
- return await this.sender.sendImage({
256
- ...mss,
257
- url: media,
258
- text: text,
259
- });
260
- case "video":
261
- return await this.sender.sendVideo({
262
- ...mss,
263
- url: media,
264
- text: text,
265
- });
266
- case "audio":
267
- return await this.sender.sendVoice({ ...mss, url: media });
268
- case "document":
269
- return await this.sender.sendFile({
270
- ...mss,
271
- url: media,
272
- fileName,
273
- text: text,
274
- });
275
- default:
276
- return await this.sender.sendText(mss);
356
+ var _a, _b;
357
+ try {
358
+ options = { ...options["options"] };
359
+ let mss = { from, text, options };
360
+ if (options.media) {
361
+ // Initialize detector with payload limits from global args
362
+ if ((_b = (_a = this.globalVendorArgs) === null || _a === void 0 ? void 0 : _a.payloadLimits) === null || _b === void 0 ? void 0 : _b.media) {
363
+ detectorMedia_1.detectorMedia.updateLimits(this.globalVendorArgs.payloadLimits.media);
364
+ }
365
+ const { media, mediaType, fileName, size } = await detectorMedia_1.detectorMedia.processMedia(options.media);
366
+ // Log large media processing
367
+ if (size && size > 1024 * 1024) {
368
+ const sizeMB = Math.round(size / 1024 / 1024 * 100) / 100;
369
+ console.log(`[SendMessage] Processing ${mediaType} file: ${sizeMB}MB`);
370
+ }
371
+ switch (mediaType) {
372
+ case "image":
373
+ return await this.sender.sendImage({
374
+ ...mss,
375
+ url: media,
376
+ text: text,
377
+ });
378
+ case "video":
379
+ return await this.sender.sendVideo({
380
+ ...mss,
381
+ url: media,
382
+ text: text,
383
+ });
384
+ case "audio":
385
+ return await this.sender.sendVoice({ ...mss, url: media });
386
+ case "document":
387
+ return await this.sender.sendFile({
388
+ ...mss,
389
+ url: media,
390
+ fileName,
391
+ text: text,
392
+ });
393
+ default:
394
+ return await this.sender.sendText(mss);
395
+ }
396
+ }
397
+ else {
398
+ return await this.sender.sendText(mss);
277
399
  }
278
400
  }
279
- else {
280
- return await this.sender.sendText(mss);
401
+ catch (error) {
402
+ // Enhanced error handling for large files
403
+ if (error.message && error.message.includes('exceeds limit')) {
404
+ console.error(`[SendMessage Error] File size validation failed: ${error.message}`);
405
+ throw new Error(`File too large: ${error.message}`);
406
+ }
407
+ else if (error.message && error.message.includes('too large')) {
408
+ console.error(`[SendMessage Error] Large file error: ${error.message}`);
409
+ throw error;
410
+ }
411
+ else {
412
+ console.error(`[SendMessage Error] ${error.message || 'Unknown error'}`);
413
+ throw error;
414
+ }
281
415
  }
282
416
  }
283
417
  // Métodos de conveniencia para mantener la API limpia
@@ -314,6 +448,9 @@ class SendWaveProvider extends bot_1.ProviderClass {
314
448
  sendLocation(...args) {
315
449
  return this.sender.sendLocation(...args);
316
450
  }
451
+ sendMedia(...args) {
452
+ return this.sender.sendMedia(...args);
453
+ }
317
454
  // Queue Flow Management Methods
318
455
  /**
319
456
  * Reset user timeout for queue flow system
@@ -3,6 +3,7 @@ export declare class SenderMessage implements ProviderInterface {
3
3
  private sendwaveApi?;
4
4
  private globalVendorArgs?;
5
5
  constructor(args: Partial<GlobalVendorArgs>);
6
+ private parseSize;
6
7
  sendPresence(data: SendPresence): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
7
8
  sendText(data: SendMessage): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
8
9
  sendList(data: SendList): Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
@@ -9,6 +9,8 @@ const axios_1 = __importDefault(require("axios"));
9
9
  const detectorMedia_1 = require("../utils/detectorMedia");
10
10
  class SenderMessage {
11
11
  constructor(args) {
12
+ const payloadLimits = args.payloadLimits || {};
13
+ const maxContentLength = this.parseSize(payloadLimits.json || '50mb');
12
14
  this.sendwaveApi = axios_1.default.create({
13
15
  baseURL: args.url,
14
16
  httpsAgent: new https_1.Agent({ rejectUnauthorized: false }),
@@ -16,9 +18,26 @@ class SenderMessage {
16
18
  "Content-Type": "application/json",
17
19
  apikey: args.apiKey,
18
20
  },
21
+ maxContentLength: maxContentLength,
22
+ maxBodyLength: maxContentLength,
23
+ timeout: 300000, // 5 minutes for large files
19
24
  });
20
25
  this.globalVendorArgs = args;
21
26
  }
27
+ parseSize(size) {
28
+ const units = {
29
+ 'b': 1,
30
+ 'kb': 1024,
31
+ 'mb': 1024 * 1024,
32
+ 'gb': 1024 * 1024 * 1024
33
+ };
34
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
35
+ if (!match)
36
+ return 50 * 1024 * 1024; // Default 50MB
37
+ const value = parseFloat(match[1]);
38
+ const unit = match[2] || 'b';
39
+ return Math.floor(value * units[unit]);
40
+ }
22
41
  async sendPresence(data) {
23
42
  var _a, _b;
24
43
  try {
@@ -96,10 +115,19 @@ class SenderMessage {
96
115
  }
97
116
  }
98
117
  async sendMedia(data) {
99
- var _a, _b;
118
+ var _a, _b, _c, _d, _e, _f, _g, _h;
100
119
  try {
101
- const { media, mimeType, mediaType } = await detectorMedia_1.detectorMedia.processMedia(data.url);
102
- return await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.post(`/message/sendMedia/${(_b = this.globalVendorArgs) === null || _b === void 0 ? void 0 : _b.name}`, {
120
+ // Initialize detector with payload limits from global args
121
+ if ((_b = (_a = this.globalVendorArgs) === null || _a === void 0 ? void 0 : _a.payloadLimits) === null || _b === void 0 ? void 0 : _b.media) {
122
+ detectorMedia_1.detectorMedia.updateLimits(this.globalVendorArgs.payloadLimits.media);
123
+ }
124
+ const { media, mimeType, mediaType, size } = await detectorMedia_1.detectorMedia.processMedia(data.url);
125
+ // Log large media processing
126
+ if (size && size > 1024 * 1024) {
127
+ const sizeMB = Math.round(size / 1024 / 1024 * 100) / 100;
128
+ console.log(`[SendMedia] Processing ${mediaType} file: ${sizeMB}MB`);
129
+ }
130
+ return await ((_c = this.sendwaveApi) === null || _c === void 0 ? void 0 : _c.post(`/message/sendMedia/${(_d = this.globalVendorArgs) === null || _d === void 0 ? void 0 : _d.name}`, {
103
131
  number: data.from.split("@")[0],
104
132
  caption: data.text,
105
133
  mediatype: mediaType,
@@ -110,7 +138,24 @@ class SenderMessage {
110
138
  }));
111
139
  }
112
140
  catch (e) {
113
- console.error(e.response.data.response.message);
141
+ // Enhanced error handling for large files
142
+ if (e.message && e.message.includes('exceeds limit')) {
143
+ console.error(`[SendMedia Error] File size validation failed: ${e.message}`);
144
+ throw new Error(`File too large: ${e.message}`);
145
+ }
146
+ else if (((_e = e.response) === null || _e === void 0 ? void 0 : _e.status) === 413) {
147
+ console.error('[SendMedia Error] Server rejected large payload');
148
+ throw new Error('File too large for server');
149
+ }
150
+ else if (e.code === 'ECONNABORTED') {
151
+ console.error('[SendMedia Error] Request timeout - file may be too large');
152
+ throw new Error('Upload timeout - file may be too large');
153
+ }
154
+ else {
155
+ const errorMsg = ((_h = (_g = (_f = e.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.response) === null || _h === void 0 ? void 0 : _h.message) || e.message || 'Unknown error';
156
+ console.error(`[SendMedia Error] ${errorMsg}`);
157
+ throw new Error(errorMsg);
158
+ }
114
159
  }
115
160
  }
116
161
  async sendFile(data) {
@@ -159,22 +204,48 @@ class SenderMessage {
159
204
  }
160
205
  }
161
206
  async sendVoice(data) {
162
- var _a, _b;
207
+ var _a, _b, _c, _d, _e, _f, _g, _h;
163
208
  try {
164
- const { media } = await detectorMedia_1.detectorMedia.processMedia(data.url);
209
+ // Initialize detector with payload limits from global args
210
+ if ((_b = (_a = this.globalVendorArgs) === null || _a === void 0 ? void 0 : _a.payloadLimits) === null || _b === void 0 ? void 0 : _b.media) {
211
+ detectorMedia_1.detectorMedia.updateLimits(this.globalVendorArgs.payloadLimits.media);
212
+ }
213
+ const { media, size } = await detectorMedia_1.detectorMedia.processMedia(data.url);
214
+ // Log large audio processing
215
+ if (size && size > 1024 * 1024) {
216
+ const sizeMB = Math.round(size / 1024 / 1024 * 100) / 100;
217
+ console.log(`[SendVoice] Processing audio file: ${sizeMB}MB`);
218
+ }
165
219
  await this.sendPresence({
166
220
  from: data.from,
167
221
  presence: "recording",
168
222
  delay: data.delay || 2000,
169
223
  });
170
- return await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.post(`/message/sendWhatsAppAudio/${(_b = this.globalVendorArgs) === null || _b === void 0 ? void 0 : _b.name}`, {
224
+ 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}`, {
171
225
  number: data.from,
172
226
  audio: media,
173
227
  ...this.globalVendorArgs,
174
228
  }));
175
229
  }
176
230
  catch (e) {
177
- console.error(e.response.data.response.message);
231
+ // Enhanced error handling for large audio files
232
+ if (e.message && e.message.includes('exceeds limit')) {
233
+ console.error(`[SendVoice Error] File size validation failed: ${e.message}`);
234
+ throw new Error(`Audio file too large: ${e.message}`);
235
+ }
236
+ else if (((_e = e.response) === null || _e === void 0 ? void 0 : _e.status) === 413) {
237
+ console.error('[SendVoice Error] Server rejected large payload');
238
+ throw new Error('Audio file too large for server');
239
+ }
240
+ else if (e.code === 'ECONNABORTED') {
241
+ console.error('[SendVoice Error] Request timeout - audio file may be too large');
242
+ throw new Error('Upload timeout - audio file may be too large');
243
+ }
244
+ else {
245
+ const errorMsg = ((_h = (_g = (_f = e.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.response) === null || _h === void 0 ? void 0 : _h.message) || e.message || 'Unknown error';
246
+ console.error(`[SendVoice Error] ${errorMsg}`);
247
+ throw new Error(errorMsg);
248
+ }
178
249
  }
179
250
  }
180
251
  async sendButton(data) {
@@ -219,7 +290,6 @@ class SenderMessage {
219
290
  var _a, _b;
220
291
  try {
221
292
  return await ((_a = this.sendwaveApi) === null || _a === void 0 ? void 0 : _a.post(`/message/sendReaction/${(_b = this.globalVendorArgs) === null || _b === void 0 ? void 0 : _b.name}`, {
222
- number: data.from.split("@")[0],
223
293
  key: data.key,
224
294
  reaction: data.reaction,
225
295
  ...this.globalVendorArgs,
@@ -3,10 +3,23 @@ interface DetectorMediaProps {
3
3
  mimeType?: string;
4
4
  mediaType?: string;
5
5
  fileName?: string;
6
+ size?: number;
7
+ }
8
+ interface MediaLimits {
9
+ image?: string;
10
+ video?: string;
11
+ audio?: string;
12
+ document?: string;
6
13
  }
7
14
  export declare class DetectorMedia {
8
15
  static instance: DetectorMedia;
9
- static getInstance(): DetectorMedia;
16
+ private mediaLimits;
17
+ constructor(limits?: MediaLimits);
18
+ static getInstance(limits?: MediaLimits): DetectorMedia;
19
+ updateLimits(limits: MediaLimits): void;
20
+ private parseSize;
21
+ private validateFileSize;
22
+ private validateUrlSize;
10
23
  isRemoteUrl(url: string): boolean;
11
24
  convertPathToBase64(filePath: string): Promise<DetectorMediaProps>;
12
25
  getMimeTypeFromUrl(url: string): Promise<string | null>;
@@ -16,6 +29,7 @@ export declare class DetectorMedia {
16
29
  * - media (base64 si es path, url si es remota)
17
30
  * - mimeType
18
31
  * - fileName
32
+ * - size
19
33
  */
20
34
  processMedia(input: string): Promise<DetectorMediaProps>;
21
35
  mapMimeToCategory(mimeType: string): "image" | "video" | "audio" | "document" | "unknown";
@@ -9,12 +9,79 @@ const path_1 = __importDefault(require("path"));
9
9
  const mime_types_1 = __importDefault(require("mime-types"));
10
10
  const axios_1 = __importDefault(require("axios"));
11
11
  class DetectorMedia {
12
- static getInstance() {
12
+ constructor(limits) {
13
+ this.mediaLimits = {
14
+ image: '10mb',
15
+ video: '100mb',
16
+ audio: '50mb',
17
+ document: '25mb',
18
+ ...limits
19
+ };
20
+ }
21
+ static getInstance(limits) {
13
22
  if (!DetectorMedia.instance) {
14
- DetectorMedia.instance = new DetectorMedia();
23
+ DetectorMedia.instance = new DetectorMedia(limits);
15
24
  }
16
25
  return DetectorMedia.instance;
17
26
  }
27
+ updateLimits(limits) {
28
+ this.mediaLimits = { ...this.mediaLimits, ...limits };
29
+ }
30
+ parseSize(size) {
31
+ const units = {
32
+ 'b': 1,
33
+ 'kb': 1024,
34
+ 'mb': 1024 * 1024,
35
+ 'gb': 1024 * 1024 * 1024
36
+ };
37
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
38
+ if (!match)
39
+ return 10 * 1024 * 1024; // Default 10MB
40
+ const value = parseFloat(match[1]);
41
+ const unit = match[2] || 'b';
42
+ return Math.floor(value * units[unit]);
43
+ }
44
+ validateFileSize(filePath, mediaType) {
45
+ return new Promise((resolve, reject) => {
46
+ promises_1.default.stat(filePath).then((stats) => {
47
+ const fileSize = stats.size;
48
+ const limitStr = this.mediaLimits[mediaType] || '10mb';
49
+ const limit = this.parseSize(limitStr);
50
+ if (fileSize > limit) {
51
+ const fileSizeMB = Math.round(fileSize / 1024 / 1024 * 100) / 100;
52
+ const limitMB = Math.round(limit / 1024 / 1024 * 100) / 100;
53
+ reject(new Error(`File size ${fileSizeMB}MB exceeds limit of ${limitMB}MB for ${mediaType} files`));
54
+ }
55
+ else {
56
+ resolve();
57
+ }
58
+ }).catch(reject);
59
+ });
60
+ }
61
+ async validateUrlSize(url, mediaType) {
62
+ try {
63
+ const response = await axios_1.default.head(url);
64
+ const contentLength = parseInt(response.headers['content-length'] || '0');
65
+ if (contentLength > 0) {
66
+ const limitStr = this.mediaLimits[mediaType] || '10mb';
67
+ const limit = this.parseSize(limitStr);
68
+ if (contentLength > limit) {
69
+ const fileSizeMB = Math.round(contentLength / 1024 / 1024 * 100) / 100;
70
+ const limitMB = Math.round(limit / 1024 / 1024 * 100) / 100;
71
+ throw new Error(`Remote file size ${fileSizeMB}MB exceeds limit of ${limitMB}MB for ${mediaType} files`);
72
+ }
73
+ }
74
+ return contentLength;
75
+ }
76
+ catch (error) {
77
+ if (error.message.includes('exceeds limit')) {
78
+ throw error;
79
+ }
80
+ // If we can't get size, assume it's valid but warn
81
+ console.warn(`[DetectorMedia] Could not validate size for URL: ${url}`);
82
+ return 0;
83
+ }
84
+ }
18
85
  isRemoteUrl(url) {
19
86
  try {
20
87
  const u = new URL(url);
@@ -25,18 +92,22 @@ class DetectorMedia {
25
92
  }
26
93
  }
27
94
  async convertPathToBase64(filePath) {
28
- const fileBuffer = await promises_1.default.readFile(filePath);
29
95
  const mediaType = this.mapMimeToCategory(mime_types_1.default.lookup(filePath).toString());
30
96
  if (!mediaType)
31
97
  throw new Error("No se pudo determinar el tipo MIME del archivo");
98
+ // Validate file size before processing
99
+ await this.validateFileSize(filePath, mediaType);
100
+ const fileBuffer = await promises_1.default.readFile(filePath);
32
101
  const base64Data = fileBuffer.toString("base64");
33
102
  const media = base64Data;
34
103
  const fileName = path_1.default.basename(filePath);
104
+ const stats = await promises_1.default.stat(filePath);
35
105
  return {
36
106
  media,
37
107
  mediaType,
38
108
  fileName,
39
109
  mimeType: mime_types_1.default.lookup(filePath).toString(),
110
+ size: stats.size,
40
111
  };
41
112
  }
42
113
  async getMimeTypeFromUrl(url) {
@@ -60,18 +131,22 @@ class DetectorMedia {
60
131
  * - media (base64 si es path, url si es remota)
61
132
  * - mimeType
62
133
  * - fileName
134
+ * - size
63
135
  */
64
136
  async processMedia(input) {
65
137
  if (this.isRemoteUrl(input)) {
66
138
  const mediaType = await this.getMimeTypeFromUrl(input);
67
139
  if (!mediaType)
68
140
  throw new Error("No se pudo determinar el tipo MIME de la URL remota");
141
+ // Validate remote file size
142
+ const size = await this.validateUrlSize(input, mediaType);
69
143
  const fileName = await this.extractFileNameFromUrl(input);
70
144
  return {
71
145
  media: input,
72
146
  mimeType: mime_types_1.default.lookup(input).toString(),
73
147
  fileName,
74
148
  mediaType,
149
+ size,
75
150
  };
76
151
  }
77
152
  return this.convertPathToBase64(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamastudio/sendwave-provider",
3
- "version": "0.0.5",
3
+ "version": "0.0.7-dev",
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",
@@ -22,7 +22,7 @@
22
22
  "author": "Ameth Galarcio <amethgm@gmail.com>",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@builderbot/bot": "^1.3.1",
25
+ "@builderbot/bot": "^1.2.9",
26
26
  "@gamastudio/colorslog": "^0.1.6",
27
27
  "@types/mime-types": "^2.1.4",
28
28
  "axios": "^1.11.0",
package/queue.class.log DELETED
File without changes