neekuro 2.2.0 → 2.3.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.
- package/CHANGELOG.md +31 -14
- package/README.md +117 -117
- package/package.json +64 -64
- package/sources/classes/SFW.js +25 -4
- package/sources/classes/errors/APIError.js +49 -49
- package/sources/classes/errors/Error.js +21 -21
- package/sources/classes/package/NekoGif.js +46 -46
- package/sources/classes/package/welcome.js +446 -446
- package/sources/classes/user/BaseUser.js +20 -20
- package/sources/classes/user/User.js +34 -34
- package/sources/main/index.js +20 -20
- package/sources/rest/APIClient.js +34 -34
- package/sources/typings/index.d.ts +374 -355
- package/sources/utils/imageLoader.js +38 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const Canvas = require('canvas');
|
|
3
|
-
const NekoError = require('../errors/Error.js');
|
|
4
|
-
const hex_reg = require('hex-color-regex')({ strict: true })
|
|
1
|
+
'use strict';
|
|
2
|
+
const Canvas = require('canvas');
|
|
3
|
+
const NekoError = require('../errors/Error.js');
|
|
4
|
+
const hex_reg = require('hex-color-regex')({ strict: true })
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const { loadImageSource, parseRemoteURL, validateImageBuffer } = require('../../utils/imageLoader.js');
|
|
@@ -11,112 +11,112 @@ const DEFAULT_HEIGHT = 520;
|
|
|
11
11
|
const MIN_DIMENSION = 128;
|
|
12
12
|
const MAX_DIMENSION = 4096;
|
|
13
13
|
const registeredFonts = new Set();
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {import('../../typings').FontNames} FontNames
|
|
17
|
-
* @typedef {import('../../typings').WelcomeTextData} WelcomeTextData
|
|
18
|
-
* @typedef {import('../../typings').WelcomeAvatarData} WelcomeAvatarData
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Clase para generar imágenes de bienvenida personalizadas para Discord
|
|
23
|
-
* @class Welcome
|
|
24
|
-
* @example
|
|
25
|
-
* const Welcome = require('neekuro').Welcome;
|
|
26
|
-
* const welcome = new Welcome()
|
|
27
|
-
* .setResolution(1200, 600)
|
|
28
|
-
* .setFont('arial')
|
|
29
|
-
* .setTitle('¡Bienvenido!', { text_color: '#FF5733' })
|
|
30
|
-
* .setDescription('Al servidor de la comunidad', { font_size: 24 });
|
|
31
|
-
*/
|
|
32
|
-
class Welcome {
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Construye una imagen
|
|
36
|
-
*/
|
|
37
|
-
constructor() {
|
|
38
|
-
/**
|
|
39
|
-
* @property {Object} data - Configuración actual de la imagen
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {import('../../typings').FontNames} FontNames
|
|
17
|
+
* @typedef {import('../../typings').WelcomeTextData} WelcomeTextData
|
|
18
|
+
* @typedef {import('../../typings').WelcomeAvatarData} WelcomeAvatarData
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Clase para generar imágenes de bienvenida personalizadas para Discord
|
|
23
|
+
* @class Welcome
|
|
24
|
+
* @example
|
|
25
|
+
* const Welcome = require('neekuro').Welcome;
|
|
26
|
+
* const welcome = new Welcome()
|
|
27
|
+
* .setResolution(1200, 600)
|
|
28
|
+
* .setFont('arial')
|
|
29
|
+
* .setTitle('¡Bienvenido!', { text_color: '#FF5733' })
|
|
30
|
+
* .setDescription('Al servidor de la comunidad', { font_size: 24 });
|
|
31
|
+
*/
|
|
32
|
+
class Welcome {
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Construye una imagen
|
|
36
|
+
*/
|
|
37
|
+
constructor() {
|
|
38
|
+
/**
|
|
39
|
+
* @property {Object} data - Configuración actual de la imagen
|
|
40
40
|
* @property {string} data.font - Fuente actualmente seleccionada
|
|
41
41
|
* @property {'center'|'manual'} data.layout - Distribución de los elementos
|
|
42
|
-
* @property {number} data.width - Ancho actual de la imagen
|
|
43
|
-
* @property {number} data.height - Alto actual de la imagen
|
|
44
|
-
* @property {Object} data.background - Configuración del fondo
|
|
45
|
-
* @property {'color'|'image'} data.background.type - Tipo de fondo
|
|
46
|
-
* @property {string} data.background.value - Color o ruta de imagen
|
|
47
|
-
* @property {Object} data.avatar - Configuración del avatar
|
|
48
|
-
* @property {?string} data.avatar.source - Ruta a la imagen del avatar
|
|
49
|
-
* @property {number} data.avatar.x - Posición X del avatar
|
|
50
|
-
* @property {number} data.avatar.y - Posición Y del avatar
|
|
51
|
-
* @property {number} data.avatar.radio - Radio para avatar circular
|
|
52
|
-
* @property {string} data.avatar.border - Color del borde del avatar
|
|
53
|
-
* @property {Object} data.title - Configuración del título
|
|
54
|
-
* @property {?string} data.title.content - Contenido del título
|
|
55
|
-
* @property {number} data.title.x - Posición X del título
|
|
56
|
-
* @property {number} data.title.y - Posición Y del título
|
|
57
|
-
* @property {number} data.title.font_size - Tamaño de fuente del título
|
|
58
|
-
* @property {string} data.title.text_color - Color del texto del título
|
|
59
|
-
* @property {Object} data.description - Configuración de la descripción
|
|
60
|
-
* @property {?string} data.description.content - Contenido de la descripción
|
|
61
|
-
* @property {number} data.description.x - Posición X de la descripción
|
|
62
|
-
* @property {number} data.description.y - Posición Y de la descripción
|
|
63
|
-
* @property {number} data.description.font_size - Tamaño de fuente de la descripción
|
|
64
|
-
* @property {string} data.description.text_color - Color del texto de la descripción
|
|
65
|
-
*/
|
|
42
|
+
* @property {number} data.width - Ancho actual de la imagen
|
|
43
|
+
* @property {number} data.height - Alto actual de la imagen
|
|
44
|
+
* @property {Object} data.background - Configuración del fondo
|
|
45
|
+
* @property {'color'|'image'} data.background.type - Tipo de fondo
|
|
46
|
+
* @property {string} data.background.value - Color o ruta de imagen
|
|
47
|
+
* @property {Object} data.avatar - Configuración del avatar
|
|
48
|
+
* @property {?string} data.avatar.source - Ruta a la imagen del avatar
|
|
49
|
+
* @property {number} data.avatar.x - Posición X del avatar
|
|
50
|
+
* @property {number} data.avatar.y - Posición Y del avatar
|
|
51
|
+
* @property {number} data.avatar.radio - Radio para avatar circular
|
|
52
|
+
* @property {string} data.avatar.border - Color del borde del avatar
|
|
53
|
+
* @property {Object} data.title - Configuración del título
|
|
54
|
+
* @property {?string} data.title.content - Contenido del título
|
|
55
|
+
* @property {number} data.title.x - Posición X del título
|
|
56
|
+
* @property {number} data.title.y - Posición Y del título
|
|
57
|
+
* @property {number} data.title.font_size - Tamaño de fuente del título
|
|
58
|
+
* @property {string} data.title.text_color - Color del texto del título
|
|
59
|
+
* @property {Object} data.description - Configuración de la descripción
|
|
60
|
+
* @property {?string} data.description.content - Contenido de la descripción
|
|
61
|
+
* @property {number} data.description.x - Posición X de la descripción
|
|
62
|
+
* @property {number} data.description.y - Posición Y de la descripción
|
|
63
|
+
* @property {number} data.description.font_size - Tamaño de fuente de la descripción
|
|
64
|
+
* @property {string} data.description.text_color - Color del texto de la descripción
|
|
65
|
+
*/
|
|
66
66
|
this.data = {
|
|
67
67
|
font: 'arial',
|
|
68
68
|
layout: 'center',
|
|
69
69
|
width: DEFAULT_WIDTH,
|
|
70
70
|
height: DEFAULT_HEIGHT,
|
|
71
|
-
background: {
|
|
72
|
-
type: 'color',
|
|
73
|
-
value: '#23272A'
|
|
74
|
-
},
|
|
75
|
-
avatar: {
|
|
76
|
-
source: null,
|
|
77
|
-
x: 448,
|
|
78
|
-
y: 80,
|
|
79
|
-
radio: 120,
|
|
80
|
-
border: "#F7F7F7"
|
|
81
|
-
},
|
|
82
|
-
title: {
|
|
83
|
-
content: "¡Bienvenido!",
|
|
84
|
-
x: 565,
|
|
85
|
-
y: 400,
|
|
86
|
-
font_size: 55,
|
|
87
|
-
text_color: '#FFFFFF'
|
|
88
|
-
},
|
|
89
|
-
description: {
|
|
90
|
-
content: 'Al mejor servidor de discord',
|
|
91
|
-
x: 590,
|
|
92
|
-
y: 450,
|
|
93
|
-
font_size: 35,
|
|
94
|
-
text_color: "#F7F7F7"
|
|
95
|
-
}
|
|
71
|
+
background: {
|
|
72
|
+
type: 'color',
|
|
73
|
+
value: '#23272A'
|
|
74
|
+
},
|
|
75
|
+
avatar: {
|
|
76
|
+
source: null,
|
|
77
|
+
x: 448,
|
|
78
|
+
y: 80,
|
|
79
|
+
radio: 120,
|
|
80
|
+
border: "#F7F7F7"
|
|
81
|
+
},
|
|
82
|
+
title: {
|
|
83
|
+
content: "¡Bienvenido!",
|
|
84
|
+
x: 565,
|
|
85
|
+
y: 400,
|
|
86
|
+
font_size: 55,
|
|
87
|
+
text_color: '#FFFFFF'
|
|
88
|
+
},
|
|
89
|
+
description: {
|
|
90
|
+
content: 'Al mejor servidor de discord',
|
|
91
|
+
x: 590,
|
|
92
|
+
y: 450,
|
|
93
|
+
font_size: 35,
|
|
94
|
+
text_color: "#F7F7F7"
|
|
95
|
+
}
|
|
96
96
|
}
|
|
97
97
|
this.setFont('arial');
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Establece la resolución de la imagen de bienvenida
|
|
102
|
-
* @param {number|'default'} width - Ancho en píxeles o 'default' para
|
|
103
|
-
* @param {number|'default'} height - Alto en píxeles o 'default' para
|
|
104
|
-
* @returns {Welcome}
|
|
105
|
-
* @throws {NekoError} Si los parámetros son inválidos
|
|
106
|
-
* @example
|
|
107
|
-
* // Establecer resolución personalizada
|
|
108
|
-
* welcome.setResolution(1920, 1080);
|
|
109
|
-
*
|
|
110
|
-
* // Restablecer a valores por defecto
|
|
111
|
-
* welcome.setResolution('default', 'default');
|
|
112
|
-
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Establece la resolución de la imagen de bienvenida
|
|
102
|
+
* @param {number|'default'} width - Ancho en píxeles o 'default' para 1140px
|
|
103
|
+
* @param {number|'default'} height - Alto en píxeles o 'default' para 520px
|
|
104
|
+
* @returns {Welcome}
|
|
105
|
+
* @throws {NekoError} Si los parámetros son inválidos
|
|
106
|
+
* @example
|
|
107
|
+
* // Establecer resolución personalizada
|
|
108
|
+
* welcome.setResolution(1920, 1080);
|
|
109
|
+
*
|
|
110
|
+
* // Restablecer a valores por defecto
|
|
111
|
+
* welcome.setResolution('default', 'default');
|
|
112
|
+
*/
|
|
113
113
|
setResolution(width, height) {
|
|
114
|
-
if (!width) throw new NekoError('ResolutionError', 'el parametro <width> no puede quedar vacio');
|
|
115
|
-
if (!height) throw new NekoError('ResolutionError', 'el parametro <height> no puede quedar vacio');
|
|
116
|
-
|
|
114
|
+
if (!width) throw new NekoError('ResolutionError', 'el parametro <width> no puede quedar vacio');
|
|
115
|
+
if (!height) throw new NekoError('ResolutionError', 'el parametro <height> no puede quedar vacio');
|
|
116
|
+
|
|
117
117
|
this.data.width = width === 'default' ? DEFAULT_WIDTH : validateDimension(width, 'width');
|
|
118
118
|
this.data.height = height === 'default' ? DEFAULT_HEIGHT : validateDimension(height, 'height');
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
return this
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -132,309 +132,309 @@ class Welcome {
|
|
|
132
132
|
this.data.layout = layout;
|
|
133
133
|
return this;
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Configura la tipografía para el texto de la imagen
|
|
138
|
-
* @param {FontNames} name - Nombre de la fuente predefinida o 'custom' para personalizada
|
|
139
|
-
* @param {string} [dir] - Ruta al archivo .ttf (requerido si name='custom')
|
|
140
|
-
* @returns {Welcome}
|
|
141
|
-
* @throws {NekoError} Si la fuente no existe o los parámetros son inválidos
|
|
142
|
-
* @example
|
|
143
|
-
* // Usar fuente predefinida
|
|
144
|
-
* welcome.setFont('calibri');
|
|
145
|
-
*
|
|
146
|
-
* // Usar fuente personalizada
|
|
147
|
-
* welcome.setFont('custom', './fonts/mi_fuente.ttf');
|
|
148
|
-
*/
|
|
149
|
-
setFont(name, dir) {
|
|
150
|
-
let ruta = null;
|
|
151
|
-
|
|
152
|
-
if (!name) throw new NekoError('FontError', 'el parametro <name> no puede quedar vacio');
|
|
153
|
-
if (typeof name != 'string') throw new NekoError('TypeError', 'el parametro <name> solo acepta tipo \'string\', recibió ' + typeof name);
|
|
154
|
-
|
|
155
|
-
switch (name) {
|
|
156
|
-
case 'arial':
|
|
157
|
-
ruta = path.join(__dirname, '../../assets/arial.ttf');
|
|
158
|
-
break;
|
|
159
|
-
case 'calibri':
|
|
160
|
-
ruta = path.join(__dirname, '../../assets/calibri.ttf');
|
|
161
|
-
break;
|
|
162
|
-
case 'custom':
|
|
163
|
-
if (!dir) throw new NekoError('FontError', 'el parametro <dir> no puede quedar vacio en un tipo de fuente customizada');
|
|
164
|
-
if (typeof dir != 'string') throw new NekoError('TypeError', 'el parametro <dir> solo acepta tipo \'string\', recibió ' + typeof dir);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Configura la tipografía para el texto de la imagen
|
|
138
|
+
* @param {FontNames} name - Nombre de la fuente predefinida o 'custom' para personalizada
|
|
139
|
+
* @param {string} [dir] - Ruta al archivo .ttf (requerido si name='custom')
|
|
140
|
+
* @returns {Welcome}
|
|
141
|
+
* @throws {NekoError} Si la fuente no existe o los parámetros son inválidos
|
|
142
|
+
* @example
|
|
143
|
+
* // Usar fuente predefinida
|
|
144
|
+
* welcome.setFont('calibri');
|
|
145
|
+
*
|
|
146
|
+
* // Usar fuente personalizada
|
|
147
|
+
* welcome.setFont('custom', './fonts/mi_fuente.ttf');
|
|
148
|
+
*/
|
|
149
|
+
setFont(name, dir) {
|
|
150
|
+
let ruta = null;
|
|
151
|
+
|
|
152
|
+
if (!name) throw new NekoError('FontError', 'el parametro <name> no puede quedar vacio');
|
|
153
|
+
if (typeof name != 'string') throw new NekoError('TypeError', 'el parametro <name> solo acepta tipo \'string\', recibió ' + typeof name);
|
|
154
|
+
|
|
155
|
+
switch (name) {
|
|
156
|
+
case 'arial':
|
|
157
|
+
ruta = path.join(__dirname, '../../assets/arial.ttf');
|
|
158
|
+
break;
|
|
159
|
+
case 'calibri':
|
|
160
|
+
ruta = path.join(__dirname, '../../assets/calibri.ttf');
|
|
161
|
+
break;
|
|
162
|
+
case 'custom':
|
|
163
|
+
if (!dir) throw new NekoError('FontError', 'el parametro <dir> no puede quedar vacio en un tipo de fuente customizada');
|
|
164
|
+
if (typeof dir != 'string') throw new NekoError('TypeError', 'el parametro <dir> solo acepta tipo \'string\', recibió ' + typeof dir);
|
|
165
165
|
if (path.extname(dir).toLowerCase() !== '.ttf') throw new NekoError('FontError', 'se necesita un archivo TTF en el parametro <dir>')
|
|
166
166
|
ruta = path.resolve(dir);
|
|
167
|
-
break;
|
|
168
|
-
default: throw new NekoError('FontError', 'el parametro <name> no acepta ningun tipo diferente a los preterminados');
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
167
|
+
break;
|
|
168
|
+
default: throw new NekoError('FontError', 'el parametro <name> no acepta ningun tipo diferente a los preterminados');
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
171
|
if (!fs.existsSync(ruta)) throw new Error('Font not found');
|
|
172
172
|
const fontKey = `${name}:${ruta}`;
|
|
173
173
|
if (!registeredFonts.has(fontKey)) {
|
|
174
174
|
Canvas.registerFont(ruta, { family: name });
|
|
175
175
|
registeredFonts.add(fontKey);
|
|
176
176
|
}
|
|
177
|
-
} catch (error) {
|
|
178
|
-
throw new NekoError('DirError', 'no se puede encontrar el directorio especificado')
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this.data.font = name
|
|
182
|
-
return this;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Agrega un título a la imagen de bienvenida
|
|
187
|
-
* @param {string} text - Texto del título
|
|
188
|
-
* @param {WelcomeTextData} data - Configuración del título
|
|
189
|
-
* @throws {NekoError} Arroja un error si parámetros son inválidos
|
|
190
|
-
* @returns {Welcome}
|
|
191
|
-
*/
|
|
192
|
-
setTitle(text, data) {
|
|
193
|
-
// Validaciones del texto
|
|
194
|
-
if (!text) {
|
|
195
|
-
throw new NekoError('TitleError', 'El parámetro <text> no puede estar vacío');
|
|
196
|
-
}
|
|
197
|
-
if (typeof text !== 'string') {
|
|
198
|
-
throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
this.data.title.content = text;
|
|
202
|
-
|
|
203
|
-
// Validaciones para las propiedades adicionales
|
|
204
|
-
if (data) {
|
|
205
|
-
// Validación para text_color
|
|
206
|
-
if (data.text_color) {
|
|
207
|
-
if (typeof data.text_color !== 'string') {
|
|
208
|
-
throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
|
|
209
|
-
}
|
|
210
|
-
if (!data.text_color.startsWith('#')) {
|
|
211
|
-
throw new NekoError('TitleError', 'El valor de <text_color> debe comenzar con \'#\'');
|
|
212
|
-
}
|
|
213
|
-
if (!hex_reg.test(data.text_color)) {
|
|
214
|
-
throw new NekoError('TitleError', 'El valor de <text_color> no es un color válido');
|
|
215
|
-
}
|
|
216
|
-
this.data.title.text_color = data.text_color;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// validacion del tamaño de texto
|
|
220
|
-
if (data.font_size) {
|
|
221
|
-
if (typeof data.font_size !== 'number') {
|
|
222
|
-
throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
|
|
223
|
-
}
|
|
224
|
-
if (data.font_size <= 0) {
|
|
225
|
-
throw new NekoError('TitleError', 'El valor de <font_size> debe ser mayor que 0');
|
|
226
|
-
}
|
|
227
|
-
this.data.title.font_size = data.font_size;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// validacion de posiciones
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw new NekoError('DirError', 'no se puede encontrar el directorio especificado')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.data.font = name
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Agrega un título a la imagen de bienvenida
|
|
187
|
+
* @param {string} text - Texto del título
|
|
188
|
+
* @param {WelcomeTextData} data - Configuración del título
|
|
189
|
+
* @throws {NekoError} Arroja un error si parámetros son inválidos
|
|
190
|
+
* @returns {Welcome}
|
|
191
|
+
*/
|
|
192
|
+
setTitle(text, data) {
|
|
193
|
+
// Validaciones del texto
|
|
194
|
+
if (!text) {
|
|
195
|
+
throw new NekoError('TitleError', 'El parámetro <text> no puede estar vacío');
|
|
196
|
+
}
|
|
197
|
+
if (typeof text !== 'string') {
|
|
198
|
+
throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.data.title.content = text;
|
|
202
|
+
|
|
203
|
+
// Validaciones para las propiedades adicionales
|
|
204
|
+
if (data) {
|
|
205
|
+
// Validación para text_color
|
|
206
|
+
if (data.text_color) {
|
|
207
|
+
if (typeof data.text_color !== 'string') {
|
|
208
|
+
throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
|
|
209
|
+
}
|
|
210
|
+
if (!data.text_color.startsWith('#')) {
|
|
211
|
+
throw new NekoError('TitleError', 'El valor de <text_color> debe comenzar con \'#\'');
|
|
212
|
+
}
|
|
213
|
+
if (!hex_reg.test(data.text_color)) {
|
|
214
|
+
throw new NekoError('TitleError', 'El valor de <text_color> no es un color válido');
|
|
215
|
+
}
|
|
216
|
+
this.data.title.text_color = data.text_color;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// validacion del tamaño de texto
|
|
220
|
+
if (data.font_size) {
|
|
221
|
+
if (typeof data.font_size !== 'number') {
|
|
222
|
+
throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
|
|
223
|
+
}
|
|
224
|
+
if (data.font_size <= 0) {
|
|
225
|
+
throw new NekoError('TitleError', 'El valor de <font_size> debe ser mayor que 0');
|
|
226
|
+
}
|
|
227
|
+
this.data.title.font_size = data.font_size;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// validacion de posiciones
|
|
231
231
|
if (data.x !== undefined) {
|
|
232
|
-
if (typeof data.x !== 'number') {
|
|
233
|
-
throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
|
|
234
|
-
}
|
|
232
|
+
if (typeof data.x !== 'number') {
|
|
233
|
+
throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
|
|
234
|
+
}
|
|
235
235
|
this.data.title.x = data.x;
|
|
236
236
|
this.data.layout = 'manual';
|
|
237
|
-
}
|
|
238
|
-
|
|
237
|
+
}
|
|
238
|
+
|
|
239
239
|
if (data.y !== undefined) {
|
|
240
|
-
if (typeof data.y !== 'number') {
|
|
241
|
-
throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
|
|
242
|
-
}
|
|
240
|
+
if (typeof data.y !== 'number') {
|
|
241
|
+
throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
|
|
242
|
+
}
|
|
243
243
|
this.data.title.y = data.y;
|
|
244
244
|
this.data.layout = 'manual';
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return this
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Agrega una descripción a la imagen de bienvenida
|
|
253
|
-
* @param {string} text - Texto de la descripción
|
|
254
|
-
* @param {WelcomeTextData} data - Configuración de la descripción
|
|
255
|
-
* @throws {NekoError} Arroja un error si parámetros son inválidos
|
|
256
|
-
* @returns {Welcome}
|
|
257
|
-
*/
|
|
258
|
-
setDescription(text, data) {
|
|
259
|
-
// Validaciones del texto
|
|
260
|
-
if (!text) {
|
|
261
|
-
throw new NekoError('DescriptionError', 'El parámetro <text> no puede estar vacío');
|
|
262
|
-
}
|
|
263
|
-
if (typeof text !== 'string') {
|
|
264
|
-
throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
this.data.description.content = text;
|
|
268
|
-
|
|
269
|
-
// Validaciones para las propiedades adicionales
|
|
270
|
-
if (data) {
|
|
271
|
-
// Validación para text_color
|
|
272
|
-
if (data.text_color) {
|
|
273
|
-
if (typeof data.text_color !== 'string') {
|
|
274
|
-
throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
|
|
275
|
-
}
|
|
276
|
-
if (!data.text_color.startsWith('#')) {
|
|
277
|
-
throw new NekoError('DescriptionError', 'El valor de <text_color> debe comenzar con \'#\'');
|
|
278
|
-
}
|
|
279
|
-
if (!hex_reg.test(data.text_color)) {
|
|
280
|
-
throw new NekoError('DescriptionError', 'El valor de <text_color> no es un color válido');
|
|
281
|
-
}
|
|
282
|
-
this.data.description.text_color = data.text_color;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// validacion del tamaño de texto
|
|
286
|
-
if (data.font_size) {
|
|
287
|
-
if (typeof data.font_size !== 'number') {
|
|
288
|
-
throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
|
|
289
|
-
}
|
|
290
|
-
if (data.font_size <= 0) {
|
|
291
|
-
throw new NekoError('DescriptionError', 'El valor de <font_size> debe ser mayor que 0');
|
|
292
|
-
}
|
|
293
|
-
this.data.description.font_size = data.font_size;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// validacion de posiciones
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return this
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Agrega una descripción a la imagen de bienvenida
|
|
253
|
+
* @param {string} text - Texto de la descripción
|
|
254
|
+
* @param {WelcomeTextData} data - Configuración de la descripción
|
|
255
|
+
* @throws {NekoError} Arroja un error si parámetros son inválidos
|
|
256
|
+
* @returns {Welcome}
|
|
257
|
+
*/
|
|
258
|
+
setDescription(text, data) {
|
|
259
|
+
// Validaciones del texto
|
|
260
|
+
if (!text) {
|
|
261
|
+
throw new NekoError('DescriptionError', 'El parámetro <text> no puede estar vacío');
|
|
262
|
+
}
|
|
263
|
+
if (typeof text !== 'string') {
|
|
264
|
+
throw new NekoError('TypeError', `El parámetro <text> solo acepta tipo 'string', recibió ${typeof text}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.data.description.content = text;
|
|
268
|
+
|
|
269
|
+
// Validaciones para las propiedades adicionales
|
|
270
|
+
if (data) {
|
|
271
|
+
// Validación para text_color
|
|
272
|
+
if (data.text_color) {
|
|
273
|
+
if (typeof data.text_color !== 'string') {
|
|
274
|
+
throw new NekoError('TypeError', `La propiedad <text_color> solo acepta tipo 'string', recibió ${typeof data.text_color}`);
|
|
275
|
+
}
|
|
276
|
+
if (!data.text_color.startsWith('#')) {
|
|
277
|
+
throw new NekoError('DescriptionError', 'El valor de <text_color> debe comenzar con \'#\'');
|
|
278
|
+
}
|
|
279
|
+
if (!hex_reg.test(data.text_color)) {
|
|
280
|
+
throw new NekoError('DescriptionError', 'El valor de <text_color> no es un color válido');
|
|
281
|
+
}
|
|
282
|
+
this.data.description.text_color = data.text_color;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// validacion del tamaño de texto
|
|
286
|
+
if (data.font_size) {
|
|
287
|
+
if (typeof data.font_size !== 'number') {
|
|
288
|
+
throw new NekoError('TypeError', `La propiedad <font_size> solo acepta tipo 'number', recibió ${typeof data.font_size}`);
|
|
289
|
+
}
|
|
290
|
+
if (data.font_size <= 0) {
|
|
291
|
+
throw new NekoError('DescriptionError', 'El valor de <font_size> debe ser mayor que 0');
|
|
292
|
+
}
|
|
293
|
+
this.data.description.font_size = data.font_size;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// validacion de posiciones
|
|
297
297
|
if (data.x !== undefined) {
|
|
298
|
-
if (typeof data.x !== 'number') {
|
|
299
|
-
throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
|
|
300
|
-
}
|
|
298
|
+
if (typeof data.x !== 'number') {
|
|
299
|
+
throw new NekoError('TypeError', `La propiedad <x> solo acepta tipo 'number', recibió ${typeof data.x}`);
|
|
300
|
+
}
|
|
301
301
|
this.data.description.x = data.x;
|
|
302
302
|
this.data.layout = 'manual';
|
|
303
|
-
}
|
|
304
|
-
|
|
303
|
+
}
|
|
304
|
+
|
|
305
305
|
if (data.y !== undefined) {
|
|
306
|
-
if (typeof data.y !== 'number') {
|
|
307
|
-
throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
|
|
308
|
-
}
|
|
306
|
+
if (typeof data.y !== 'number') {
|
|
307
|
+
throw new NekoError('TypeError', `La propiedad <y> solo acepta tipo 'number', recibió ${typeof data.y}`);
|
|
308
|
+
}
|
|
309
309
|
this.data.description.y = data.y;
|
|
310
310
|
this.data.layout = 'manual';
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return this
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Establece el fondo de la imagen de bienvenida (color o imagen)
|
|
319
|
-
* @param {'color'|'image'} type - Tipo de fondo ('color' o 'image')
|
|
320
|
-
* @param {string|Buffer} value - Color hexadecimal (ej. '#FF5733') o imagen (URL/Buffer)
|
|
321
|
-
* @returns {Welcome}
|
|
322
|
-
* @throws {NekoError} Si los parámetros son inválidos
|
|
323
|
-
* @example
|
|
324
|
-
* // Fondo con color
|
|
325
|
-
* welcome.setBackground('color', '#23272A');
|
|
326
|
-
*
|
|
327
|
-
* // Fondo con imagen desde URL
|
|
328
|
-
* welcome.setBackground('image', 'https://ejemplo.com/fondo.jpg');
|
|
329
|
-
*
|
|
330
|
-
* // Fondo con imagen desde Buffer
|
|
331
|
-
* const imageBuffer = fs.readFileSync('fondo.jpg');
|
|
332
|
-
* welcome.setBackground('image', imageBuffer);
|
|
333
|
-
*/
|
|
334
|
-
setBackground(type, value) {
|
|
335
|
-
if (!type) throw new NekoError('BackgroundError', 'El parámetro <type> no puede estar vacío');
|
|
336
|
-
if (typeof type != 'string') throw new NekoError('TypeError', `El parámetro <type> debe ser string, recibió ${typeof type}`);
|
|
337
|
-
if (!['color', 'image'].includes(type)) throw new NekoError('BackgroundError', 'El tipo de fondo debe ser "color" o "image"');
|
|
338
|
-
|
|
339
|
-
if (!value) throw new NekoError('BackgroundError', 'El parámetro <value> no puede estar vacío');
|
|
340
|
-
|
|
341
|
-
if (type == 'color') {
|
|
342
|
-
if (typeof value != 'string') throw new NekoError('TypeError', 'Para fondo de color, <data> debe ser string');
|
|
343
|
-
if (!value.startsWith('#')) throw new NekoError('BackgroundError', 'El color debe comenzar con \'#\'');
|
|
344
|
-
if (!hex_reg.test(value)) throw new NekoError('BackgroundError', 'El valor no es un color hexadecimal válido');
|
|
345
|
-
|
|
346
|
-
this.data.background = { type, value };
|
|
347
|
-
return this;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (type == 'image') {
|
|
351
|
-
if (typeof value != 'string' && !Buffer.isBuffer(value)) {
|
|
352
|
-
throw new NekoError('TypeError', 'Para fondo de imagen, <value> debe ser URL (string) o Buffer');
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (typeof value == 'string') {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return this
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Establece el fondo de la imagen de bienvenida (color o imagen)
|
|
319
|
+
* @param {'color'|'image'} type - Tipo de fondo ('color' o 'image')
|
|
320
|
+
* @param {string|Buffer} value - Color hexadecimal (ej. '#FF5733') o imagen (URL/Buffer)
|
|
321
|
+
* @returns {Welcome}
|
|
322
|
+
* @throws {NekoError} Si los parámetros son inválidos
|
|
323
|
+
* @example
|
|
324
|
+
* // Fondo con color
|
|
325
|
+
* welcome.setBackground('color', '#23272A');
|
|
326
|
+
*
|
|
327
|
+
* // Fondo con imagen desde URL
|
|
328
|
+
* welcome.setBackground('image', 'https://ejemplo.com/fondo.jpg');
|
|
329
|
+
*
|
|
330
|
+
* // Fondo con imagen desde Buffer
|
|
331
|
+
* const imageBuffer = fs.readFileSync('fondo.jpg');
|
|
332
|
+
* welcome.setBackground('image', imageBuffer);
|
|
333
|
+
*/
|
|
334
|
+
setBackground(type, value) {
|
|
335
|
+
if (!type) throw new NekoError('BackgroundError', 'El parámetro <type> no puede estar vacío');
|
|
336
|
+
if (typeof type != 'string') throw new NekoError('TypeError', `El parámetro <type> debe ser string, recibió ${typeof type}`);
|
|
337
|
+
if (!['color', 'image'].includes(type)) throw new NekoError('BackgroundError', 'El tipo de fondo debe ser "color" o "image"');
|
|
338
|
+
|
|
339
|
+
if (!value) throw new NekoError('BackgroundError', 'El parámetro <value> no puede estar vacío');
|
|
340
|
+
|
|
341
|
+
if (type == 'color') {
|
|
342
|
+
if (typeof value != 'string') throw new NekoError('TypeError', 'Para fondo de color, <data> debe ser string');
|
|
343
|
+
if (!value.startsWith('#')) throw new NekoError('BackgroundError', 'El color debe comenzar con \'#\'');
|
|
344
|
+
if (!hex_reg.test(value)) throw new NekoError('BackgroundError', 'El valor no es un color hexadecimal válido');
|
|
345
|
+
|
|
346
|
+
this.data.background = { type, value };
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (type == 'image') {
|
|
351
|
+
if (typeof value != 'string' && !Buffer.isBuffer(value)) {
|
|
352
|
+
throw new NekoError('TypeError', 'Para fondo de imagen, <value> debe ser URL (string) o Buffer');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (typeof value == 'string') {
|
|
356
356
|
parseRemoteURL(value, 'el fondo');
|
|
357
357
|
} else {
|
|
358
358
|
validateImageBuffer(value, 'el fondo');
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
this.data.background = { type, value };
|
|
362
|
-
return this;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Establece el avatar y su configuración en la imagen de bienvenida
|
|
368
|
-
* @param {string|Buffer} source - URL del avatar o Buffer de la imagen
|
|
369
|
-
* @param {WelcomeAvatarData} [data] - Configuración opcional del avatar
|
|
370
|
-
* @returns {Welcome}
|
|
371
|
-
* @throws {NekoError} Si los parámetros son inválidos
|
|
372
|
-
* @example
|
|
373
|
-
* // Configuración básica con URL
|
|
374
|
-
* welcome.setAvatar('https://example.com/avatar.jpg');
|
|
375
|
-
*
|
|
376
|
-
* // Configuración completa con Buffer
|
|
377
|
-
* const avatarBuffer = fs.readFileSync('avatar.png');
|
|
378
|
-
* welcome.setAvatar(avatarBuffer, {
|
|
379
|
-
* x: 300,
|
|
380
|
-
* y: 100,
|
|
381
|
-
* border: '#FF5733',
|
|
382
|
-
* radio: 120
|
|
383
|
-
* });
|
|
384
|
-
*/
|
|
385
|
-
setAvatar(source, data) {
|
|
386
|
-
// Validación del source
|
|
387
|
-
if (!source) {
|
|
388
|
-
throw new NekoError('AvatarError', 'El parámetro <source> no puede estar vacío');
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (typeof source != 'string' && !Buffer.isBuffer(source)) {
|
|
392
|
-
throw new NekoError('TypeError', 'El parámetro <source> debe ser string (URL) o Buffer');
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (typeof source === 'string') {
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this.data.background = { type, value };
|
|
362
|
+
return this;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Establece el avatar y su configuración en la imagen de bienvenida
|
|
368
|
+
* @param {string|Buffer} source - URL del avatar o Buffer de la imagen
|
|
369
|
+
* @param {WelcomeAvatarData} [data] - Configuración opcional del avatar
|
|
370
|
+
* @returns {Welcome}
|
|
371
|
+
* @throws {NekoError} Si los parámetros son inválidos
|
|
372
|
+
* @example
|
|
373
|
+
* // Configuración básica con URL
|
|
374
|
+
* welcome.setAvatar('https://example.com/avatar.jpg');
|
|
375
|
+
*
|
|
376
|
+
* // Configuración completa con Buffer
|
|
377
|
+
* const avatarBuffer = fs.readFileSync('avatar.png');
|
|
378
|
+
* welcome.setAvatar(avatarBuffer, {
|
|
379
|
+
* x: 300,
|
|
380
|
+
* y: 100,
|
|
381
|
+
* border: '#FF5733',
|
|
382
|
+
* radio: 120
|
|
383
|
+
* });
|
|
384
|
+
*/
|
|
385
|
+
setAvatar(source, data) {
|
|
386
|
+
// Validación del source
|
|
387
|
+
if (!source) {
|
|
388
|
+
throw new NekoError('AvatarError', 'El parámetro <source> no puede estar vacío');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (typeof source != 'string' && !Buffer.isBuffer(source)) {
|
|
392
|
+
throw new NekoError('TypeError', 'El parámetro <source> debe ser string (URL) o Buffer');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (typeof source === 'string') {
|
|
396
396
|
parseRemoteURL(source, 'el avatar');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (Buffer.isBuffer(source)) {
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (Buffer.isBuffer(source)) {
|
|
400
400
|
validateImageBuffer(source, 'el avatar');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
this.data.avatar.source = source;
|
|
404
|
-
|
|
405
|
-
if (data) {
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
this.data.avatar.source = source;
|
|
404
|
+
|
|
405
|
+
if (data) {
|
|
406
406
|
if (data.x !== undefined) {
|
|
407
|
-
if (typeof data.x != 'number') {
|
|
408
|
-
throw new NekoError('TypeError', 'La propiedad <x> debe ser un número');
|
|
409
|
-
}
|
|
410
|
-
if (data.x < 0) {
|
|
411
|
-
throw new NekoError('AvatarError', 'La posición X no puede ser negativa');
|
|
412
|
-
}
|
|
407
|
+
if (typeof data.x != 'number') {
|
|
408
|
+
throw new NekoError('TypeError', 'La propiedad <x> debe ser un número');
|
|
409
|
+
}
|
|
410
|
+
if (data.x < 0) {
|
|
411
|
+
throw new NekoError('AvatarError', 'La posición X no puede ser negativa');
|
|
412
|
+
}
|
|
413
413
|
this.data.avatar.x = data.x;
|
|
414
414
|
this.data.layout = 'manual';
|
|
415
|
-
}
|
|
416
|
-
|
|
415
|
+
}
|
|
416
|
+
|
|
417
417
|
if (data.y !== undefined) {
|
|
418
|
-
if (typeof data.y != 'number') {
|
|
419
|
-
throw new NekoError('TypeError', 'La propiedad <y> debe ser un número');
|
|
420
|
-
}
|
|
418
|
+
if (typeof data.y != 'number') {
|
|
419
|
+
throw new NekoError('TypeError', 'La propiedad <y> debe ser un número');
|
|
420
|
+
}
|
|
421
421
|
this.data.avatar.y = data.y;
|
|
422
422
|
this.data.layout = 'manual';
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (data.border) {
|
|
426
|
-
if (typeof data.border != 'string') {
|
|
427
|
-
throw new NekoError('TypeError', 'La propiedad <border> debe ser un string');
|
|
428
|
-
}
|
|
429
|
-
if (!data.border.startsWith('#')) {
|
|
430
|
-
throw new NekoError('AvatarError', 'El color del borde debe comenzar con #');
|
|
431
|
-
}
|
|
432
|
-
if (!hex_reg.test(data.border)) {
|
|
433
|
-
throw new NekoError('AvatarError', 'El color del borde no es un hexadecimal válido');
|
|
434
|
-
}
|
|
435
|
-
this.data.avatar.border = data.border;
|
|
436
|
-
}
|
|
437
|
-
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (data.border) {
|
|
426
|
+
if (typeof data.border != 'string') {
|
|
427
|
+
throw new NekoError('TypeError', 'La propiedad <border> debe ser un string');
|
|
428
|
+
}
|
|
429
|
+
if (!data.border.startsWith('#')) {
|
|
430
|
+
throw new NekoError('AvatarError', 'El color del borde debe comenzar con #');
|
|
431
|
+
}
|
|
432
|
+
if (!hex_reg.test(data.border)) {
|
|
433
|
+
throw new NekoError('AvatarError', 'El color del borde no es un hexadecimal válido');
|
|
434
|
+
}
|
|
435
|
+
this.data.avatar.border = data.border;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
438
|
if (data.radio !== undefined) {
|
|
439
439
|
if (typeof data.radio != 'number') {
|
|
440
440
|
throw new NekoError('TypeError', 'La propiedad <radio> debe ser un número');
|
|
@@ -442,71 +442,71 @@ class Welcome {
|
|
|
442
442
|
if (!Number.isFinite(data.radio) || data.radio <= 0 || data.radio > Math.min(this.data.width, this.data.height) / 2) {
|
|
443
443
|
throw new NekoError('AvatarError', 'El radio debe ser positivo y caber dentro de la imagen');
|
|
444
444
|
}
|
|
445
|
-
this.data.avatar.radio = data.radio;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return this;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Genera la imagen de bienvenida con la configuracion actual
|
|
454
|
-
* @returns {Promise<Buffer>} Buffer de la imagen generada
|
|
455
|
-
* @throws {NekoError} Si faltan datos
|
|
456
|
-
* @throws {Error} Si hay problemas al cargar imágenes
|
|
457
|
-
*/
|
|
458
|
-
async build() {
|
|
459
|
-
if (!this.data.avatar?.source) {
|
|
460
|
-
throw new NekoError('BuildError', 'No se ha podido cargar un avatar. Usa el metodo .setAvatar()')
|
|
461
|
-
}
|
|
462
|
-
if (!this.data.title?.content) {
|
|
463
|
-
throw new NekoError('BuildError', 'No se puede dar un titulo vacío. Usa el metodo .setTitle()')
|
|
464
|
-
}
|
|
465
|
-
if (!this.data.description?.content) {
|
|
466
|
-
throw new NekoError('BuildError', 'No se puede dar una descripcion vacía. Usa el metodo .setDescription()')
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
try {
|
|
470
|
-
const [bg, avatar] = await Promise.all([
|
|
445
|
+
this.data.avatar.radio = data.radio;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Genera la imagen de bienvenida con la configuracion actual
|
|
454
|
+
* @returns {Promise<Buffer>} Buffer de la imagen generada
|
|
455
|
+
* @throws {NekoError} Si faltan datos
|
|
456
|
+
* @throws {Error} Si hay problemas al cargar imágenes
|
|
457
|
+
*/
|
|
458
|
+
async build() {
|
|
459
|
+
if (!this.data.avatar?.source) {
|
|
460
|
+
throw new NekoError('BuildError', 'No se ha podido cargar un avatar. Usa el metodo .setAvatar()')
|
|
461
|
+
}
|
|
462
|
+
if (!this.data.title?.content) {
|
|
463
|
+
throw new NekoError('BuildError', 'No se puede dar un titulo vacío. Usa el metodo .setTitle()')
|
|
464
|
+
}
|
|
465
|
+
if (!this.data.description?.content) {
|
|
466
|
+
throw new NekoError('BuildError', 'No se puede dar una descripcion vacía. Usa el metodo .setDescription()')
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
const [bg, avatar] = await Promise.all([
|
|
471
471
|
this.data.background.type == 'image' ? loadImageSource(this.data.background.value, 'el fondo') : null,
|
|
472
472
|
loadImageSource(this.data.avatar.source, 'el avatar')
|
|
473
|
-
])
|
|
474
|
-
|
|
475
|
-
const canvas = Canvas.createCanvas(this.data.width, this.data.height)
|
|
476
|
-
const ctx = canvas.getContext('2d')
|
|
477
|
-
|
|
478
|
-
if (bg) {
|
|
479
|
-
let ratio = Math.max(canvas.width / bg.width, canvas.height / bg.height);
|
|
480
|
-
let width = bg.width * ratio;
|
|
481
|
-
let height = bg.height * ratio;
|
|
482
|
-
ctx.drawImage(bg, (canvas.width - width) / 2, (canvas.height - height) / 2, width, height);
|
|
483
|
-
} else {
|
|
484
|
-
ctx.fillStyle = this.data.background.value;
|
|
485
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
486
|
-
}
|
|
487
|
-
|
|
473
|
+
])
|
|
474
|
+
|
|
475
|
+
const canvas = Canvas.createCanvas(this.data.width, this.data.height)
|
|
476
|
+
const ctx = canvas.getContext('2d')
|
|
477
|
+
|
|
478
|
+
if (bg) {
|
|
479
|
+
let ratio = Math.max(canvas.width / bg.width, canvas.height / bg.height);
|
|
480
|
+
let width = bg.width * ratio;
|
|
481
|
+
let height = bg.height * ratio;
|
|
482
|
+
ctx.drawImage(bg, (canvas.width - width) / 2, (canvas.height - height) / 2, width, height);
|
|
483
|
+
} else {
|
|
484
|
+
ctx.fillStyle = this.data.background.value;
|
|
485
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
488
|
const maxTitleWidth = this.data.width * 0.8;
|
|
489
489
|
let titleFontSize = this.data.layout === 'center'
|
|
490
490
|
? Math.min(this.data.title.font_size, this.data.height * 0.14)
|
|
491
491
|
: this.data.title.font_size;
|
|
492
492
|
ctx.font = `${titleFontSize}px ${this.data.font}`;
|
|
493
|
-
|
|
494
|
-
while (ctx.measureText(this.data.title.content).width > maxTitleWidth && titleFontSize > 10) {
|
|
495
|
-
titleFontSize -= 1;
|
|
496
|
-
ctx.font = `${titleFontSize}px ${this.data.font}`;
|
|
497
|
-
}
|
|
498
|
-
|
|
493
|
+
|
|
494
|
+
while (ctx.measureText(this.data.title.content).width > maxTitleWidth && titleFontSize > 10) {
|
|
495
|
+
titleFontSize -= 1;
|
|
496
|
+
ctx.font = `${titleFontSize}px ${this.data.font}`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
499
|
const maxDescWidth = this.data.width * 0.9;
|
|
500
500
|
let descFontSize = this.data.layout === 'center'
|
|
501
501
|
? Math.min(this.data.description.font_size, this.data.height * 0.09)
|
|
502
502
|
: this.data.description.font_size;
|
|
503
503
|
ctx.font = `${descFontSize}px ${this.data.font}`;
|
|
504
|
-
|
|
505
|
-
while (ctx.measureText(this.data.description.content).width > maxDescWidth && descFontSize > 8) {
|
|
506
|
-
descFontSize -= 1;
|
|
507
|
-
ctx.font = `${descFontSize}px ${this.data.font}`;
|
|
508
|
-
}
|
|
509
|
-
|
|
504
|
+
|
|
505
|
+
while (ctx.measureText(this.data.description.content).width > maxDescWidth && descFontSize > 8) {
|
|
506
|
+
descFontSize -= 1;
|
|
507
|
+
ctx.font = `${descFontSize}px ${this.data.font}`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
510
|
const layout = this.data.layout === 'center'
|
|
511
511
|
? calculateCenteredLayout(this.data, titleFontSize, descFontSize)
|
|
512
512
|
: {
|
|
@@ -522,20 +522,20 @@ class Welcome {
|
|
|
522
522
|
const x = layout.avatarX;
|
|
523
523
|
const y = layout.avatarY;
|
|
524
524
|
const radio = layout.radio;
|
|
525
|
-
|
|
526
|
-
if (this.data.avatar.border) {
|
|
527
|
-
ctx.beginPath();
|
|
528
|
-
ctx.fillStyle = this.data.avatar.border;
|
|
529
|
-
ctx.arc(x + radio, y + radio, radio + 6, 0, 2 * Math.PI);
|
|
530
|
-
ctx.fill();
|
|
531
|
-
ctx.closePath();
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
ctx.save();
|
|
535
|
-
ctx.beginPath();
|
|
536
|
-
ctx.arc(x + radio, y + radio, radio, 0, Math.PI * 2, true);
|
|
537
|
-
ctx.closePath();
|
|
538
|
-
ctx.clip();
|
|
525
|
+
|
|
526
|
+
if (this.data.avatar.border) {
|
|
527
|
+
ctx.beginPath();
|
|
528
|
+
ctx.fillStyle = this.data.avatar.border;
|
|
529
|
+
ctx.arc(x + radio, y + radio, radio + 6, 0, 2 * Math.PI);
|
|
530
|
+
ctx.fill();
|
|
531
|
+
ctx.closePath();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
ctx.save();
|
|
535
|
+
ctx.beginPath();
|
|
536
|
+
ctx.arc(x + radio, y + radio, radio, 0, Math.PI * 2, true);
|
|
537
|
+
ctx.closePath();
|
|
538
|
+
ctx.clip();
|
|
539
539
|
ctx.drawImage(avatar, x, y, radio * 2, radio * 2);
|
|
540
540
|
ctx.restore();
|
|
541
541
|
|
|
@@ -548,15 +548,15 @@ class Welcome {
|
|
|
548
548
|
ctx.font = `${descFontSize}px ${this.data.font}`;
|
|
549
549
|
ctx.fillStyle = this.data.description.text_color;
|
|
550
550
|
ctx.fillText(this.data.description.content, layout.descriptionX, layout.descriptionY);
|
|
551
|
-
|
|
552
|
-
return canvas.toBuffer();
|
|
553
|
-
} catch (error) {
|
|
554
|
-
if(error instanceof NekoError) {
|
|
555
|
-
throw error;
|
|
556
|
-
}
|
|
557
|
-
throw new NekoError('GenerateError', 'Error al generar la imagen: '+error.message)
|
|
558
|
-
}
|
|
559
|
-
}
|
|
551
|
+
|
|
552
|
+
return canvas.toBuffer();
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if(error instanceof NekoError) {
|
|
555
|
+
throw error;
|
|
556
|
+
}
|
|
557
|
+
throw new NekoError('GenerateError', 'Error al generar la imagen: '+error.message)
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
function calculateCenteredLayout(data, titleFontSize, descFontSize) {
|
|
@@ -602,5 +602,5 @@ function validateDimension(value, name) {
|
|
|
602
602
|
}
|
|
603
603
|
return value;
|
|
604
604
|
}
|
|
605
|
-
|
|
605
|
+
|
|
606
606
|
module.exports = Welcome;
|