@getparallel/parx 0.4.4 → 0.6.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 +16 -0
- package/README.md +21 -6
- package/dist/actualizar.js +2 -1
- package/dist/agente.js +82 -2
- package/dist/api.js +32 -0
- package/dist/cli.js +385 -36
- package/dist/comandos.js +71 -0
- package/dist/config.js +4 -0
- package/dist/consejos.js +2 -1
- package/dist/estimacion.js +118 -0
- package/dist/firmas.js +55 -0
- package/dist/marca.js +56 -19
- package/dist/menciones.js +85 -0
- package/dist/par.js +355 -0
- package/dist/remoto.js +284 -0
- package/dist/tar.js +99 -0
- package/dist/textos.js +95 -6
- package/dist/tui.js +132 -15
- package/dist/ui.js +35 -2
- package/dist/versiones.js +341 -0
- package/package.json +1 -1
package/dist/par.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
export const LADO = 16;
|
|
2
|
+
export const TONOS = {
|
|
3
|
+
300: { cuerpo: '#ae9bff', sombra: '#8b70ff', detalle: '#ddd3ff' },
|
|
4
|
+
400: { cuerpo: '#8b70ff', sombra: '#6d4aff', detalle: '#ae9bff' },
|
|
5
|
+
500: { cuerpo: '#7b5cff', sombra: '#5a36e6', detalle: '#ae9bff' },
|
|
6
|
+
600: { cuerpo: '#6d4aff', sombra: '#5a36e6', detalle: '#ae9bff' },
|
|
7
|
+
700: { cuerpo: '#5a36e6', sombra: '#4626ba', detalle: '#8b70ff' },
|
|
8
|
+
};
|
|
9
|
+
export const ORDEN_DE_TONOS = [600, 400, 700, 500, 300];
|
|
10
|
+
export const TINTA = '#15131f';
|
|
11
|
+
export const CLARO = '#d3cde8';
|
|
12
|
+
export const ORO = '#f2b632';
|
|
13
|
+
function huella(s) {
|
|
14
|
+
let h = 2166136261;
|
|
15
|
+
for (let i = 0; i < s.length; i++) {
|
|
16
|
+
h ^= s.charCodeAt(i);
|
|
17
|
+
h = Math.imul(h, 16777619);
|
|
18
|
+
}
|
|
19
|
+
return h >>> 0;
|
|
20
|
+
}
|
|
21
|
+
export function tonoDe(semilla) {
|
|
22
|
+
if (!semilla)
|
|
23
|
+
return 600;
|
|
24
|
+
return ORDEN_DE_TONOS[huella(semilla) % ORDEN_DE_TONOS.length];
|
|
25
|
+
}
|
|
26
|
+
export function tonosDelEquipo(semillas) {
|
|
27
|
+
const usados = new Set();
|
|
28
|
+
return semillas.map((s) => {
|
|
29
|
+
let t = tonoDe(s);
|
|
30
|
+
if (usados.size < ORDEN_DE_TONOS.length) {
|
|
31
|
+
let i = ORDEN_DE_TONOS.indexOf(t);
|
|
32
|
+
while (usados.has(t)) {
|
|
33
|
+
i = (i + 1) % ORDEN_DE_TONOS.length;
|
|
34
|
+
t = ORDEN_DE_TONOS[i];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
usados.add(t);
|
|
38
|
+
return t;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function colorDe(c, tono) {
|
|
42
|
+
const t = TONOS[tono];
|
|
43
|
+
switch (c) {
|
|
44
|
+
case 'B':
|
|
45
|
+
return t.cuerpo;
|
|
46
|
+
case 'D':
|
|
47
|
+
return t.sombra;
|
|
48
|
+
case 'A':
|
|
49
|
+
return t.detalle;
|
|
50
|
+
case 'E':
|
|
51
|
+
return TINTA;
|
|
52
|
+
case 'W':
|
|
53
|
+
return CLARO;
|
|
54
|
+
case 'Y':
|
|
55
|
+
return ORO;
|
|
56
|
+
default:
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const INICIO = [7, 11];
|
|
61
|
+
const ARRIBA = 2;
|
|
62
|
+
const FILAS = 12;
|
|
63
|
+
const FILA_OJO = ARRIBA + 3;
|
|
64
|
+
function pon(m, x, y, c) {
|
|
65
|
+
if (x >= 0 && x < LADO && y >= 0 && y < LADO)
|
|
66
|
+
m[y][x] = c;
|
|
67
|
+
}
|
|
68
|
+
export function matrizDe(c, accesorio) {
|
|
69
|
+
const m = Array.from({ length: LADO }, () => Array(LADO).fill('.'));
|
|
70
|
+
const ojos = Array.isArray(c.ojos) ? c.ojos : [c.ojos ?? 'abierto', c.ojos ?? 'abierto'];
|
|
71
|
+
[c.izq, c.der].forEach((b, k) => {
|
|
72
|
+
const dx = b?.dx ?? 0;
|
|
73
|
+
const dy = b?.dy ?? 0;
|
|
74
|
+
const estira = b?.estira ?? 0;
|
|
75
|
+
const inicio = INICIO[k];
|
|
76
|
+
const xDe = (y) => inicio - Math.floor((y - ARRIBA) / 2) + dx;
|
|
77
|
+
for (let y = ARRIBA - estira; y <= ARRIBA + FILAS; y++) {
|
|
78
|
+
const color = y === ARRIBA + FILAS ? 'D' : 'B';
|
|
79
|
+
for (let i = 0; i < 3; i++)
|
|
80
|
+
pon(m, xDe(y) + i, y + dy, color);
|
|
81
|
+
}
|
|
82
|
+
const filaOjo = FILA_OJO - estira;
|
|
83
|
+
const fy = filaOjo + dy;
|
|
84
|
+
const cx = xDe(filaOjo) + 1;
|
|
85
|
+
const ojo = ojos[k];
|
|
86
|
+
switch (ojo) {
|
|
87
|
+
case 'abierto':
|
|
88
|
+
pon(m, cx, fy, 'E');
|
|
89
|
+
break;
|
|
90
|
+
case 'cerrado':
|
|
91
|
+
pon(m, cx - 1, fy, 'D');
|
|
92
|
+
pon(m, cx, fy, 'D');
|
|
93
|
+
pon(m, cx + 1, fy, 'D');
|
|
94
|
+
break;
|
|
95
|
+
case 'feliz':
|
|
96
|
+
pon(m, cx, fy - 1, 'E');
|
|
97
|
+
pon(m, cx - 1, fy, 'E');
|
|
98
|
+
pon(m, cx + 1, fy, 'E');
|
|
99
|
+
break;
|
|
100
|
+
case 'grande':
|
|
101
|
+
pon(m, cx, fy - 1, 'E');
|
|
102
|
+
pon(m, cx, fy, 'E');
|
|
103
|
+
break;
|
|
104
|
+
case 'x':
|
|
105
|
+
pon(m, cx - 1, fy - 1, 'E');
|
|
106
|
+
pon(m, cx + 1, fy - 1, 'E');
|
|
107
|
+
pon(m, cx, fy, 'E');
|
|
108
|
+
pon(m, cx - 1, fy + 1, 'E');
|
|
109
|
+
pon(m, cx + 1, fy + 1, 'E');
|
|
110
|
+
break;
|
|
111
|
+
case 'abajo':
|
|
112
|
+
pon(m, cx, fy + 1, 'E');
|
|
113
|
+
break;
|
|
114
|
+
case 'arriba':
|
|
115
|
+
pon(m, cx, fy - 1, 'E');
|
|
116
|
+
break;
|
|
117
|
+
case 'izquierda':
|
|
118
|
+
pon(m, cx - 1, fy, 'E');
|
|
119
|
+
break;
|
|
120
|
+
case 'derecha':
|
|
121
|
+
pon(m, cx + 1, fy, 'E');
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (accesorio) {
|
|
126
|
+
for (const [x, y, ch] of PIEZAS[accesorio]) {
|
|
127
|
+
const b = x <= 9 ? c.izq : c.der;
|
|
128
|
+
pon(m, x + (b?.dx ?? 0), y + (b?.dy ?? 0) - (b?.estira ?? 0), ch);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const p of c.extra ?? [])
|
|
132
|
+
pon(m, p[0], p[1], p[2] ?? 'A');
|
|
133
|
+
return m.map((f) => f.join(''));
|
|
134
|
+
}
|
|
135
|
+
const FILAS_DE_ACCESORIOS = {
|
|
136
|
+
gorra: { 0: '...........DDD..', 1: '...........DDDDD' },
|
|
137
|
+
copa: { 0: '.......EAE......', 1: '......EEEEE.....' },
|
|
138
|
+
astronauta: { 0: '........WWWWW...', 1: '......WW.....WW.', 2: '......W.......W.', 3: '......W.......W.' },
|
|
139
|
+
corona: { 0: '...........Y.Y..', 1: '...........YYY..' },
|
|
140
|
+
gafas_sol: { 4: '......WEE.WEE...', 5: '......EEEEEEE...' },
|
|
141
|
+
gafas: { 4: '......YYY.YYY...', 5: '......Y.YYY.Y...', 6: '......YYY.YYY...' },
|
|
142
|
+
bigote: { 7: '.......EEE......' },
|
|
143
|
+
pajarita: { 9: '.....EEAEE......' },
|
|
144
|
+
idea: { 0: '.......YYY......', 1: '......A.Y.A.....' },
|
|
145
|
+
gato: { 0: '...........B.B..', 1: '...........BBB..' },
|
|
146
|
+
fiesta: { 0: '........Y.......', 1: '.......YEY......' },
|
|
147
|
+
auriculares: { 0: '............E...', 1: '...........E.E..', 3: '..........E....E', 4: '..........E....E' },
|
|
148
|
+
cocinero: { 0: '......WWWWW.....', 1: '.......WWW......' },
|
|
149
|
+
pirata: { 4: '......EEE.......', 5: '......EEE.......' },
|
|
150
|
+
bufanda: { 10: '...YYY.YYY......', 11: '........YY......' },
|
|
151
|
+
antena: { 0: '............Y...', 1: '............D...' },
|
|
152
|
+
sombrilla: { 0: '.......DDD......', 1: '......DDDDD.....' },
|
|
153
|
+
nube: { 0: '.......WW.......', 1: '......WWWW......', 2: '.....A..........', 3: '....A...........', 4: '.....A..........' },
|
|
154
|
+
halo: { 0: '.......YYY......' },
|
|
155
|
+
pelo: { 0: '......B.B.B.....', 1: '......BBBBB.....' },
|
|
156
|
+
};
|
|
157
|
+
export const LISTA_DE_ACCESORIOS = Object.keys(FILAS_DE_ACCESORIOS);
|
|
158
|
+
const PIEZAS = Object.fromEntries(LISTA_DE_ACCESORIOS.map((id) => [
|
|
159
|
+
id,
|
|
160
|
+
Object.entries(FILAS_DE_ACCESORIOS[id]).flatMap(([y, fila]) => [...fila].flatMap((ch, x) => (ch === '.' ? [] : [[x, Number(y), ch]]))),
|
|
161
|
+
]));
|
|
162
|
+
export function accesorioDe(semilla) {
|
|
163
|
+
if (!semilla)
|
|
164
|
+
return null;
|
|
165
|
+
return LISTA_DE_ACCESORIOS[huella(`${semilla}·accesorio`) % LISTA_DE_ACCESORIOS.length];
|
|
166
|
+
}
|
|
167
|
+
export function accesoriosDelEquipo(semillas) {
|
|
168
|
+
const usados = new Set();
|
|
169
|
+
return semillas.map((s) => {
|
|
170
|
+
let a = accesorioDe(s || 'parallel');
|
|
171
|
+
if (usados.size < LISTA_DE_ACCESORIOS.length) {
|
|
172
|
+
let i = LISTA_DE_ACCESORIOS.indexOf(a);
|
|
173
|
+
while (usados.has(a)) {
|
|
174
|
+
i = (i + 1) % LISTA_DE_ACCESORIOS.length;
|
|
175
|
+
a = LISTA_DE_ACCESORIOS[i];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
usados.add(a);
|
|
179
|
+
return a;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const P = (puntos, dx = 0, dy = 0, c = 'A') => puntos.map(([x, y]) => [x + dx, y + dy, c]);
|
|
183
|
+
const INTERROGACION = [[1, 0], [2, 0], [3, 1], [2, 2], [2, 4]];
|
|
184
|
+
const EXCLAMACION = [[2, 0], [2, 1], [2, 2], [2, 4]];
|
|
185
|
+
const TIC = [[0, 2], [1, 3], [2, 2], [3, 1], [4, 0]];
|
|
186
|
+
const CORAZON = [[1, 0], [3, 0], [0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [1, 2], [2, 2], [3, 2], [2, 3]];
|
|
187
|
+
const ZETA = [[0, 0], [1, 0], [2, 0], [1, 1], [0, 2], [1, 2], [2, 2]];
|
|
188
|
+
const NOTA = [[1, 0], [2, 0], [1, 1], [1, 2], [0, 3], [1, 3]];
|
|
189
|
+
export const ANIMACIONES = {
|
|
190
|
+
reposo: [{ ms: 1500 }, { ojos: 'cerrado', ms: 120 }, { ms: 1800 }, { ojos: 'derecha', ms: 700 }, { ms: 500 }, { izq: { dy: 1 }, der: { dy: 1 }, ms: 400 }, { ms: 900 }],
|
|
191
|
+
saludo: [
|
|
192
|
+
{ ojos: 'feliz', ms: 200 },
|
|
193
|
+
{ ojos: 'feliz', der: { dx: 1, dy: -1 }, ms: 180 },
|
|
194
|
+
{ ojos: 'feliz', ms: 180 },
|
|
195
|
+
{ ojos: 'feliz', der: { dx: 1, dy: -1 }, ms: 180 },
|
|
196
|
+
{ ojos: 'feliz', ms: 600 },
|
|
197
|
+
],
|
|
198
|
+
pensando: [
|
|
199
|
+
{ ojos: 'arriba', ms: 350 },
|
|
200
|
+
{ ojos: 'arriba', extra: P([[2, 1]]), ms: 350 },
|
|
201
|
+
{ ojos: 'arriba', extra: P([[2, 1], [4, 1]]), ms: 350 },
|
|
202
|
+
{ ojos: 'arriba', extra: P([[2, 1], [4, 1], [6, 0]]), ms: 600 },
|
|
203
|
+
{ ojos: 'cerrado', extra: P([[2, 1], [4, 1], [6, 0]]), ms: 120 },
|
|
204
|
+
],
|
|
205
|
+
trabajando: [
|
|
206
|
+
{ izq: { dy: -1 }, extra: P([[1, 9], [15, 3]]), ms: 170 },
|
|
207
|
+
{ der: { dy: -1 }, extra: P([[0, 7], [15, 6]]), ms: 170 },
|
|
208
|
+
],
|
|
209
|
+
escribiendo: [
|
|
210
|
+
{ ojos: 'abajo', extra: P([[3, 15]]), ms: 220 },
|
|
211
|
+
{ ojos: 'abajo', extra: P([[3, 15], [5, 15]]), ms: 220 },
|
|
212
|
+
{ ojos: 'abajo', extra: P([[3, 15], [5, 15], [7, 15]]), ms: 220 },
|
|
213
|
+
{ ojos: 'abajo', ms: 220 },
|
|
214
|
+
],
|
|
215
|
+
buscando: [{ ojos: 'izquierda', ms: 380 }, { ms: 200 }, { ojos: 'derecha', ms: 380 }, { ms: 200 }],
|
|
216
|
+
esperando: [{ ms: 450 }, { extra: P(INTERROGACION, 1, 1), ms: 650 }, { extra: P(INTERROGACION, 1, 1), ojos: 'cerrado', ms: 120 }, { extra: P(INTERROGACION, 1, 1), ms: 650 }],
|
|
217
|
+
hecho: [
|
|
218
|
+
{ ms: 200 },
|
|
219
|
+
{ izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 140 },
|
|
220
|
+
{ izq: { dy: -2 }, der: { dy: -2 }, ojos: 'feliz', extra: P(TIC, 1, 1), ms: 240 },
|
|
221
|
+
{ izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', extra: P(TIC, 1, 1), ms: 140 },
|
|
222
|
+
{ ojos: 'feliz', extra: P(TIC, 1, 1), ms: 900 },
|
|
223
|
+
],
|
|
224
|
+
error: [
|
|
225
|
+
{ ojos: 'x', izq: { dx: -1 }, der: { dx: -1 }, ms: 90 },
|
|
226
|
+
{ ojos: 'x', izq: { dx: 1 }, der: { dx: 1 }, ms: 90 },
|
|
227
|
+
{ ojos: 'x', izq: { dx: -1 }, der: { dx: -1 }, ms: 90 },
|
|
228
|
+
{ ojos: 'x', izq: { dx: 1 }, der: { dx: 1 }, ms: 90 },
|
|
229
|
+
{ ojos: 'x', ms: 900 },
|
|
230
|
+
],
|
|
231
|
+
dormido: [
|
|
232
|
+
{ ojos: 'cerrado', extra: P(ZETA, 1, 6), ms: 600 },
|
|
233
|
+
{ ojos: 'cerrado', izq: { dy: 1 }, der: { dy: 1 }, extra: P(ZETA, 1, 3), ms: 600 },
|
|
234
|
+
{ ojos: 'cerrado', extra: P(ZETA, 2, 0), ms: 600 },
|
|
235
|
+
{ ojos: 'cerrado', izq: { dy: 1 }, der: { dy: 1 }, ms: 600 },
|
|
236
|
+
],
|
|
237
|
+
estirarse: [
|
|
238
|
+
{ ms: 300 },
|
|
239
|
+
{ izq: { estira: 1 }, der: { estira: 1 }, ojos: 'cerrado', ms: 220 },
|
|
240
|
+
{ izq: { estira: 2 }, der: { estira: 2 }, ojos: 'cerrado', ms: 500 },
|
|
241
|
+
{ izq: { estira: 1 }, der: { estira: 1 }, ojos: 'cerrado', ms: 200 },
|
|
242
|
+
{ ojos: 'feliz', ms: 600 },
|
|
243
|
+
],
|
|
244
|
+
salto: [
|
|
245
|
+
{ izq: { dy: 1 }, der: { dy: 1 }, ms: 160 },
|
|
246
|
+
{ izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 120 },
|
|
247
|
+
{ izq: { dy: -2 }, der: { dy: -2 }, ojos: 'feliz', ms: 220 },
|
|
248
|
+
{ izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 120 },
|
|
249
|
+
{ izq: { dy: 1 }, der: { dy: 1 }, ms: 140 },
|
|
250
|
+
{ ms: 500 },
|
|
251
|
+
],
|
|
252
|
+
baile: [
|
|
253
|
+
{ izq: { dy: -1, dx: -1 }, der: { dx: -1 }, ojos: 'feliz', extra: P(NOTA, 0, 1), ms: 220 },
|
|
254
|
+
{ izq: { dx: 1 }, der: { dy: -1, dx: 1 }, ojos: 'feliz', ms: 220 },
|
|
255
|
+
{ izq: { dy: -1, dx: -1 }, der: { dx: -1 }, ojos: 'feliz', ms: 220 },
|
|
256
|
+
{ izq: { dx: 1 }, der: { dy: -1, dx: 1 }, ojos: 'feliz', extra: P(NOTA, 0, 0), ms: 220 },
|
|
257
|
+
],
|
|
258
|
+
guino: [{ ms: 600 }, { ojos: ['abierto', 'cerrado'], ms: 260 }, { ojos: 'feliz', ms: 700 }],
|
|
259
|
+
corazon: [
|
|
260
|
+
{ ojos: 'feliz', extra: P(CORAZON, 0, 3), ms: 260 },
|
|
261
|
+
{ ojos: 'feliz', extra: P(CORAZON, 0, 2), ms: 260 },
|
|
262
|
+
{ ojos: 'feliz', extra: P(CORAZON, 0, 1), ms: 260 },
|
|
263
|
+
{ ojos: 'feliz', extra: P(CORAZON, 0, 0), ms: 700 },
|
|
264
|
+
],
|
|
265
|
+
fiesta: [
|
|
266
|
+
{ ojos: 'feliz', izq: { dy: -1 }, der: { dy: -1 }, extra: P([[1, 2], [4, 0], [15, 1], [14, 9], [0, 11]]), ms: 200 },
|
|
267
|
+
{ ojos: 'feliz', extra: P([[2, 4], [5, 1], [14, 3], [15, 11], [1, 13]]), ms: 200 },
|
|
268
|
+
{ ojos: 'feliz', izq: { dy: -1 }, der: { dy: -1 }, extra: P([[0, 6], [3, 0], [15, 5], [13, 13], [2, 15]]), ms: 200 },
|
|
269
|
+
{ ojos: 'feliz', extra: P([[1, 8], [6, 0], [14, 7], [15, 14], [0, 1]]), ms: 200 },
|
|
270
|
+
],
|
|
271
|
+
musica: [
|
|
272
|
+
{ ojos: 'cerrado', izq: { dx: -1 }, der: { dx: -1 }, extra: P(NOTA, 1, 5), ms: 380 },
|
|
273
|
+
{ ojos: 'cerrado', extra: P(NOTA, 1, 3), ms: 380 },
|
|
274
|
+
{ ojos: 'cerrado', izq: { dx: 1 }, der: { dx: 1 }, extra: P(NOTA, 2, 1), ms: 380 },
|
|
275
|
+
{ ojos: 'cerrado', extra: P(NOTA, 2, 0), ms: 380 },
|
|
276
|
+
],
|
|
277
|
+
mirar: [{ ojos: 'izquierda', ms: 600 }, { ms: 250 }, { ojos: 'derecha', ms: 600 }, { ojos: 'arriba', ms: 500 }, { ms: 500 }],
|
|
278
|
+
sorpresa: [{ ms: 300 }, { ojos: 'grande', izq: { dy: -1 }, der: { dy: -1 }, extra: P(EXCLAMACION, 1, 1), ms: 200 }, { ojos: 'grande', extra: P(EXCLAMACION, 1, 1), ms: 900 }],
|
|
279
|
+
asomarse: [
|
|
280
|
+
{ izq: { dy: 12 }, der: { dy: 12 }, ms: 500 },
|
|
281
|
+
{ izq: { dy: 8 }, der: { dy: 8 }, ms: 140 },
|
|
282
|
+
{ izq: { dy: 5 }, der: { dy: 5 }, ms: 140 },
|
|
283
|
+
{ izq: { dy: 3 }, der: { dy: 3 }, ms: 900 },
|
|
284
|
+
{ izq: { dy: 3 }, der: { dy: 3 }, ojos: 'cerrado', ms: 120 },
|
|
285
|
+
{ izq: { dy: 3 }, der: { dy: 3 }, ojos: 'derecha', ms: 700 },
|
|
286
|
+
{ izq: { dy: 6 }, der: { dy: 6 }, ms: 140 },
|
|
287
|
+
{ izq: { dy: 12 }, der: { dy: 12 }, ms: 700 },
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
export const LISTA_DE_ANIMACIONES = Object.keys(ANIMACIONES);
|
|
291
|
+
export const DEL_TRABAJO = ['saludo', 'pensando', 'trabajando', 'escribiendo', 'buscando', 'esperando', 'hecho', 'error'];
|
|
292
|
+
export function animacionDelEstado(estado) {
|
|
293
|
+
switch (estado) {
|
|
294
|
+
case 'trabajando':
|
|
295
|
+
return 'trabajando';
|
|
296
|
+
case 'espera':
|
|
297
|
+
return 'esperando';
|
|
298
|
+
case 'parado':
|
|
299
|
+
return 'error';
|
|
300
|
+
case 'hecho':
|
|
301
|
+
return 'hecho';
|
|
302
|
+
default:
|
|
303
|
+
return 'reposo';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const cache = new Map();
|
|
307
|
+
export function fotogramasDe(a, accesorio) {
|
|
308
|
+
const clave = `${a}|${accesorio ?? ''}`;
|
|
309
|
+
let f = cache.get(clave);
|
|
310
|
+
if (!f) {
|
|
311
|
+
f = ANIMACIONES[a].map((c) => ({ matriz: matrizDe(c, accesorio), ms: c.ms }));
|
|
312
|
+
cache.set(clave, f);
|
|
313
|
+
}
|
|
314
|
+
return f;
|
|
315
|
+
}
|
|
316
|
+
export function duracionDe(a) {
|
|
317
|
+
return fotogramasDe(a).reduce((s, f) => s + f.ms, 0);
|
|
318
|
+
}
|
|
319
|
+
export function fotogramaEn(a, t, accesorio) {
|
|
320
|
+
const fs = fotogramasDe(a, accesorio);
|
|
321
|
+
const total = duracionDe(a);
|
|
322
|
+
let r = ((t % total) + total) % total;
|
|
323
|
+
for (const f of fs) {
|
|
324
|
+
if (r < f.ms)
|
|
325
|
+
return f;
|
|
326
|
+
r -= f.ms;
|
|
327
|
+
}
|
|
328
|
+
return fs[fs.length - 1];
|
|
329
|
+
}
|
|
330
|
+
export function rectangulosDe(matriz, tono) {
|
|
331
|
+
const out = [];
|
|
332
|
+
matriz.forEach((fila, y) => {
|
|
333
|
+
let x = 0;
|
|
334
|
+
while (x < LADO) {
|
|
335
|
+
const c = fila[x];
|
|
336
|
+
const color = colorDe(c, tono);
|
|
337
|
+
if (!color) {
|
|
338
|
+
x++;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
let fin = x + 1;
|
|
342
|
+
while (fin < LADO && fila[fin] === c)
|
|
343
|
+
fin++;
|
|
344
|
+
out.push({ x, y, ancho: fin - x, color });
|
|
345
|
+
x = fin;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
export function svgDe(matriz, tono, px = 64) {
|
|
351
|
+
const r = rectangulosDe(matriz, tono)
|
|
352
|
+
.map((q) => `<rect x="${q.x}" y="${q.y}" width="${q.ancho}" height="1" fill="${q.color}"/>`)
|
|
353
|
+
.join('');
|
|
354
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${px}" height="${px}" viewBox="0 0 16 16" shape-rendering="crispEdges">${r}</svg>`;
|
|
355
|
+
}
|
package/dist/remoto.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { abrirRemoto, cerrarRemoto, ErrorDeParallel, latidoRemoto, ordenesRemotas } from "./api.js";
|
|
5
|
+
import { sinColor } from "./marca.js";
|
|
6
|
+
export function seContestaDesdeFuera(clase) {
|
|
7
|
+
return clase !== 'credencial' && clase !== 'bypass';
|
|
8
|
+
}
|
|
9
|
+
const SECRETOS = [
|
|
10
|
+
/\b(sk|pk|rk)[-_](live|test|proj|ant)?[-_]?[A-Za-z0-9_-]{16,}/g,
|
|
11
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}/g,
|
|
12
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
13
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
14
|
+
/\bAIza[0-9A-Za-z_-]{30,}/g,
|
|
15
|
+
/\bpar_(term|key)_[A-Za-z0-9_-]{8,}/g,
|
|
16
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
|
|
17
|
+
];
|
|
18
|
+
const CON_NOMBRE = [/\b(Bearer\s+)[A-Za-z0-9._~+/-]{16,}=*/gi, /\b((?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*['"]?)[^\s'"]{6,}/gi];
|
|
19
|
+
export function taparSecretos(s) {
|
|
20
|
+
let r = s;
|
|
21
|
+
for (const re of CON_NOMBRE)
|
|
22
|
+
r = r.replace(re, (_m, nombre) => `${nombre}[hidden]`);
|
|
23
|
+
for (const re of SECRETOS)
|
|
24
|
+
r = r.replace(re, '[hidden]');
|
|
25
|
+
return r;
|
|
26
|
+
}
|
|
27
|
+
const MAX_TEXTO = 8000;
|
|
28
|
+
export function paraElMovil(v, max = MAX_TEXTO) {
|
|
29
|
+
const s = taparSecretos(sinColor(typeof v === 'string' ? v : v == null ? '' : String(v)));
|
|
30
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
31
|
+
}
|
|
32
|
+
function limpiar(datos) {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [k, v] of Object.entries(datos)) {
|
|
35
|
+
if (typeof v === 'string')
|
|
36
|
+
out[k] = paraElMovil(v);
|
|
37
|
+
else if (Array.isArray(v))
|
|
38
|
+
out[k] = v.slice(0, 60).map((x) => (typeof x === 'string' ? paraElMovil(x, 400) : x && typeof x === 'object' ? limpiar(x) : x));
|
|
39
|
+
else if (v && typeof v === 'object')
|
|
40
|
+
out[k] = limpiar(v);
|
|
41
|
+
else
|
|
42
|
+
out[k] = v;
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
const API = { abrir: abrirRemoto, latido: latidoRemoto, ordenes: ordenesRemotas, cerrar: cerrarRemoto };
|
|
47
|
+
let contadorDePreguntas = 0;
|
|
48
|
+
export class Remoto {
|
|
49
|
+
id = null;
|
|
50
|
+
url = null;
|
|
51
|
+
o;
|
|
52
|
+
api;
|
|
53
|
+
n = 0;
|
|
54
|
+
cola = [];
|
|
55
|
+
estado = 'libre';
|
|
56
|
+
tarea = null;
|
|
57
|
+
acciones = 0;
|
|
58
|
+
modo = '';
|
|
59
|
+
modelo = '';
|
|
60
|
+
activo = false;
|
|
61
|
+
enviando = false;
|
|
62
|
+
otraVez = false;
|
|
63
|
+
relojEnvio = null;
|
|
64
|
+
relojLatido = null;
|
|
65
|
+
escucha = null;
|
|
66
|
+
ultimoEnvio = 0;
|
|
67
|
+
preguntas = new Map();
|
|
68
|
+
constructor(o) {
|
|
69
|
+
this.o = o;
|
|
70
|
+
this.api = { ...API, ...o.api };
|
|
71
|
+
}
|
|
72
|
+
get encendido() {
|
|
73
|
+
return this.activo;
|
|
74
|
+
}
|
|
75
|
+
async abrir(datos) {
|
|
76
|
+
const r = await this.api.abrir(this.o.servidor, this.o.token, datos);
|
|
77
|
+
this.id = r.id;
|
|
78
|
+
this.url = r.url;
|
|
79
|
+
this.modo = datos.modo;
|
|
80
|
+
this.modelo = datos.modelo;
|
|
81
|
+
this.activo = true;
|
|
82
|
+
this.relojLatido = setInterval(() => void this.enviar(), this.o.latidoMs ?? 15_000);
|
|
83
|
+
this.relojLatido.unref?.();
|
|
84
|
+
void this.enviar();
|
|
85
|
+
void this.escuchar();
|
|
86
|
+
return r;
|
|
87
|
+
}
|
|
88
|
+
async cerrar() {
|
|
89
|
+
if (!this.activo)
|
|
90
|
+
return;
|
|
91
|
+
this.apagar();
|
|
92
|
+
if (this.id) {
|
|
93
|
+
try {
|
|
94
|
+
await this.api.cerrar(this.o.servidor, this.o.token, this.id);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
apagar() {
|
|
101
|
+
this.activo = false;
|
|
102
|
+
if (this.carpeta && !this.o.carpetaDeFotos)
|
|
103
|
+
rmSync(this.carpeta, { recursive: true, force: true });
|
|
104
|
+
if (this.relojEnvio)
|
|
105
|
+
clearTimeout(this.relojEnvio);
|
|
106
|
+
if (this.relojLatido)
|
|
107
|
+
clearInterval(this.relojLatido);
|
|
108
|
+
this.relojEnvio = this.relojLatido = null;
|
|
109
|
+
this.escucha?.abort();
|
|
110
|
+
this.escucha = null;
|
|
111
|
+
this.preguntas.clear();
|
|
112
|
+
}
|
|
113
|
+
evento(tipo, datos) {
|
|
114
|
+
if (!this.activo)
|
|
115
|
+
return;
|
|
116
|
+
this.cola.push({ n: this.n++, tipo, datos: limpiar(datos) });
|
|
117
|
+
if (!this.relojEnvio) {
|
|
118
|
+
this.relojEnvio = setTimeout(() => {
|
|
119
|
+
this.relojEnvio = null;
|
|
120
|
+
void this.enviar();
|
|
121
|
+
}, this.o.cadaMs ?? 400);
|
|
122
|
+
this.relojEnvio.unref?.();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
fijarEstado(estado, tarea) {
|
|
126
|
+
if (!this.activo)
|
|
127
|
+
return;
|
|
128
|
+
const cambia = estado !== this.estado || (tarea !== undefined && tarea !== this.tarea);
|
|
129
|
+
this.estado = estado;
|
|
130
|
+
if (tarea !== undefined)
|
|
131
|
+
this.tarea = tarea ? paraElMovil(tarea, 200) : null;
|
|
132
|
+
if (cambia)
|
|
133
|
+
void this.enviar();
|
|
134
|
+
}
|
|
135
|
+
fijarModo(modo, modelo) {
|
|
136
|
+
this.modo = modo;
|
|
137
|
+
this.modelo = modelo;
|
|
138
|
+
}
|
|
139
|
+
progreso(acciones) {
|
|
140
|
+
this.acciones = acciones;
|
|
141
|
+
}
|
|
142
|
+
preguntaRemota(texto, clase, opciones) {
|
|
143
|
+
const id = `p${Date.now().toString(36)}${(contadorDePreguntas++).toString(36)}`;
|
|
144
|
+
const fuera = seContestaDesdeFuera(clase);
|
|
145
|
+
let deFuera = null;
|
|
146
|
+
const valores = opciones.map((x) => x.valor);
|
|
147
|
+
if (this.activo && fuera)
|
|
148
|
+
deFuera = new Promise((resolver) => this.preguntas.set(id, { resolver, valores }));
|
|
149
|
+
const anterior = this.estado;
|
|
150
|
+
this.evento('pregunta', { id, texto, clase, opciones: opciones.map((x) => ({ etiqueta: x.etiqueta, valor: x.valor })), remota: fuera });
|
|
151
|
+
this.fijarEstado('pregunta');
|
|
152
|
+
return {
|
|
153
|
+
id,
|
|
154
|
+
deFuera,
|
|
155
|
+
contestada: (valor, desde) => {
|
|
156
|
+
this.preguntas.delete(id);
|
|
157
|
+
this.evento('respuesta', { id, valor, desde });
|
|
158
|
+
this.fijarEstado(anterior === 'pregunta' ? 'trabajando' : anterior);
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async enviar() {
|
|
163
|
+
if (!this.activo || !this.id)
|
|
164
|
+
return;
|
|
165
|
+
if (this.enviando) {
|
|
166
|
+
this.otraVez = true;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.enviando = true;
|
|
170
|
+
try {
|
|
171
|
+
const tanda = this.cola.slice(0, 200);
|
|
172
|
+
const r = await this.api.latido(this.o.servidor, this.o.token, this.id, {
|
|
173
|
+
estado: this.estado,
|
|
174
|
+
tarea: this.tarea,
|
|
175
|
+
acciones: this.acciones,
|
|
176
|
+
modo: this.modo,
|
|
177
|
+
modelo: this.modelo,
|
|
178
|
+
eventos: tanda,
|
|
179
|
+
});
|
|
180
|
+
this.cola.splice(0, tanda.length);
|
|
181
|
+
this.ultimoEnvio = Date.now();
|
|
182
|
+
if (r.cerrado) {
|
|
183
|
+
this.apagar();
|
|
184
|
+
this.o.alApagarse('web');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
if (this.cola.length > 2000)
|
|
190
|
+
this.cola.splice(0, this.cola.length - 2000);
|
|
191
|
+
if (e instanceof ErrorDeParallel && (e.codigo === 'sesion' || e.codigo === 'sin_acceso')) {
|
|
192
|
+
this.apagar();
|
|
193
|
+
this.o.alApagarse(e.codigo);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
this.enviando = false;
|
|
199
|
+
}
|
|
200
|
+
if (this.otraVez || (this.cola.length && !this.relojEnvio)) {
|
|
201
|
+
this.otraVez = false;
|
|
202
|
+
void this.enviar();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async escuchar() {
|
|
206
|
+
let pausa = 1000;
|
|
207
|
+
while (this.activo && this.id) {
|
|
208
|
+
this.escucha = new AbortController();
|
|
209
|
+
try {
|
|
210
|
+
const r = await this.api.ordenes(this.o.servidor, this.o.token, this.id, this.o.esperaMs ?? 25_000, this.escucha.signal);
|
|
211
|
+
pausa = 1000;
|
|
212
|
+
if (r.cerrado) {
|
|
213
|
+
this.apagar();
|
|
214
|
+
this.o.alApagarse('web');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
for (const orden of r.ordenes)
|
|
218
|
+
this.cumplir(orden);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
if (!this.activo)
|
|
222
|
+
return;
|
|
223
|
+
if (e instanceof ErrorDeParallel && (e.codigo === 'sesion' || e.codigo === 'sin_acceso')) {
|
|
224
|
+
this.apagar();
|
|
225
|
+
this.o.alApagarse(e.codigo);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
await new Promise((r) => setTimeout(r, pausa).unref?.());
|
|
229
|
+
pausa = Math.min(30_000, pausa * 2);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
cumplir(orden) {
|
|
234
|
+
if (!this.activo)
|
|
235
|
+
return;
|
|
236
|
+
if (orden.tipo === 'mensaje') {
|
|
237
|
+
const texto = typeof orden.datos.texto === 'string' ? orden.datos.texto.trim().slice(0, MAX_TEXTO) : '';
|
|
238
|
+
const rutas = this.guardarFotos(orden);
|
|
239
|
+
const conFotos = [texto || (rutas.length ? 'Look at this photo.' : ''), ...rutas.map((r) => `"${r}"`)].filter(Boolean).join(' ');
|
|
240
|
+
if (conFotos)
|
|
241
|
+
this.o.alMensaje(conFotos);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (orden.tipo === 'parar') {
|
|
245
|
+
this.o.alParar();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (orden.tipo === 'respuesta') {
|
|
249
|
+
const id = String(orden.datos.pregunta ?? '');
|
|
250
|
+
const valor = orden.datos.valor;
|
|
251
|
+
const p = this.preguntas.get(id);
|
|
252
|
+
if (!p || (valor !== 'si' && valor !== 'siempre' && valor !== 'no') || !p.valores.includes(valor))
|
|
253
|
+
return;
|
|
254
|
+
this.preguntas.delete(id);
|
|
255
|
+
p.resolver(valor);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
carpeta = null;
|
|
259
|
+
guardarFotos(orden) {
|
|
260
|
+
const fotos = Array.isArray(orden.datos.fotos) ? orden.datos.fotos.slice(0, 3) : [];
|
|
261
|
+
const rutas = [];
|
|
262
|
+
fotos.forEach((f, i) => {
|
|
263
|
+
const ext = f.mime === 'image/png' ? 'png' : f.mime === 'image/webp' ? 'webp' : f.mime === 'image/jpeg' ? 'jpg' : null;
|
|
264
|
+
if (!ext || typeof f.datos !== 'string' || !/^[A-Za-z0-9+/]+={0,2}$/.test(f.datos))
|
|
265
|
+
return;
|
|
266
|
+
const buf = Buffer.from(f.datos, 'base64');
|
|
267
|
+
if (buf.length < 16 || buf.length > 2_500_000)
|
|
268
|
+
return;
|
|
269
|
+
try {
|
|
270
|
+
this.carpeta ??= this.o.carpetaDeFotos ?? join(tmpdir(), `parx-remoto-${process.pid}`);
|
|
271
|
+
mkdirSync(this.carpeta, { recursive: true, mode: 0o700 });
|
|
272
|
+
const ruta = join(this.carpeta, `photo-${orden.id.replace(/[^a-z0-9-]/gi, '').slice(0, 12)}-${i + 1}.${ext}`);
|
|
273
|
+
writeFileSync(ruta, buf, { mode: 0o600 });
|
|
274
|
+
rutas.push(ruta);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
return rutas;
|
|
280
|
+
}
|
|
281
|
+
get msDesdeElUltimoEnvio() {
|
|
282
|
+
return this.ultimoEnvio ? Date.now() - this.ultimoEnvio : Number.POSITIVE_INFINITY;
|
|
283
|
+
}
|
|
284
|
+
}
|