neekuro 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,540 @@
1
+ 'use strict';
2
+ const Canvas = require('canvas');
3
+ const NekoError = require('../errors/error');
4
+ const hex_reg = require('hex-color-regex')({ strict: true })
5
+ const path = require('path');
6
+
7
+ /**
8
+ * @typedef {import('../../typings').FontNames} FontNames
9
+ * @typedef {import('../../typings').WelcomeTextData} WelcomeTextData
10
+ * @typedef {import('../../typings').WelcomeAvatarData} WelcomeAvatarData
11
+ */
12
+
13
+ /**
14
+ * Clase para generar imágenes de bienvenida personalizadas para Discord
15
+ * @class Welcome
16
+ * @example
17
+ * const Welcome = require('neekuro').Welcome;
18
+ * const welcome = new Welcome()
19
+ * .setResolution(1200, 600)
20
+ * .setFont('arial')
21
+ * .setTitle('¡Bienvenido!', { text_color: '#FF5733' })
22
+ * .setDescription('Al servidor de la comunidad', { font_size: 24 });
23
+ */
24
+ class Welcome {
25
+
26
+ /**
27
+ * Construye una imagen
28
+ */
29
+ constructor() {
30
+ /**
31
+ * @property {Object} data - Configuración actual de la imagen
32
+ * @property {string} data.font - Fuente actualmente seleccionada
33
+ * @property {number} data.width - Ancho actual de la imagen
34
+ * @property {number} data.height - Alto actual de la imagen
35
+ * @property {Object} data.background - Configuración del fondo
36
+ * @property {'color'|'image'} data.background.type - Tipo de fondo
37
+ * @property {string} data.background.value - Color o ruta de imagen
38
+ * @property {Object} data.avatar - Configuración del avatar
39
+ * @property {?string} data.avatar.source - Ruta a la imagen del avatar
40
+ * @property {number} data.avatar.x - Posición X del avatar
41
+ * @property {number} data.avatar.y - Posición Y del avatar
42
+ * @property {number} data.avatar.radio - Radio para avatar circular
43
+ * @property {string} data.avatar.border - Color del borde del avatar
44
+ * @property {Object} data.title - Configuración del título
45
+ * @property {?string} data.title.content - Contenido del título
46
+ * @property {number} data.title.x - Posición X del título
47
+ * @property {number} data.title.y - Posición Y del título
48
+ * @property {number} data.title.font_size - Tamaño de fuente del título
49
+ * @property {string} data.title.text_color - Color del texto del título
50
+ * @property {Object} data.description - Configuración de la descripción
51
+ * @property {?string} data.description.content - Contenido de la descripción
52
+ * @property {number} data.description.x - Posición X de la descripción
53
+ * @property {number} data.description.y - Posición Y de la descripción
54
+ * @property {number} data.description.font_size - Tamaño de fuente de la descripción
55
+ * @property {string} data.description.text_color - Color del texto de la descripción
56
+ */
57
+ this.data = {
58
+ font: 'arial',
59
+ width: 1024,
60
+ height: 450,
61
+ background: {
62
+ type: 'color',
63
+ value: '#23272A'
64
+ },
65
+ avatar: {
66
+ source: null,
67
+ x: 455,
68
+ y: 80,
69
+ radio: 120,
70
+ border: "#F7F7F7"
71
+ },
72
+ title: {
73
+ content: "¡Bienvenido!",
74
+ x: 590,
75
+ y: 450,
76
+ font_size: 40,
77
+ text_color: '#FFFFFF'
78
+ },
79
+ description: {
80
+ content: 'Al mejor servidor de discord',
81
+ x: 590,
82
+ y: 500,
83
+ font_size: 30,
84
+ text_color: "#F7F7F7"
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Establece la resolución de la imagen de bienvenida
91
+ * @param {number|'default'} width - Ancho en píxeles o 'default' para 1024px
92
+ * @param {number|'default'} height - Alto en píxeles o 'default' para 450px
93
+ * @returns {Welcome}
94
+ * @throws {NekoError} Si los parámetros son inválidos
95
+ * @example
96
+ * // Establecer resolución personalizada
97
+ * welcome.setResolution(1920, 1080);
98
+ *
99
+ * // Restablecer a valores por defecto
100
+ * welcome.setResolution('default', 'default');
101
+ */
102
+ setResolution(width, height) {
103
+ if (!width) throw new NekoError('ResolutionError', 'el parametro <width> no puede quedar vacio');
104
+ if (!height) throw new NekoError('ResolutionError', 'el parametro <height> no puede quedar vacio');
105
+
106
+ if (width == 'default') width = this.data.width; else {
107
+ if (typeof width != 'number') throw new NekoError('TypeError', 'el parametro <width> solo acepta tipo \'number\', recibió ' + typeof width);
108
+ this.data.width = width;
109
+ }
110
+
111
+ if (height == 'default') height = this.data.height; else {
112
+ if (typeof height != 'number') throw new NekoError('TypeError', 'el parametro <height> solo acepta tipo \'number\', recibió ' + typeof height);
113
+ this.data.height = height;
114
+ }
115
+
116
+ return this
117
+ }
118
+
119
+ /**
120
+ * Configura la tipografía para el texto de la imagen
121
+ * @param {FontNames} name - Nombre de la fuente predefinida o 'custom' para personalizada
122
+ * @param {string} [dir] - Ruta al archivo .ttf (requerido si name='custom')
123
+ * @returns {Welcome}
124
+ * @throws {NekoError} Si la fuente no existe o los parámetros son inválidos
125
+ * @example
126
+ * // Usar fuente predefinida
127
+ * welcome.setFont('calibri');
128
+ *
129
+ * // Usar fuente personalizada
130
+ * welcome.setFont('custom', './fonts/mi_fuente.ttf');
131
+ */
132
+ setFont(name, dir) {
133
+ let ruta = null;
134
+
135
+ if (!name) throw new NekoError('FontError', 'el parametro <name> no puede quedar vacio');
136
+ if (typeof name != 'string') throw new NekoError('TypeError', 'el parametro <name> solo acepta tipo \'string\', recibió ' + typeof name);
137
+
138
+ switch (name) {
139
+ case 'arial':
140
+ ruta = path.join(__dirname, '../../assets/arial.ttf');
141
+ break;
142
+ case 'calibri':
143
+ ruta = path.join(__dirname, '../../assets/calibri.ttf');
144
+ break;
145
+ case 'custom':
146
+ if (!dir) throw new NekoError('FontError', 'el parametro <dir> no puede quedar vacio en un tipo de fuente customizada');
147
+ if (typeof dir != 'string') throw new NekoError('TypeError', 'el parametro <dir> solo acepta tipo \'string\', recibió ' + typeof dir);
148
+ if (!dir.endsWith('ttf')) throw new NekoError('FontError', 'se nesecita un archivo ttf en el parametro <dir>')
149
+ ruta = path.join(__dirname, '../../../../' + dir.replace('./', ''));
150
+ break;
151
+ default: throw new NekoError('FontError', 'el parametro <name> no acepta ningun tipo diferente a los preterminados');
152
+ }
153
+ try {
154
+ Canvas.registerFont(ruta, { family: name })
155
+ } catch (error) {
156
+ throw new NekoError('DirError', 'no se puede encontrar el directorio especificado')
157
+ }
158
+
159
+ this.data.font = name
160
+ return this;
161
+ }
162
+
163
+ /**
164
+ * Agrega un título a la imagen de bienvenida
165
+ * @param {string} text - Texto del título
166
+ * @param {WelcomeTextData} data - Configuración del título
167
+ * @throws {NekoError} Arroja un error si parámetros son inválidos
168
+ * @returns {Welcome}
169
+ */
170
+ setTitle(text, data) {
171
+ // Validaciones del texto
172
+ if (!text) {
173
+ throw new NekoError('TitleError', 'El parámetro <text> no puede estar vacío');
174
+ }
175
+ if (typeof text !== 'string') {
176
+ throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
177
+ }
178
+
179
+ this.data.title.content = text;
180
+
181
+ // Validaciones para las propiedades adicionales
182
+ if (data) {
183
+ // Validación para text_color
184
+ if (data.text_color) {
185
+ if (typeof data.text_color !== 'string') {
186
+ throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
187
+ }
188
+ if (!data.text_color.startsWith('#')) {
189
+ throw new NekoError('TitleError', 'El valor de <text_color> debe comenzar con \'#\'');
190
+ }
191
+ if (!hex_reg.test(data.text_color)) {
192
+ throw new NekoError('TitleError', 'El valor de <text_color> no es un color válido');
193
+ }
194
+ this.data.title.text_color = data.text_color;
195
+ }
196
+
197
+ // validacion del tamaño de texto
198
+ if (data.font_size) {
199
+ if (typeof data.font_size !== 'number') {
200
+ throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
201
+ }
202
+ if (data.font_size <= 0) {
203
+ throw new NekoError('TitleError', 'El valor de <font_size> debe ser mayor que 0');
204
+ }
205
+ this.data.title.font_size = data.font_size;
206
+ }
207
+
208
+ // validacion de posiciones
209
+ if (data.x) {
210
+ if (typeof data.x !== 'number') {
211
+ throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
212
+ }
213
+ this.data.title.x = data.x;
214
+ }
215
+
216
+ if (data.y) {
217
+ if (typeof data.y !== 'number') {
218
+ throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
219
+ }
220
+ this.data.title.y = data.y;
221
+ }
222
+ }
223
+
224
+ return this
225
+ }
226
+
227
+ /**
228
+ * Agrega una descripción a la imagen de bienvenida
229
+ * @param {string} text - Texto de la descripción
230
+ * @param {WelcomeTextData} data - Configuración de la descripción
231
+ * @throws {NekoError} Arroja un error si parámetros son inválidos
232
+ * @returns {Welcome}
233
+ */
234
+ setDescription(text, data) {
235
+ // Validaciones del texto
236
+ if (!text) {
237
+ throw new NekoError('DescriptionError', 'El parámetro <text> no puede estar vacío');
238
+ }
239
+ if (typeof text !== 'string') {
240
+ throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
241
+ }
242
+
243
+ this.data.description.content = text;
244
+
245
+ // Validaciones para las propiedades adicionales
246
+ if (data) {
247
+ // Validación para text_color
248
+ if (data.text_color) {
249
+ if (typeof data.text_color !== 'string') {
250
+ throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
251
+ }
252
+ if (!data.text_color.startsWith('#')) {
253
+ throw new NekoError('DescriptionError', 'El valor de <text_color> debe comenzar con \'#\'');
254
+ }
255
+ if (!hex_reg.test(data.text_color)) {
256
+ throw new NekoError('DescriptionError', 'El valor de <text_color> no es un color válido');
257
+ }
258
+ this.data.description.text_color = data.text_color;
259
+ }
260
+
261
+ // validacion del tamaño de texto
262
+ if (data.font_size) {
263
+ if (typeof data.font_size !== 'number') {
264
+ throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
265
+ }
266
+ if (data.font_size <= 0) {
267
+ throw new NekoError('DescriptionError', 'El valor de <font_size> debe ser mayor que 0');
268
+ }
269
+ this.data.description.font_size = data.font_size;
270
+ }
271
+
272
+ // validacion de posiciones
273
+ if (data.x) {
274
+ if (typeof data.x !== 'number') {
275
+ throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
276
+ }
277
+ this.data.description.x = data.x;
278
+ }
279
+
280
+ if (data.y) {
281
+ if (typeof data.y !== 'number') {
282
+ throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
283
+ }
284
+ this.data.description.y = data.y;
285
+ }
286
+ }
287
+
288
+ return this
289
+ }
290
+
291
+ /**
292
+ * Establece el fondo de la imagen de bienvenida (color o imagen)
293
+ * @param {'color'|'image'} type - Tipo de fondo ('color' o 'image')
294
+ * @param {string|Buffer} data - Color hexadecimal (ej. '#FF5733') o imagen (URL/Buffer)
295
+ * @returns {Welcome}
296
+ * @throws {NekoError} Si los parámetros son inválidos
297
+ * @example
298
+ * // Fondo con color
299
+ * welcome.setBackground('color', '#23272A');
300
+ *
301
+ * // Fondo con imagen desde URL
302
+ * welcome.setBackground('image', 'https://ejemplo.com/fondo.jpg');
303
+ *
304
+ * // Fondo con imagen desde Buffer
305
+ * const imageBuffer = fs.readFileSync('fondo.jpg');
306
+ * welcome.setBackground('image', imageBuffer);
307
+ */
308
+ setBackground(type, data) {
309
+ if (!type) throw new NekoError('BackgroundError', 'El parámetro <type> no puede estar vacío');
310
+ if (typeof type != 'string') throw new NekoError('TypeError', `El parámetro <type> debe ser string, recibió ${typeof type}`);
311
+ if (!['color', 'image'].includes(type)) throw new NekoError('BackgroundError', 'El tipo de fondo debe ser "color" o "image"');
312
+
313
+ if (!data) throw new NekoError('BackgroundError', 'El parámetro <data> no puede estar vacío');
314
+
315
+ if (type == 'color') {
316
+ if (typeof data != 'string') throw new NekoError('TypeError', 'Para fondo de color, <data> debe ser string');
317
+ if (!data.startsWith('#')) throw new NekoError('BackgroundError', 'El color debe comenzar con \'#\'');
318
+ if (!hex_reg.test(data)) throw new NekoError('BackgroundError', 'El valor no es un color hexadecimal válido');
319
+
320
+ this.data.background = { type, data };
321
+ return this;
322
+ }
323
+
324
+ if (type == 'image') {
325
+ if (typeof data != 'string' && !Buffer.isBuffer(data)) {
326
+ throw new NekoError('TypeError', 'Para fondo de imagen, <data> debe ser URL (string) o Buffer');
327
+ }
328
+
329
+ if (typeof data == 'string') {
330
+ try {
331
+ new URL(data);
332
+ } catch (error) {
333
+ throw new NekoError('BackgroundError', 'La URL proporcionada no es válida');
334
+ }
335
+ }
336
+
337
+ this.data.background = { type, data };
338
+ return this;
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Establece el avatar y su configuración en la imagen de bienvenida
344
+ * @param {string|Buffer} source - URL del avatar o Buffer de la imagen
345
+ * @param {WelcomeAvatarData} [data] - Configuración opcional del avatar
346
+ * @returns {Welcome}
347
+ * @throws {NekoError} Si los parámetros son inválidos
348
+ * @example
349
+ * // Configuración básica con URL
350
+ * welcome.setAvatar('https://example.com/avatar.jpg');
351
+ *
352
+ * // Configuración completa con Buffer
353
+ * const avatarBuffer = fs.readFileSync('avatar.png');
354
+ * welcome.setAvatar(avatarBuffer, {
355
+ * x: 300,
356
+ * y: 100,
357
+ * border: '#FF5733',
358
+ * radio: 120
359
+ * });
360
+ */
361
+ setAvatar(source, data) {
362
+ // Validación del source
363
+ if (!source) {
364
+ throw new NekoError('AvatarError', 'El parámetro <source> no puede estar vacío');
365
+ }
366
+
367
+ if (typeof source != 'string' && !Buffer.isBuffer(source)) {
368
+ throw new NekoError('TypeError', 'El parámetro <source> debe ser string (URL) o Buffer');
369
+ }
370
+
371
+ if (typeof source === 'string') {
372
+ try {
373
+ const url = new URL(source);
374
+
375
+ if (!['http:', 'https:'].includes(url.protocol)) {
376
+ throw new NekoError('AvatarError', 'La URL debe usar HTTP/HTTPS');
377
+ }
378
+
379
+ const extension = url.pathname.toLowerCase().split('.').pop();
380
+ if (!['jpg', 'jpeg', 'png', 'gif'].includes(extension)) {
381
+ throw new NekoError('AvatarError', 'Formato de imagen no soportado. Usa JPEG, PNG o GIF');
382
+ }
383
+ } catch (error) {
384
+ if (error instanceof NekoError) throw error;
385
+ throw new NekoError('AvatarError', 'La URL del avatar no es válida');
386
+ }
387
+ }
388
+
389
+ if (Buffer.isBuffer(source)) {
390
+ if (source.length < 8) {
391
+ throw new NekoError('AvatarError', 'El Buffer de imagen es demasiado pequeño');
392
+ }
393
+
394
+ const header = source.toString('hex', 0, 8);
395
+ const isJPEG = header.startsWith('ffd8ff');
396
+ const isPNG = header.startsWith('89504e470d0a1a0a');
397
+ const isGIF = header.startsWith('47494638');
398
+
399
+ if (!isJPEG && !isPNG && !isGIF) {
400
+ throw new NekoError('AvatarError', 'Formato de imagen no soportado. El Buffer debe ser JPEG, PNG o GIF');
401
+ }
402
+ }
403
+
404
+ this.data.avatar.source = source;
405
+
406
+ if (data) {
407
+ if (data.x) {
408
+ if (typeof data.x != 'number') {
409
+ throw new NekoError('TypeError', 'La propiedad <x> debe ser un número');
410
+ }
411
+ if (data.x < 0) {
412
+ throw new NekoError('AvatarError', 'La posición X no puede ser negativa');
413
+ }
414
+ this.data.avatar.x = data.x;
415
+ }
416
+
417
+ if (data.y) {
418
+ if (typeof data.y != 'number') {
419
+ throw new NekoError('TypeError', 'La propiedad <y> debe ser un número');
420
+ }
421
+ this.data.avatar.y = data.y;
422
+ }
423
+
424
+ if (data.border) {
425
+ if (typeof data.border != 'string') {
426
+ throw new NekoError('TypeError', 'La propiedad <border> debe ser un string');
427
+ }
428
+ if (!data.border.startsWith('#')) {
429
+ throw new NekoError('AvatarError', 'El color del borde debe comenzar con #');
430
+ }
431
+ if (!hex_reg.test(data.border)) {
432
+ throw new NekoError('AvatarError', 'El color del borde no es un hexadecimal válido');
433
+ }
434
+ this.data.avatar.border = data.border;
435
+ }
436
+
437
+ if (data.radio) {
438
+ if (typeof data.radio != 'number') {
439
+ throw new NekoError('TypeError', 'La propiedad <radio> debe ser un número');
440
+ }
441
+ this.data.avatar.radio = data.radio;
442
+ }
443
+ }
444
+
445
+ return this;
446
+ }
447
+
448
+ /**
449
+ * Genera la imagen de bienvenida con la configuracion actual
450
+ * @returns {Promise<Buffer>} Buffer de la imagen generada
451
+ * @throws {NekoError} Si faltan datos
452
+ * @throws {Error} Si hay problemas al cargar imágenes
453
+ */
454
+ async build() {
455
+ if (!this.data.avatar?.source) {
456
+ throw new NekoError('BuildError', 'No se ha podido cargar un avatar. Usa el metodo .setAvatar()')
457
+ }
458
+ if (!this.data.title?.content) {
459
+ throw new NekoError('BuildError', 'No se puede dar un titulo vacío. Usa el metodo .setTitle()')
460
+ }
461
+ if (!this.data.description?.content) {
462
+ throw new NekoError('BuildError', 'No se puede dar una descripcion vacía. Usa el metodo .setDescription()')
463
+ }
464
+
465
+ try {
466
+ const [bg, avatar] = await Promise.all([
467
+ this.data.background.type == 'image' ? Canvas.loadImage(this.data.background.value) : null,
468
+ Canvas.loadImage(this.data.avatar.source)
469
+ ])
470
+
471
+ const canvas = Canvas.createCanvas(this.data.width, this.data.height)
472
+ const ctx = canvas.getContext('2d')
473
+
474
+ if (bg) {
475
+ let ratio = Math.max(canvas.width / bg.width, canvas.height / bg.height);
476
+ let width = bg.width * ratio;
477
+ let height = bg.height * ratio;
478
+ ctx.drawImage(bg, (canvas.width - width) / 2, (canvas.height - height) / 2, width, height);
479
+ } else {
480
+ ctx.fillStyle = this.data.background.value;
481
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
482
+ }
483
+
484
+ const maxTitleWidth = this.data.width * 0.8;
485
+ let titleFontSize = this.data.title.font_size;
486
+ ctx.font = `${titleFontSize}px ${this.data.font}`;
487
+
488
+ while (ctx.measureText(this.data.title.content).width > maxTitleWidth && titleFontSize > 10) {
489
+ titleFontSize -= 1;
490
+ ctx.font = `${titleFontSize}px ${this.data.font}`;
491
+ }
492
+
493
+ ctx.fillStyle = this.data.title.text_color;
494
+ ctx.textAlign = "center";
495
+ ctx.fillText(this.data.title.content, this.data.title.x, this.data.title.y);
496
+
497
+ const maxDescWidth = this.data.width * 0.9;
498
+ let descFontSize = this.data.description.font_size;
499
+ ctx.font = `${descFontSize}px ${this.data.font}`;
500
+
501
+ while (ctx.measureText(this.data.description.content).width > maxDescWidth && descFontSize > 8) {
502
+ descFontSize -= 1;
503
+ ctx.font = `${descFontSize}px ${this.data.font}`;
504
+ }
505
+
506
+ ctx.fillStyle = this.data.description.text_color;
507
+ ctx.textAlign = "center";
508
+ ctx.fillText(this.data.description.content, this.data.description.x, this.data.description.y);
509
+
510
+ const x = this.data.avatar.x;
511
+ const y = this.data.avatar.y;
512
+ const radio = this.data.avatar.radio;
513
+
514
+ if (this.data.avatar.border) {
515
+ ctx.beginPath();
516
+ ctx.fillStyle = this.data.avatar.border;
517
+ ctx.arc(x + radio, y + radio, radio + 6, 0, 2 * Math.PI);
518
+ ctx.fill();
519
+ ctx.closePath();
520
+ }
521
+
522
+ ctx.save();
523
+ ctx.beginPath();
524
+ ctx.arc(x + radio, y + radio, radio, 0, Math.PI * 2, true);
525
+ ctx.closePath();
526
+ ctx.clip();
527
+ ctx.drawImage(avatar, x, y, radio * 2, radio * 2);
528
+ ctx.restore();
529
+
530
+ return canvas.toBuffer();
531
+ } catch (error) {
532
+ if(error instanceof NekoError) {
533
+ throw error;
534
+ }
535
+ throw new NekoError('GenerateError', 'Error al generar la imagen: '+error.message)
536
+ }
537
+ }
538
+ }
539
+
540
+ module.exports = Welcome;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @typedef {import('../typings').SfwCategories} SfwCategories
3
+ * @typedef {import('../typings').GifMap} GifMap
4
+ */
5
+
6
+ const APIClient = require('../rest/apiclient');
7
+ const NekoError = require('./errors/error');
8
+ const NekoGif = require('./package/nekogif');
9
+ const { getToken } = require('./user/baseuser');
10
+
11
+ // url base de la api para SFW
12
+ const base = 'https://www.nexatdc.work.gd/api/sfw/'
13
+
14
+ /**
15
+ * Clase SFW
16
+ * No se necesita instanciar para acceder a sus metodos
17
+ */
18
+ class SFW {
19
+ /**
20
+ * Funcion principal para obtener gifs de cierta categoria
21
+ * @template {SfwCategories} T
22
+ * @param {T} cat corresponde a la subcategoria de SFW
23
+ * @param {GifMap[T]} gif corresponde al gif que quieras obtener por categoria
24
+ * @returns {Promise<NekoGif>}
25
+ */
26
+ static async getGif(cat, gif) {
27
+ if (!cat) throw new NekoError('NoInput', 'el parametro <cat> es requerido');
28
+ if (!gif) throw new NekoError('NoInput', 'el parametro <gif> es requerido');
29
+
30
+ let solicitud = new APIClient(base, { token: getToken() }), response = null;
31
+ switch (cat) {
32
+ case 'action':
33
+ response = solicitud.get('/action/' + gif + '?random=true')
34
+ break;
35
+ case 'reaction':
36
+ response = solicitud.get('/reaction/' + gif + '?random=true')
37
+ break;
38
+ default:
39
+ throw new NekoError('InvalidInput', 'la categoria que ingresas es invalida')
40
+ }
41
+ return new NekoGif(await response);
42
+ }
43
+ }
44
+
45
+ module.exports = SFW
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ let tokenGlobal = null;
4
+
5
+ /**
6
+ * Clase BaseUser
7
+ * Solamente gestiona el token por todo el paquete.
8
+ */
9
+ class BaseUser {
10
+ /**
11
+ * Establece el token "global" en todo el paquete.
12
+ * @param {string} token token a establecer.
13
+ */
14
+ static setToken(token) {
15
+ tokenGlobal = token;
16
+ }
17
+
18
+ /**
19
+ * Obten el token del usuario si este ya se estableció
20
+ * @returns {string} Token de usuario
21
+ */
22
+ static getToken() {
23
+ return tokenGlobal;
24
+ }
25
+ }
26
+
27
+ module.exports = BaseUser;
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const { setToken } = require("./baseuser");
4
+ const NekoError = require('../errors/error')
5
+
6
+ /**
7
+ * Clase Usuario
8
+ * Instancia al usuario que hara uso de la api
9
+ */
10
+ class User {
11
+ /**
12
+ * Puedes establecer el token aqui, sin embargo, recomiendo hacerlo en el metodo dedicado
13
+ * @param {string} token Token de la api
14
+ */
15
+ constructor(token) {
16
+ this.token_ = token
17
+ setToken(this.#getToken());
18
+ }
19
+
20
+ /**
21
+ * Establece el token para usar la api
22
+ * @param {string} token token de la api
23
+ * @throws {NekoError} si el token no fue dado o no es una cadena
24
+ */
25
+ token(token) {
26
+ if(!token) throw new NekoError('NoToken', 'el parametro <token> es requerido');
27
+ if(typeof(token) !== 'string') throw new NekoError('InvalidType', `Token invaldo... el tipo de dato es String no ${typeof(token)}`);
28
+ this.token_ = token;
29
+ setToken(this.#getToken());
30
+ }
31
+
32
+ /**
33
+ * Obtiene el token
34
+ * @returns {string|undefined} token de la api
35
+ */
36
+ #getToken() {
37
+ return this.token_;
38
+ }
39
+ }
40
+
41
+ module.exports = User
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ //#region Clases
4
+
5
+ exports.User = require('../classes/user/user')
6
+
7
+ // paquete
8
+
9
+ exports.NekoGif = require('../classes/package/nekogif')
10
+ exports.BaseUser = require('../classes/user/baseuser')
11
+ exports.Welcome = require('../classes/package/welcome')
12
+
13
+ // rest
14
+
15
+ exports.APIClient = require('../rest/apiclient')
16
+
17
+ // errores
18
+
19
+ exports.APIError = require('../classes/errors/apierror')
20
+ exports.Error = require('../classes/errors/error')
21
+
22
+ //#region "constantes"
23
+
24
+ exports.SFW = require('../classes/sfw')