command-cmd 1.0.2 → 1.0.4
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/cmd.js +1640 -16
- package/doc.md +199 -157
- package/docPTBR.md +204 -157
- package/map.md +238 -0
- package/mapPTBR.md +238 -0
- package/package.json +5 -2
package/cmd.js
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const { readFile, writeFile } = fs.promises;
|
|
5
|
+
const { resolve: resolvePath } = path;
|
|
6
|
+
|
|
7
|
+
const DEFAULT_APPS = Object.freeze({
|
|
8
|
+
spotify: {
|
|
9
|
+
launcher: { x: -2, y: 145 },
|
|
10
|
+
searchIcon: { x: 584, y: 90 }
|
|
11
|
+
},
|
|
12
|
+
chrome: {
|
|
13
|
+
launcher: { x: 20, y: 219 },
|
|
14
|
+
searchIcon: { x: 546, y: 87 }
|
|
15
|
+
},
|
|
16
|
+
youtube: {
|
|
17
|
+
launcher: { x: 20, y: 225 },
|
|
18
|
+
searchIcon: { x: 575, y: 144 }
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const ACTION_ALIASES = Object.freeze({
|
|
23
|
+
skip_ads: 'skip_ads',
|
|
24
|
+
skipads: 'skip_ads',
|
|
25
|
+
pular_ads: 'skip_ads',
|
|
26
|
+
pularads: 'skip_ads',
|
|
27
|
+
click: 'click',
|
|
28
|
+
clicar: 'click',
|
|
29
|
+
double_click: 'double_click',
|
|
30
|
+
doubleclick: 'double_click',
|
|
31
|
+
clique_duplo: 'double_click',
|
|
32
|
+
cliqueduplo: 'double_click',
|
|
33
|
+
scroll: 'scroll',
|
|
34
|
+
rolar: 'scroll',
|
|
35
|
+
move_sequence: 'move_sequence',
|
|
36
|
+
movesequence: 'move_sequence',
|
|
37
|
+
mover_sequencia: 'move_sequence',
|
|
38
|
+
moversequencia: 'move_sequence',
|
|
39
|
+
sequencia_movimento: 'move_sequence',
|
|
40
|
+
sequence_move: 'move_sequence',
|
|
41
|
+
open_app: 'open_app',
|
|
42
|
+
openapp: 'open_app',
|
|
43
|
+
abrir_app: 'open_app',
|
|
44
|
+
abrirapp: 'open_app',
|
|
45
|
+
close_app: 'close_app',
|
|
46
|
+
closeapp: 'close_app',
|
|
47
|
+
fechar_app: 'close_app',
|
|
48
|
+
fecharapp: 'close_app'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const BUTTON_ALIASES = Object.freeze({
|
|
52
|
+
pularads: 'pular_ads',
|
|
53
|
+
pular_ads: 'pular_ads',
|
|
54
|
+
pular_anuncio: 'pular_ads',
|
|
55
|
+
pular_anuncios: 'pular_ads',
|
|
56
|
+
skip_ads: 'pular_ads',
|
|
57
|
+
skipad: 'pular_ads',
|
|
58
|
+
search: 'barra_pesquisa',
|
|
59
|
+
searchbar: 'barra_pesquisa',
|
|
60
|
+
search_bar: 'barra_pesquisa',
|
|
61
|
+
barra_pesquisa: 'barra_pesquisa',
|
|
62
|
+
barrapesquisa: 'barra_pesquisa',
|
|
63
|
+
fechar: 'fechar',
|
|
64
|
+
close: 'fechar',
|
|
65
|
+
close_app: 'fechar'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const APP_ALIASES = Object.freeze({
|
|
69
|
+
google: 'chrome',
|
|
70
|
+
google_chrome: 'chrome'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const APP_STATE = Object.freeze({
|
|
74
|
+
OPEN: 'aberto',
|
|
75
|
+
CLOSED: 'fechado'
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const CLICK_MODE = Object.freeze({
|
|
79
|
+
NONE: 'none',
|
|
80
|
+
BETWEEN: 'between',
|
|
81
|
+
EACH: 'each',
|
|
82
|
+
FIRST: 'first',
|
|
83
|
+
LAST: 'last'
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let robotCache;
|
|
87
|
+
|
|
1
88
|
export function cmd(message) {
|
|
2
89
|
if (typeof message !== 'string') {
|
|
3
90
|
return [];
|
|
@@ -11,10 +98,17 @@ export function cmd(message) {
|
|
|
11
98
|
const inside = match[1].trim();
|
|
12
99
|
if (!inside) continue;
|
|
13
100
|
|
|
14
|
-
const
|
|
101
|
+
const command = {
|
|
15
102
|
type: undefined,
|
|
16
103
|
command: undefined,
|
|
17
104
|
extra: undefined,
|
|
105
|
+
button: undefined,
|
|
106
|
+
points: undefined,
|
|
107
|
+
path: undefined,
|
|
108
|
+
interval: undefined,
|
|
109
|
+
clickDelay: undefined,
|
|
110
|
+
click: undefined,
|
|
111
|
+
doubleClick: undefined,
|
|
18
112
|
seq: undefined
|
|
19
113
|
};
|
|
20
114
|
|
|
@@ -24,6 +118,13 @@ export function cmd(message) {
|
|
|
24
118
|
const part = rawPart.trim();
|
|
25
119
|
if (!part) continue;
|
|
26
120
|
|
|
121
|
+
if (part.indexOf(':') === -1) {
|
|
122
|
+
if (!command.type) {
|
|
123
|
+
command.type = part;
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
27
128
|
const [rawKey, ...rest] = part.split(':');
|
|
28
129
|
if (!rawKey || rest.length === 0) continue;
|
|
29
130
|
|
|
@@ -32,28 +133,75 @@ export function cmd(message) {
|
|
|
32
133
|
if (!value) continue;
|
|
33
134
|
|
|
34
135
|
if (key === 'type' || key === 'tipo') {
|
|
35
|
-
|
|
136
|
+
command.type = value;
|
|
36
137
|
} else if (key === 'command' || key === 'comando') {
|
|
37
|
-
|
|
138
|
+
command.command = value;
|
|
38
139
|
} else if (key === 'extra') {
|
|
39
|
-
|
|
140
|
+
command.extra = value;
|
|
141
|
+
} else if (key === 'button' || key === 'botao' || key === 'botão') {
|
|
142
|
+
command.button = value;
|
|
143
|
+
} else if (
|
|
144
|
+
key === 'points' ||
|
|
145
|
+
key === 'pontos' ||
|
|
146
|
+
key === 'path' ||
|
|
147
|
+
key === 'trajeto' ||
|
|
148
|
+
key === 'caminho' ||
|
|
149
|
+
key === 'coords' ||
|
|
150
|
+
key === 'coordenadas'
|
|
151
|
+
) {
|
|
152
|
+
command.points = value;
|
|
153
|
+
command.path = value;
|
|
154
|
+
if (!command.command) {
|
|
155
|
+
command.command = value;
|
|
156
|
+
}
|
|
157
|
+
} else if (key === 'interval' || key === 'intervalo') {
|
|
158
|
+
const interval = toNonNegativeInteger(value);
|
|
159
|
+
if (interval !== null) {
|
|
160
|
+
command.interval = interval;
|
|
161
|
+
}
|
|
162
|
+
} else if (
|
|
163
|
+
key === 'clickdelay' ||
|
|
164
|
+
key === 'delayclick' ||
|
|
165
|
+
key === 'delay_clique' ||
|
|
166
|
+
key === 'delayclique' ||
|
|
167
|
+
key === 'click_delay' ||
|
|
168
|
+
key === 'delay'
|
|
169
|
+
) {
|
|
170
|
+
const clickDelay = toNonNegativeInteger(value);
|
|
171
|
+
if (clickDelay !== null) {
|
|
172
|
+
command.clickDelay = clickDelay;
|
|
173
|
+
}
|
|
174
|
+
} else if (key === 'click' || key === 'clique') {
|
|
175
|
+
command.click = value;
|
|
176
|
+
} else if (
|
|
177
|
+
key === 'doubleclick' ||
|
|
178
|
+
key === 'double_click' ||
|
|
179
|
+
key === 'double' ||
|
|
180
|
+
key === 'duplo' ||
|
|
181
|
+
key === 'cliqueduplo' ||
|
|
182
|
+
key === 'clique_duplo'
|
|
183
|
+
) {
|
|
184
|
+
const doubleClick = parseBooleanValue(value);
|
|
185
|
+
if (doubleClick !== null) {
|
|
186
|
+
command.doubleClick = doubleClick;
|
|
187
|
+
}
|
|
40
188
|
} else if (key === 'seq') {
|
|
41
|
-
const seq =
|
|
42
|
-
if (
|
|
43
|
-
|
|
189
|
+
const seq = toNonNegativeInteger(value);
|
|
190
|
+
if (seq !== null) {
|
|
191
|
+
command.seq = seq;
|
|
44
192
|
}
|
|
45
|
-
} else if (!
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} else if (!
|
|
49
|
-
|
|
50
|
-
} else if (!
|
|
51
|
-
|
|
193
|
+
} else if (!command.type) {
|
|
194
|
+
command.type = rawKey.trim();
|
|
195
|
+
command.command = value;
|
|
196
|
+
} else if (!command.command) {
|
|
197
|
+
command.command = value;
|
|
198
|
+
} else if (!command.extra) {
|
|
199
|
+
command.extra = value;
|
|
52
200
|
}
|
|
53
201
|
}
|
|
54
202
|
|
|
55
|
-
if (
|
|
56
|
-
results.push(
|
|
203
|
+
if (command.type && command.command) {
|
|
204
|
+
results.push(command);
|
|
57
205
|
}
|
|
58
206
|
}
|
|
59
207
|
|
|
@@ -64,3 +212,1479 @@ export function extractFirstCommand(message) {
|
|
|
64
212
|
const [first] = cmd(message);
|
|
65
213
|
return first || null;
|
|
66
214
|
}
|
|
215
|
+
|
|
216
|
+
export async function cursor(input, options = {}) {
|
|
217
|
+
const commands = normalizeCursorInput(input);
|
|
218
|
+
if (commands.length === 0) {
|
|
219
|
+
return {
|
|
220
|
+
executed: [],
|
|
221
|
+
skipped: [{ reason: 'no_commands' }],
|
|
222
|
+
totalCommands: 0,
|
|
223
|
+
totalExecutions: 0
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const runtime = createCursorRuntime(options);
|
|
228
|
+
let actions;
|
|
229
|
+
const executed = [];
|
|
230
|
+
const skipped = [];
|
|
231
|
+
|
|
232
|
+
async function getActions() {
|
|
233
|
+
if (actions) {
|
|
234
|
+
return actions;
|
|
235
|
+
}
|
|
236
|
+
const robot = await getRobot();
|
|
237
|
+
actions = createActionHandlers(robot, runtime);
|
|
238
|
+
return actions;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (let commandIndex = 0; commandIndex < commands.length; commandIndex++) {
|
|
242
|
+
const command = commands[commandIndex];
|
|
243
|
+
const actionKey = resolveActionKey(command.type);
|
|
244
|
+
|
|
245
|
+
if (!actionKey) {
|
|
246
|
+
skipped.push({
|
|
247
|
+
commandIndex,
|
|
248
|
+
type: command.type,
|
|
249
|
+
command: command.command,
|
|
250
|
+
reason: 'unknown_type'
|
|
251
|
+
});
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const repeatCount = getRepeatCount(command.seq);
|
|
256
|
+
if (repeatCount === 0) {
|
|
257
|
+
skipped.push({
|
|
258
|
+
commandIndex,
|
|
259
|
+
type: command.type,
|
|
260
|
+
command: command.command,
|
|
261
|
+
reason: 'seq_zero'
|
|
262
|
+
});
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (isMapAwareAction(actionKey)) {
|
|
267
|
+
try {
|
|
268
|
+
await runtime.getMapStore();
|
|
269
|
+
} catch (error) {
|
|
270
|
+
skipped.push({
|
|
271
|
+
commandIndex,
|
|
272
|
+
type: command.type,
|
|
273
|
+
command: command.command,
|
|
274
|
+
reason: 'map_unavailable',
|
|
275
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
276
|
+
});
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let action;
|
|
282
|
+
try {
|
|
283
|
+
const actionHandlers = await getActions();
|
|
284
|
+
action = actionHandlers[actionKey];
|
|
285
|
+
} catch (error) {
|
|
286
|
+
skipped.push({
|
|
287
|
+
commandIndex,
|
|
288
|
+
type: command.type,
|
|
289
|
+
command: command.command,
|
|
290
|
+
reason: 'robot_unavailable',
|
|
291
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
292
|
+
});
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!action) {
|
|
297
|
+
skipped.push({
|
|
298
|
+
commandIndex,
|
|
299
|
+
type: command.type,
|
|
300
|
+
command: command.command,
|
|
301
|
+
reason: 'unknown_type'
|
|
302
|
+
});
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (let iteration = 1; iteration <= repeatCount; iteration++) {
|
|
307
|
+
try {
|
|
308
|
+
await action(command);
|
|
309
|
+
executed.push({
|
|
310
|
+
commandIndex,
|
|
311
|
+
iteration,
|
|
312
|
+
type: command.type,
|
|
313
|
+
command: command.command,
|
|
314
|
+
extra: command.extra,
|
|
315
|
+
button: command.button,
|
|
316
|
+
points: command.points,
|
|
317
|
+
path: command.path,
|
|
318
|
+
interval: command.interval,
|
|
319
|
+
clickDelay: command.clickDelay,
|
|
320
|
+
click: command.click,
|
|
321
|
+
doubleClick: command.doubleClick,
|
|
322
|
+
seq: command.seq
|
|
323
|
+
});
|
|
324
|
+
} catch (error) {
|
|
325
|
+
skipped.push({
|
|
326
|
+
commandIndex,
|
|
327
|
+
iteration,
|
|
328
|
+
type: command.type,
|
|
329
|
+
command: command.command,
|
|
330
|
+
reason: 'action_error',
|
|
331
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
332
|
+
});
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
executed,
|
|
340
|
+
skipped,
|
|
341
|
+
totalCommands: commands.length,
|
|
342
|
+
totalExecutions: executed.length
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function createCursorRuntime(options) {
|
|
347
|
+
const config = buildCursorConfig(options);
|
|
348
|
+
let mapStorePromise;
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
config,
|
|
352
|
+
async getMapStore() {
|
|
353
|
+
if (mapStorePromise) {
|
|
354
|
+
return mapStorePromise;
|
|
355
|
+
}
|
|
356
|
+
mapStorePromise = createMapStore(config);
|
|
357
|
+
return mapStorePromise;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function buildCursorConfig(options) {
|
|
363
|
+
const raw = options && typeof options === 'object' ? options : {};
|
|
364
|
+
const mapPath =
|
|
365
|
+
typeof raw.mapPath === 'string' && raw.mapPath.trim()
|
|
366
|
+
? raw.mapPath.trim()
|
|
367
|
+
: 'map.json';
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
mapPath: resolvePath(mapPath),
|
|
371
|
+
persistMap: raw.persistMap !== false,
|
|
372
|
+
createMapIfMissing: raw.createMapIfMissing !== false,
|
|
373
|
+
apps: mergeOptionApps(raw.apps),
|
|
374
|
+
launchKey:
|
|
375
|
+
raw.launchKey === false || raw.launchKey === null
|
|
376
|
+
? undefined
|
|
377
|
+
: typeof raw.launchKey === 'string' && raw.launchKey.trim()
|
|
378
|
+
? raw.launchKey.trim()
|
|
379
|
+
: 'command',
|
|
380
|
+
typeDelay: parseIntegerOption(raw.typeDelay, 50, 0),
|
|
381
|
+
moveSteps: parseIntegerOption(raw.moveSteps, 50, 1),
|
|
382
|
+
moveDelay: parseIntegerOption(raw.moveDelay, 8, 0),
|
|
383
|
+
scrollSteps: parseIntegerOption(raw.scrollSteps, 10, 1),
|
|
384
|
+
scrollDelay: parseIntegerOption(raw.scrollDelay, 20, 0),
|
|
385
|
+
sequenceInterval: parseIntegerOption(raw.sequenceInterval, 250, 0),
|
|
386
|
+
sequenceClickMode: normalizeClickMode(raw.sequenceClick, CLICK_MODE.NONE),
|
|
387
|
+
sequenceDoubleClick:
|
|
388
|
+
parseBooleanValue(raw.sequenceDoubleClick) === true,
|
|
389
|
+
defaultClosePoint: parsePoint(raw.defaultClosePoint) || { x: 1888, y: 16 },
|
|
390
|
+
skipAdsPoint: parsePoint(raw.skipAdsPoint) || { x: 808, y: 569 }
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function mergeOptionApps(customApps) {
|
|
395
|
+
const merged = {};
|
|
396
|
+
|
|
397
|
+
for (const [appName, appConfig] of Object.entries(DEFAULT_APPS)) {
|
|
398
|
+
merged[appName] = cloneAppMap(appConfig);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (!customApps || typeof customApps !== 'object') {
|
|
402
|
+
return merged;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for (const [rawName, rawConfig] of Object.entries(customApps)) {
|
|
406
|
+
const appName = resolveAppName(rawName);
|
|
407
|
+
const normalized = normalizeAppConfig(rawConfig);
|
|
408
|
+
if (!normalized) continue;
|
|
409
|
+
|
|
410
|
+
const current = merged[appName] || {
|
|
411
|
+
launcher: undefined,
|
|
412
|
+
searchIcon: undefined,
|
|
413
|
+
state: APP_STATE.CLOSED,
|
|
414
|
+
buttons: {}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
merged[appName] = {
|
|
418
|
+
launcher: normalized.launcher || current.launcher,
|
|
419
|
+
searchIcon: normalized.searchIcon || current.searchIcon,
|
|
420
|
+
state: normalized.state || current.state || APP_STATE.CLOSED,
|
|
421
|
+
buttons: {
|
|
422
|
+
...(current.buttons || {}),
|
|
423
|
+
...(normalized.buttons || {})
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return merged;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function normalizeCommandObject(value) {
|
|
432
|
+
if (!value || typeof value !== 'object') {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const rawType = value.type !== undefined ? value.type : value.tipo;
|
|
437
|
+
const rawCommand = value.command !== undefined ? value.command : value.comando;
|
|
438
|
+
const rawButton =
|
|
439
|
+
value.button !== undefined
|
|
440
|
+
? value.button
|
|
441
|
+
: value.botao !== undefined
|
|
442
|
+
? value.botao
|
|
443
|
+
: value['botão'];
|
|
444
|
+
const rawPoints =
|
|
445
|
+
value.points !== undefined
|
|
446
|
+
? value.points
|
|
447
|
+
: value.pontos !== undefined
|
|
448
|
+
? value.pontos
|
|
449
|
+
: value.path !== undefined
|
|
450
|
+
? value.path
|
|
451
|
+
: value.trajeto !== undefined
|
|
452
|
+
? value.trajeto
|
|
453
|
+
: value.caminho !== undefined
|
|
454
|
+
? value.caminho
|
|
455
|
+
: value.coords !== undefined
|
|
456
|
+
? value.coords
|
|
457
|
+
: value.coordenadas;
|
|
458
|
+
const rawPath =
|
|
459
|
+
value.path !== undefined
|
|
460
|
+
? value.path
|
|
461
|
+
: value.caminho !== undefined
|
|
462
|
+
? value.caminho
|
|
463
|
+
: value.trajeto;
|
|
464
|
+
const rawInterval =
|
|
465
|
+
value.interval !== undefined ? value.interval : value.intervalo;
|
|
466
|
+
const rawClickDelay =
|
|
467
|
+
value.clickDelay !== undefined
|
|
468
|
+
? value.clickDelay
|
|
469
|
+
: value.delayClick !== undefined
|
|
470
|
+
? value.delayClick
|
|
471
|
+
: value.delayClique !== undefined
|
|
472
|
+
? value.delayClique
|
|
473
|
+
: value.delay;
|
|
474
|
+
const rawClick = value.click !== undefined ? value.click : value.clique;
|
|
475
|
+
const rawDoubleClick =
|
|
476
|
+
value.doubleClick !== undefined
|
|
477
|
+
? value.doubleClick
|
|
478
|
+
: value.double_click !== undefined
|
|
479
|
+
? value.double_click
|
|
480
|
+
: value.duplo !== undefined
|
|
481
|
+
? value.duplo
|
|
482
|
+
: value.cliqueDuplo !== undefined
|
|
483
|
+
? value.cliqueDuplo
|
|
484
|
+
: value.clique_duplo;
|
|
485
|
+
|
|
486
|
+
const type =
|
|
487
|
+
typeof rawType === 'string' ? rawType.trim() : String(rawType || '').trim();
|
|
488
|
+
|
|
489
|
+
const normalizedPoints = normalizePointsValue(
|
|
490
|
+
rawPoints !== undefined ? rawPoints : rawPath
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
let command =
|
|
494
|
+
typeof rawCommand === 'string'
|
|
495
|
+
? rawCommand.trim()
|
|
496
|
+
: Number.isFinite(rawCommand)
|
|
497
|
+
? String(rawCommand)
|
|
498
|
+
: '';
|
|
499
|
+
|
|
500
|
+
if (!command && normalizedPoints) {
|
|
501
|
+
command = serializePoints(normalizedPoints);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (!type || !command) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const normalized = {
|
|
509
|
+
type,
|
|
510
|
+
command,
|
|
511
|
+
extra:
|
|
512
|
+
value.extra === undefined || value.extra === null
|
|
513
|
+
? undefined
|
|
514
|
+
: String(value.extra),
|
|
515
|
+
button: undefined,
|
|
516
|
+
points: undefined,
|
|
517
|
+
path: undefined,
|
|
518
|
+
interval: undefined,
|
|
519
|
+
clickDelay: undefined,
|
|
520
|
+
click: undefined,
|
|
521
|
+
doubleClick: undefined,
|
|
522
|
+
seq: undefined
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
if (rawButton !== undefined && rawButton !== null) {
|
|
526
|
+
const buttonText = String(rawButton).trim();
|
|
527
|
+
if (buttonText) {
|
|
528
|
+
normalized.button = buttonText;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (normalizedPoints) {
|
|
533
|
+
normalized.points = normalizedPoints;
|
|
534
|
+
normalized.path = normalizedPoints;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const interval = toNonNegativeInteger(rawInterval);
|
|
538
|
+
if (interval !== null) {
|
|
539
|
+
normalized.interval = interval;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const clickDelay = toNonNegativeInteger(rawClickDelay);
|
|
543
|
+
if (clickDelay !== null) {
|
|
544
|
+
normalized.clickDelay = clickDelay;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (rawClick !== undefined && rawClick !== null) {
|
|
548
|
+
const clickMode = String(rawClick).trim();
|
|
549
|
+
if (clickMode) {
|
|
550
|
+
normalized.click = clickMode;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const doubleClick = parseBooleanValue(rawDoubleClick);
|
|
555
|
+
if (doubleClick !== null) {
|
|
556
|
+
normalized.doubleClick = doubleClick;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const rawSeq = value.seq !== undefined ? value.seq : value.sequencia;
|
|
560
|
+
const seq = toNonNegativeInteger(rawSeq);
|
|
561
|
+
if (seq !== null) {
|
|
562
|
+
normalized.seq = seq;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return normalized;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function normalizeCursorInput(input) {
|
|
569
|
+
if (typeof input === 'string') {
|
|
570
|
+
return cmd(input);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (Array.isArray(input)) {
|
|
574
|
+
return input.map(normalizeCommandObject).filter(Boolean);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const single = normalizeCommandObject(input);
|
|
578
|
+
return single ? [single] : [];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function getRepeatCount(seq) {
|
|
582
|
+
const parsed = toNonNegativeInteger(seq);
|
|
583
|
+
if (parsed === null) {
|
|
584
|
+
return 1;
|
|
585
|
+
}
|
|
586
|
+
return parsed;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function toNonNegativeInteger(value) {
|
|
590
|
+
const parsed = Number(value);
|
|
591
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
return parsed;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function parseIntegerOption(value, fallback, min) {
|
|
598
|
+
const parsed = Number(value);
|
|
599
|
+
if (!Number.isInteger(parsed) || parsed < min) {
|
|
600
|
+
return fallback;
|
|
601
|
+
}
|
|
602
|
+
return parsed;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function parseBooleanValue(value) {
|
|
606
|
+
if (value === undefined || value === null) {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (typeof value === 'boolean') {
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const token = canonicalToken(value);
|
|
615
|
+
if (
|
|
616
|
+
token === 'true' ||
|
|
617
|
+
token === '1' ||
|
|
618
|
+
token === 'sim' ||
|
|
619
|
+
token === 'yes' ||
|
|
620
|
+
token === 'on'
|
|
621
|
+
) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (
|
|
626
|
+
token === 'false' ||
|
|
627
|
+
token === '0' ||
|
|
628
|
+
token === 'nao' ||
|
|
629
|
+
token === 'no' ||
|
|
630
|
+
token === 'off'
|
|
631
|
+
) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function normalizeClickMode(value, fallback) {
|
|
639
|
+
if (value === true) {
|
|
640
|
+
return CLICK_MODE.BETWEEN;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (value === false) {
|
|
644
|
+
return CLICK_MODE.NONE;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const token = canonicalToken(value);
|
|
648
|
+
|
|
649
|
+
if (
|
|
650
|
+
token === CLICK_MODE.EACH ||
|
|
651
|
+
token === 'cada' ||
|
|
652
|
+
token === 'todos' ||
|
|
653
|
+
token === 'todas'
|
|
654
|
+
) {
|
|
655
|
+
return CLICK_MODE.EACH;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (token === CLICK_MODE.BETWEEN || token === 'entre') {
|
|
659
|
+
return CLICK_MODE.BETWEEN;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (token === CLICK_MODE.FIRST || token === 'primeiro') {
|
|
663
|
+
return CLICK_MODE.FIRST;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (token === CLICK_MODE.LAST || token === 'ultimo' || token === 'final') {
|
|
667
|
+
return CLICK_MODE.LAST;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (
|
|
671
|
+
token === CLICK_MODE.NONE ||
|
|
672
|
+
token === 'sem' ||
|
|
673
|
+
token === 'nenhum' ||
|
|
674
|
+
token === 'desligado'
|
|
675
|
+
) {
|
|
676
|
+
return CLICK_MODE.NONE;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return fallback || CLICK_MODE.NONE;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function parsePointToken(token) {
|
|
683
|
+
const value = String(token || '').trim();
|
|
684
|
+
if (!value) return null;
|
|
685
|
+
|
|
686
|
+
const match = value.match(/^(-?\d+)\s*(?:x|:|,)\s*(-?\d+)$/i);
|
|
687
|
+
if (!match) return null;
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
x: Number(match[1]),
|
|
691
|
+
y: Number(match[2])
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function normalizePointsValue(value) {
|
|
696
|
+
if (value === undefined || value === null) {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (Array.isArray(value)) {
|
|
701
|
+
const points = [];
|
|
702
|
+
for (const item of value) {
|
|
703
|
+
const point = parsePoint(item) || parsePointToken(item);
|
|
704
|
+
if (!point) {
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
points.push(point);
|
|
708
|
+
}
|
|
709
|
+
return points.length > 0 ? points : undefined;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (typeof value === 'string') {
|
|
713
|
+
const parts = value
|
|
714
|
+
.split(/[|;>]/g)
|
|
715
|
+
.map((part) => part.trim())
|
|
716
|
+
.filter(Boolean);
|
|
717
|
+
|
|
718
|
+
if (parts.length === 0) {
|
|
719
|
+
return undefined;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const points = [];
|
|
723
|
+
for (const part of parts) {
|
|
724
|
+
const point = parsePointToken(part);
|
|
725
|
+
if (!point) {
|
|
726
|
+
return undefined;
|
|
727
|
+
}
|
|
728
|
+
points.push(point);
|
|
729
|
+
}
|
|
730
|
+
return points.length > 0 ? points : undefined;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const singlePoint = parsePoint(value);
|
|
734
|
+
if (singlePoint) {
|
|
735
|
+
return [singlePoint];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function serializePoints(points) {
|
|
742
|
+
if (!Array.isArray(points) || points.length === 0) {
|
|
743
|
+
return '';
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return points.map((point) => `${point.x}x${point.y}`).join('|');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function normalizePathConfig(rawPath) {
|
|
750
|
+
if (rawPath === undefined || rawPath === null) {
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
let points;
|
|
755
|
+
let clickMode = CLICK_MODE.NONE;
|
|
756
|
+
let interval = 0;
|
|
757
|
+
let clickDelay = 0;
|
|
758
|
+
let doubleClick = false;
|
|
759
|
+
|
|
760
|
+
if (typeof rawPath === 'string' || Array.isArray(rawPath)) {
|
|
761
|
+
points = normalizePointsValue(rawPath);
|
|
762
|
+
} else if (typeof rawPath === 'object') {
|
|
763
|
+
points = normalizePointsValue(
|
|
764
|
+
rawPath.points !== undefined
|
|
765
|
+
? rawPath.points
|
|
766
|
+
: rawPath.pontos !== undefined
|
|
767
|
+
? rawPath.pontos
|
|
768
|
+
: rawPath.path !== undefined
|
|
769
|
+
? rawPath.path
|
|
770
|
+
: rawPath.caminho !== undefined
|
|
771
|
+
? rawPath.caminho
|
|
772
|
+
: rawPath.coords !== undefined
|
|
773
|
+
? rawPath.coords
|
|
774
|
+
: rawPath.coordenadas
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
clickMode = normalizeClickMode(
|
|
778
|
+
rawPath.click !== undefined ? rawPath.click : rawPath.clique,
|
|
779
|
+
CLICK_MODE.NONE
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
interval =
|
|
783
|
+
toNonNegativeInteger(
|
|
784
|
+
rawPath.interval !== undefined ? rawPath.interval : rawPath.intervalo
|
|
785
|
+
) || 0;
|
|
786
|
+
|
|
787
|
+
clickDelay =
|
|
788
|
+
toNonNegativeInteger(
|
|
789
|
+
rawPath.clickDelay !== undefined
|
|
790
|
+
? rawPath.clickDelay
|
|
791
|
+
: rawPath.delayClick !== undefined
|
|
792
|
+
? rawPath.delayClick
|
|
793
|
+
: rawPath.delayClique !== undefined
|
|
794
|
+
? rawPath.delayClique
|
|
795
|
+
: rawPath.delay
|
|
796
|
+
) || 0;
|
|
797
|
+
|
|
798
|
+
doubleClick =
|
|
799
|
+
parseBooleanValue(
|
|
800
|
+
rawPath.doubleClick !== undefined
|
|
801
|
+
? rawPath.doubleClick
|
|
802
|
+
: rawPath.double_click !== undefined
|
|
803
|
+
? rawPath.double_click
|
|
804
|
+
: rawPath.duplo
|
|
805
|
+
) === true;
|
|
806
|
+
} else {
|
|
807
|
+
return undefined;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (!points || points.length === 0) {
|
|
811
|
+
return undefined;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return {
|
|
815
|
+
points,
|
|
816
|
+
clickMode,
|
|
817
|
+
interval,
|
|
818
|
+
clickDelay,
|
|
819
|
+
doubleClick
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function normalizeTargetConfig(rawTarget) {
|
|
824
|
+
if (rawTarget === undefined || rawTarget === null) {
|
|
825
|
+
return undefined;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (typeof rawTarget !== 'object') {
|
|
829
|
+
const directPoint = parsePoint(rawTarget);
|
|
830
|
+
return directPoint ? { position: directPoint } : undefined;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
let position =
|
|
834
|
+
parsePoint(
|
|
835
|
+
rawTarget.position !== undefined
|
|
836
|
+
? rawTarget.position
|
|
837
|
+
: rawTarget.posicao !== undefined
|
|
838
|
+
? rawTarget.posicao
|
|
839
|
+
: rawTarget.destino !== undefined
|
|
840
|
+
? rawTarget.destino
|
|
841
|
+
: rawTarget.target !== undefined
|
|
842
|
+
? rawTarget.target
|
|
843
|
+
: rawTarget
|
|
844
|
+
) || undefined;
|
|
845
|
+
|
|
846
|
+
const pathConfig = normalizePathConfig(
|
|
847
|
+
rawTarget.caminho !== undefined ? rawTarget.caminho : rawTarget.path
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
if (!position && pathConfig && pathConfig.points.length > 0) {
|
|
851
|
+
const lastPoint = pathConfig.points[pathConfig.points.length - 1];
|
|
852
|
+
position = { x: lastPoint.x, y: lastPoint.y };
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (!position) {
|
|
856
|
+
return undefined;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return {
|
|
860
|
+
position,
|
|
861
|
+
...(pathConfig ? { path: pathConfig } : {})
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function shouldClickOnStep(mode, index, totalPoints) {
|
|
866
|
+
if (mode === CLICK_MODE.EACH) {
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (mode === CLICK_MODE.BETWEEN) {
|
|
871
|
+
return index < totalPoints - 1;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (mode === CLICK_MODE.FIRST) {
|
|
875
|
+
return index === 0;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (mode === CLICK_MODE.LAST) {
|
|
879
|
+
return index === totalPoints - 1;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function removeDiacritics(value) {
|
|
886
|
+
return String(value || '')
|
|
887
|
+
.normalize('NFD')
|
|
888
|
+
.replace(/[\u0300-\u036f]/g, '');
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function canonicalToken(value) {
|
|
892
|
+
return removeDiacritics(value)
|
|
893
|
+
.trim()
|
|
894
|
+
.toLowerCase()
|
|
895
|
+
.replace(/[^a-z0-9_\-\s]/g, '')
|
|
896
|
+
.replace(/[-\s]+/g, '_');
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function resolveActionKey(type) {
|
|
900
|
+
const key = canonicalToken(type);
|
|
901
|
+
return ACTION_ALIASES[key];
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
function isMapAwareAction(actionKey) {
|
|
905
|
+
return (
|
|
906
|
+
actionKey === 'open_app' ||
|
|
907
|
+
actionKey === 'close_app' ||
|
|
908
|
+
actionKey === 'skip_ads'
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function resolveAppName(name) {
|
|
913
|
+
const key = canonicalToken(name);
|
|
914
|
+
return APP_ALIASES[key] || key;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function resolveButtonKey(name) {
|
|
918
|
+
const key = canonicalToken(name);
|
|
919
|
+
return BUTTON_ALIASES[key] || key;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function normalizeState(value) {
|
|
923
|
+
const key = canonicalToken(value);
|
|
924
|
+
if (key === 'aberto' || key === 'open' || key === 'opened') {
|
|
925
|
+
return APP_STATE.OPEN;
|
|
926
|
+
}
|
|
927
|
+
if (key === 'fechado' || key === 'closed') {
|
|
928
|
+
return APP_STATE.CLOSED;
|
|
929
|
+
}
|
|
930
|
+
return undefined;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function parsePoint(value) {
|
|
934
|
+
if (Array.isArray(value) && value.length === 2) {
|
|
935
|
+
const x = Number(value[0]);
|
|
936
|
+
const y = Number(value[1]);
|
|
937
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
938
|
+
return { x, y };
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (value && typeof value === 'object') {
|
|
943
|
+
const x = Number(value.x);
|
|
944
|
+
const y = Number(value.y);
|
|
945
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
946
|
+
return { x, y };
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function clonePoint(value) {
|
|
954
|
+
if (!value) return undefined;
|
|
955
|
+
return { x: value.x, y: value.y };
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function clonePathConfig(pathConfig) {
|
|
959
|
+
if (!pathConfig || typeof pathConfig !== 'object') {
|
|
960
|
+
return undefined;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
return {
|
|
964
|
+
points: (pathConfig.points || []).map((point) => clonePoint(point)),
|
|
965
|
+
clickMode: pathConfig.clickMode || CLICK_MODE.NONE,
|
|
966
|
+
interval: toNonNegativeInteger(pathConfig.interval) || 0,
|
|
967
|
+
clickDelay: toNonNegativeInteger(pathConfig.clickDelay) || 0,
|
|
968
|
+
doubleClick: pathConfig.doubleClick === true
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function cloneTargetConfig(targetConfig) {
|
|
973
|
+
if (!targetConfig || typeof targetConfig !== 'object') {
|
|
974
|
+
return undefined;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const position = targetConfig.position
|
|
978
|
+
? clonePoint(targetConfig.position)
|
|
979
|
+
: clonePoint(parsePoint(targetConfig));
|
|
980
|
+
|
|
981
|
+
if (!position) {
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
return {
|
|
986
|
+
position,
|
|
987
|
+
...(targetConfig.path ? { path: clonePathConfig(targetConfig.path) } : {})
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function cloneButtonMap(buttons) {
|
|
992
|
+
const cloned = {};
|
|
993
|
+
if (!buttons || typeof buttons !== 'object') {
|
|
994
|
+
return cloned;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
for (const [buttonName, buttonConfig] of Object.entries(buttons)) {
|
|
998
|
+
if (!buttonConfig || typeof buttonConfig !== 'object') continue;
|
|
999
|
+
if (!buttonConfig.position) continue;
|
|
1000
|
+
cloned[buttonName] = {
|
|
1001
|
+
position: clonePoint(buttonConfig.position),
|
|
1002
|
+
...(buttonConfig.path ? { path: clonePathConfig(buttonConfig.path) } : {}),
|
|
1003
|
+
...(buttonConfig.setState ? { setState: buttonConfig.setState } : {})
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return cloned;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function cloneAppMap(app) {
|
|
1011
|
+
return {
|
|
1012
|
+
launcher: cloneTargetConfig(app.launcher),
|
|
1013
|
+
searchIcon: cloneTargetConfig(app.searchIcon),
|
|
1014
|
+
state: app.state || APP_STATE.CLOSED,
|
|
1015
|
+
buttons: cloneButtonMap(app.buttons)
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function normalizeButtonConfig(rawConfig) {
|
|
1020
|
+
if (rawConfig === undefined || rawConfig === null) {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const target = normalizeTargetConfig(rawConfig);
|
|
1025
|
+
if (!target) {
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const rawState =
|
|
1030
|
+
rawConfig && typeof rawConfig === 'object'
|
|
1031
|
+
? rawConfig.setState !== undefined
|
|
1032
|
+
? rawConfig.setState
|
|
1033
|
+
: rawConfig.stateAfter !== undefined
|
|
1034
|
+
? rawConfig.stateAfter
|
|
1035
|
+
: rawConfig.estadoDepois !== undefined
|
|
1036
|
+
? rawConfig.estadoDepois
|
|
1037
|
+
: undefined
|
|
1038
|
+
: undefined;
|
|
1039
|
+
|
|
1040
|
+
const state = normalizeState(rawState);
|
|
1041
|
+
|
|
1042
|
+
return {
|
|
1043
|
+
position: target.position,
|
|
1044
|
+
...(target.path ? { path: target.path } : {}),
|
|
1045
|
+
...(state ? { setState: state } : {})
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
function mergeTargetPath(target, rawPath) {
|
|
1050
|
+
if (!target || target.path) {
|
|
1051
|
+
return target;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const pathConfig = normalizePathConfig(rawPath);
|
|
1055
|
+
if (!pathConfig) {
|
|
1056
|
+
return target;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
return {
|
|
1060
|
+
...target,
|
|
1061
|
+
path: pathConfig
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
function normalizeButtons(rawButtons) {
|
|
1066
|
+
const buttons = {};
|
|
1067
|
+
if (!rawButtons || typeof rawButtons !== 'object') {
|
|
1068
|
+
return buttons;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
for (const [rawName, rawConfig] of Object.entries(rawButtons)) {
|
|
1072
|
+
const button = normalizeButtonConfig(rawConfig);
|
|
1073
|
+
if (!button) continue;
|
|
1074
|
+
|
|
1075
|
+
const buttonName = resolveButtonKey(rawName);
|
|
1076
|
+
buttons[buttonName] = button;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return buttons;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function normalizeAppConfig(rawConfig) {
|
|
1083
|
+
if (!rawConfig || typeof rawConfig !== 'object') {
|
|
1084
|
+
return null;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const rawLauncher =
|
|
1088
|
+
rawConfig.launcher !== undefined
|
|
1089
|
+
? rawConfig.launcher
|
|
1090
|
+
: rawConfig.position !== undefined
|
|
1091
|
+
? rawConfig.position
|
|
1092
|
+
: rawConfig.posicao;
|
|
1093
|
+
|
|
1094
|
+
let launcher = normalizeTargetConfig(rawLauncher);
|
|
1095
|
+
launcher = mergeTargetPath(
|
|
1096
|
+
launcher,
|
|
1097
|
+
rawConfig.caminho !== undefined ? rawConfig.caminho : rawConfig.path
|
|
1098
|
+
);
|
|
1099
|
+
|
|
1100
|
+
const rawSearchIcon =
|
|
1101
|
+
rawConfig.searchIcon !== undefined
|
|
1102
|
+
? rawConfig.searchIcon
|
|
1103
|
+
: rawConfig.lupa !== undefined
|
|
1104
|
+
? rawConfig.lupa
|
|
1105
|
+
: rawConfig.search;
|
|
1106
|
+
|
|
1107
|
+
const searchIcon = normalizeTargetConfig(rawSearchIcon);
|
|
1108
|
+
|
|
1109
|
+
const state = normalizeState(
|
|
1110
|
+
rawConfig.state !== undefined ? rawConfig.state : rawConfig.estado
|
|
1111
|
+
);
|
|
1112
|
+
|
|
1113
|
+
const buttons = normalizeButtons(
|
|
1114
|
+
rawConfig.buttons !== undefined ? rawConfig.buttons : rawConfig.botoes
|
|
1115
|
+
);
|
|
1116
|
+
|
|
1117
|
+
return {
|
|
1118
|
+
...(launcher ? { launcher } : {}),
|
|
1119
|
+
...(searchIcon ? { searchIcon } : {}),
|
|
1120
|
+
...(state ? { state } : {}),
|
|
1121
|
+
buttons
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function buildFallbackMap(config) {
|
|
1126
|
+
const apps = {};
|
|
1127
|
+
|
|
1128
|
+
for (const [appName, appConfig] of Object.entries(config.apps)) {
|
|
1129
|
+
const normalizedApp = cloneAppMap({
|
|
1130
|
+
...appConfig,
|
|
1131
|
+
state: appConfig.state || APP_STATE.CLOSED
|
|
1132
|
+
});
|
|
1133
|
+
const buttons = normalizedApp.buttons || {};
|
|
1134
|
+
if (!buttons.fechar) {
|
|
1135
|
+
buttons.fechar = {
|
|
1136
|
+
position: clonePoint(config.defaultClosePoint),
|
|
1137
|
+
setState: APP_STATE.CLOSED
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
normalizedApp.buttons = buttons;
|
|
1141
|
+
apps[appName] = normalizedApp;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (apps.youtube) {
|
|
1145
|
+
const buttons = apps.youtube.buttons || {};
|
|
1146
|
+
if (!buttons.pular_ads) {
|
|
1147
|
+
buttons.pular_ads = {
|
|
1148
|
+
position: clonePoint(config.skipAdsPoint)
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
apps.youtube.buttons = buttons;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return {
|
|
1155
|
+
version: 1,
|
|
1156
|
+
apps
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function normalizeMapObject(rawMap) {
|
|
1161
|
+
const result = {
|
|
1162
|
+
version: 1,
|
|
1163
|
+
apps: {}
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
if (!rawMap || typeof rawMap !== 'object') {
|
|
1167
|
+
return result;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (Number.isFinite(rawMap.version)) {
|
|
1171
|
+
result.version = Number(rawMap.version);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
const rawApps =
|
|
1175
|
+
rawMap.apps !== undefined
|
|
1176
|
+
? rawMap.apps
|
|
1177
|
+
: rawMap.aplicativos !== undefined
|
|
1178
|
+
? rawMap.aplicativos
|
|
1179
|
+
: undefined;
|
|
1180
|
+
|
|
1181
|
+
if (!rawApps || typeof rawApps !== 'object') {
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
for (const [rawAppName, rawAppConfig] of Object.entries(rawApps)) {
|
|
1186
|
+
const appName = resolveAppName(rawAppName);
|
|
1187
|
+
const appConfig = normalizeAppConfig(rawAppConfig);
|
|
1188
|
+
if (!appConfig) continue;
|
|
1189
|
+
|
|
1190
|
+
result.apps[appName] = {
|
|
1191
|
+
launcher: appConfig.launcher,
|
|
1192
|
+
searchIcon: appConfig.searchIcon,
|
|
1193
|
+
state: appConfig.state || APP_STATE.CLOSED,
|
|
1194
|
+
buttons: appConfig.buttons || {}
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
return result;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function mergeMaps(baseMap, overrideMap) {
|
|
1202
|
+
const merged = {
|
|
1203
|
+
version:
|
|
1204
|
+
overrideMap && Number.isFinite(overrideMap.version)
|
|
1205
|
+
? overrideMap.version
|
|
1206
|
+
: baseMap.version,
|
|
1207
|
+
apps: {}
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
for (const [appName, appConfig] of Object.entries(baseMap.apps || {})) {
|
|
1211
|
+
merged.apps[appName] = cloneAppMap(appConfig);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
for (const [appName, appConfig] of Object.entries(overrideMap.apps || {})) {
|
|
1215
|
+
const current = merged.apps[appName] || {
|
|
1216
|
+
launcher: undefined,
|
|
1217
|
+
searchIcon: undefined,
|
|
1218
|
+
state: APP_STATE.CLOSED,
|
|
1219
|
+
buttons: {}
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
merged.apps[appName] = {
|
|
1223
|
+
launcher: appConfig.launcher || current.launcher,
|
|
1224
|
+
searchIcon: appConfig.searchIcon || current.searchIcon,
|
|
1225
|
+
state: appConfig.state || current.state || APP_STATE.CLOSED,
|
|
1226
|
+
buttons: {
|
|
1227
|
+
...(current.buttons || {}),
|
|
1228
|
+
...(appConfig.buttons || {})
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return merged;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
async function createMapStore(config) {
|
|
1237
|
+
const fallbackMap = buildFallbackMap(config);
|
|
1238
|
+
let fromFileMap = null;
|
|
1239
|
+
let mapCreatedFromMissing = false;
|
|
1240
|
+
|
|
1241
|
+
try {
|
|
1242
|
+
const content = await readFile(config.mapPath, 'utf8');
|
|
1243
|
+
let parsed;
|
|
1244
|
+
try {
|
|
1245
|
+
parsed = JSON.parse(content);
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
throw new Error(`invalid_map_json:${config.mapPath}`);
|
|
1248
|
+
}
|
|
1249
|
+
fromFileMap = normalizeMapObject(parsed);
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
if (error && error.code === 'ENOENT') {
|
|
1252
|
+
if (!config.createMapIfMissing) {
|
|
1253
|
+
throw new Error(
|
|
1254
|
+
`map_not_found:${config.mapPath}. Crie o arquivo map.json ou use { createMapIfMissing: true }.`
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
fromFileMap = { version: 1, apps: {} };
|
|
1258
|
+
mapCreatedFromMissing = true;
|
|
1259
|
+
} else if (error instanceof Error) {
|
|
1260
|
+
throw error;
|
|
1261
|
+
} else {
|
|
1262
|
+
throw new Error(`map_read_error:${config.mapPath}`);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const map = mergeMaps(fallbackMap, fromFileMap);
|
|
1267
|
+
const store = {
|
|
1268
|
+
path: config.mapPath,
|
|
1269
|
+
persistMap: config.persistMap,
|
|
1270
|
+
map
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
if (config.createMapIfMissing && mapCreatedFromMissing) {
|
|
1274
|
+
await persistMapStore(store);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
return store;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function resolveAppInMap(store, rawAppName) {
|
|
1281
|
+
const appName = resolveAppName(rawAppName);
|
|
1282
|
+
return {
|
|
1283
|
+
appName,
|
|
1284
|
+
app: store.map.apps[appName]
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function resolveMappedButton(appConfig, rawButtonName) {
|
|
1289
|
+
if (!rawButtonName) return null;
|
|
1290
|
+
const buttonName = resolveButtonKey(rawButtonName);
|
|
1291
|
+
return appConfig.buttons && appConfig.buttons[buttonName]
|
|
1292
|
+
? appConfig.buttons[buttonName]
|
|
1293
|
+
: null;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
async function updateAppState(store, appName, nextState) {
|
|
1297
|
+
const state = normalizeState(nextState);
|
|
1298
|
+
if (!state) return;
|
|
1299
|
+
|
|
1300
|
+
const app = store.map.apps[appName];
|
|
1301
|
+
if (!app) return;
|
|
1302
|
+
if (app.state === state) return;
|
|
1303
|
+
|
|
1304
|
+
app.state = state;
|
|
1305
|
+
await persistMapStore(store);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
async function persistMapStore(store) {
|
|
1309
|
+
if (!store.persistMap) return;
|
|
1310
|
+
const serialized = JSON.stringify(store.map, null, 2) + '\n';
|
|
1311
|
+
await writeFile(store.path, serialized, 'utf8');
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
function getAppState(appConfig) {
|
|
1315
|
+
return normalizeState(appConfig && appConfig.state) || APP_STATE.CLOSED;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
function sleep(ms) {
|
|
1319
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
async function smoothMove(robot, xFinal, yFinal, steps, delay) {
|
|
1323
|
+
const current = robot.getMousePos();
|
|
1324
|
+
const totalSteps = Math.max(1, parseIntegerOption(steps, 50, 1));
|
|
1325
|
+
|
|
1326
|
+
for (let i = 1; i <= totalSteps; i++) {
|
|
1327
|
+
const progress = i / totalSteps;
|
|
1328
|
+
const x = Math.round(current.x + (xFinal - current.x) * progress);
|
|
1329
|
+
const y = Math.round(current.y + (yFinal - current.y) * progress);
|
|
1330
|
+
robot.moveMouse(x, y);
|
|
1331
|
+
await sleep(delay);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
async function smoothScroll(robot, amount, steps, delay) {
|
|
1336
|
+
const totalSteps = Math.max(1, parseIntegerOption(steps, 10, 1));
|
|
1337
|
+
let applied = 0;
|
|
1338
|
+
|
|
1339
|
+
for (let i = 1; i <= totalSteps; i++) {
|
|
1340
|
+
const target = Math.round((amount * i) / totalSteps);
|
|
1341
|
+
const delta = target - applied;
|
|
1342
|
+
if (delta !== 0) {
|
|
1343
|
+
robot.scrollMouse(0, delta);
|
|
1344
|
+
applied = target;
|
|
1345
|
+
}
|
|
1346
|
+
await sleep(delay);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
function clickCurrentPosition(robot, doubleClick) {
|
|
1351
|
+
if (doubleClick) {
|
|
1352
|
+
robot.mouseClick('left', true);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
robot.mouseClick();
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
async function clickPoint(robot, point, config, clickOptions = {}) {
|
|
1359
|
+
await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
|
|
1360
|
+
clickCurrentPosition(robot, clickOptions.doubleClick === true);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
async function runConfiguredPath(robot, pathConfig, config) {
|
|
1364
|
+
if (!pathConfig || !Array.isArray(pathConfig.points) || pathConfig.points.length === 0) {
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const clickMode = normalizeClickMode(pathConfig.clickMode, CLICK_MODE.NONE);
|
|
1369
|
+
const interval = toNonNegativeInteger(pathConfig.interval) || 0;
|
|
1370
|
+
const clickDelay = toNonNegativeInteger(pathConfig.clickDelay) || 0;
|
|
1371
|
+
const doubleClick = pathConfig.doubleClick === true;
|
|
1372
|
+
|
|
1373
|
+
for (let index = 0; index < pathConfig.points.length; index++) {
|
|
1374
|
+
const point = pathConfig.points[index];
|
|
1375
|
+
await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
|
|
1376
|
+
|
|
1377
|
+
if (shouldClickOnStep(clickMode, index, pathConfig.points.length)) {
|
|
1378
|
+
if (clickDelay > 0) {
|
|
1379
|
+
await sleep(clickDelay);
|
|
1380
|
+
}
|
|
1381
|
+
clickCurrentPosition(robot, doubleClick);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
if (index < pathConfig.points.length - 1 && interval > 0) {
|
|
1385
|
+
await sleep(interval);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
async function clickTarget(robot, target, config, clickOptions = {}) {
|
|
1391
|
+
if (!target || !target.position) {
|
|
1392
|
+
throw new Error('invalid_target_config');
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (target.path) {
|
|
1396
|
+
await runConfiguredPath(robot, target.path, config);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
await clickPoint(robot, target.position, config, clickOptions);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
function createActionHandlers(robot, runtime) {
|
|
1403
|
+
const config = runtime.config;
|
|
1404
|
+
let mapStorePromise;
|
|
1405
|
+
|
|
1406
|
+
async function getMapStore() {
|
|
1407
|
+
if (mapStorePromise) {
|
|
1408
|
+
return mapStorePromise;
|
|
1409
|
+
}
|
|
1410
|
+
mapStorePromise = runtime.getMapStore();
|
|
1411
|
+
return mapStorePromise;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
async function openAppIfNeeded(appName, appConfig, mapStore) {
|
|
1415
|
+
if (getAppState(appConfig) === APP_STATE.OPEN) {
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
if (!appConfig.launcher || !appConfig.launcher.position) {
|
|
1420
|
+
throw new Error(`missing_launcher:${appName}`);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
if (config.launchKey) {
|
|
1424
|
+
robot.keyTap(config.launchKey);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (appConfig.launcher.path) {
|
|
1428
|
+
await clickTarget(robot, appConfig.launcher, config);
|
|
1429
|
+
} else {
|
|
1430
|
+
await smoothMove(robot, 0, 0, config.moveSteps, config.moveDelay);
|
|
1431
|
+
await smoothMove(
|
|
1432
|
+
robot,
|
|
1433
|
+
appConfig.launcher.position.x,
|
|
1434
|
+
appConfig.launcher.position.y,
|
|
1435
|
+
config.moveSteps,
|
|
1436
|
+
config.moveDelay
|
|
1437
|
+
);
|
|
1438
|
+
await smoothMove(
|
|
1439
|
+
robot,
|
|
1440
|
+
appConfig.launcher.position.x + 20,
|
|
1441
|
+
appConfig.launcher.position.y + 10,
|
|
1442
|
+
config.moveSteps,
|
|
1443
|
+
config.moveDelay
|
|
1444
|
+
);
|
|
1445
|
+
await smoothMove(
|
|
1446
|
+
robot,
|
|
1447
|
+
appConfig.launcher.position.x - 20,
|
|
1448
|
+
appConfig.launcher.position.y - 10,
|
|
1449
|
+
config.moveSteps,
|
|
1450
|
+
config.moveDelay
|
|
1451
|
+
);
|
|
1452
|
+
robot.mouseClick();
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
await updateAppState(mapStore, appName, APP_STATE.OPEN);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function resolveSequencePoints(command) {
|
|
1459
|
+
const points = normalizePointsValue(
|
|
1460
|
+
command.points !== undefined
|
|
1461
|
+
? command.points
|
|
1462
|
+
: command.path !== undefined
|
|
1463
|
+
? command.path
|
|
1464
|
+
: command.command
|
|
1465
|
+
);
|
|
1466
|
+
|
|
1467
|
+
if (!points || points.length < 2) {
|
|
1468
|
+
throw new Error('move_sequence_requires_at_least_2_points');
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
return points;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function resolveSequenceInterval(command) {
|
|
1475
|
+
const interval = toNonNegativeInteger(command.interval);
|
|
1476
|
+
if (interval !== null) {
|
|
1477
|
+
return interval;
|
|
1478
|
+
}
|
|
1479
|
+
return config.sequenceInterval;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function resolveSequenceClickMode(command) {
|
|
1483
|
+
return normalizeClickMode(command.click, config.sequenceClickMode);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
function resolveSequenceClickDelay(command) {
|
|
1487
|
+
const clickDelay = toNonNegativeInteger(command.clickDelay);
|
|
1488
|
+
if (clickDelay !== null) {
|
|
1489
|
+
return clickDelay;
|
|
1490
|
+
}
|
|
1491
|
+
return 0;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function resolveSequenceDoubleClick(command) {
|
|
1495
|
+
const value = parseBooleanValue(command.doubleClick);
|
|
1496
|
+
if (value !== null) {
|
|
1497
|
+
return value;
|
|
1498
|
+
}
|
|
1499
|
+
return config.sequenceDoubleClick;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
return {
|
|
1503
|
+
async skip_ads(command) {
|
|
1504
|
+
let point = config.skipAdsPoint;
|
|
1505
|
+
|
|
1506
|
+
if (command.command) {
|
|
1507
|
+
try {
|
|
1508
|
+
const mapStore = await getMapStore();
|
|
1509
|
+
const appRef = resolveAppInMap(mapStore, command.command);
|
|
1510
|
+
const appConfig = appRef.app;
|
|
1511
|
+
if (appConfig) {
|
|
1512
|
+
const button = resolveMappedButton(
|
|
1513
|
+
appConfig,
|
|
1514
|
+
command.button || 'pular_ads'
|
|
1515
|
+
);
|
|
1516
|
+
if (button && button.position) {
|
|
1517
|
+
if (button.path) {
|
|
1518
|
+
await clickTarget(robot, button, config);
|
|
1519
|
+
point = null;
|
|
1520
|
+
} else {
|
|
1521
|
+
point = button.position;
|
|
1522
|
+
}
|
|
1523
|
+
if (button.setState) {
|
|
1524
|
+
await updateAppState(mapStore, appRef.appName, button.setState);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
} catch (error) {
|
|
1529
|
+
// keep fallback point when map is unavailable for skip_ads
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (point) {
|
|
1534
|
+
await clickPoint(robot, point, config);
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
|
|
1538
|
+
async click() {
|
|
1539
|
+
const size = robot.getScreenSize();
|
|
1540
|
+
const centerX = Math.floor(size.width / 2);
|
|
1541
|
+
const centerY = Math.floor(size.height / 2);
|
|
1542
|
+
await clickPoint(robot, { x: centerX, y: centerY }, config);
|
|
1543
|
+
},
|
|
1544
|
+
|
|
1545
|
+
async double_click() {
|
|
1546
|
+
const size = robot.getScreenSize();
|
|
1547
|
+
const centerX = Math.floor(size.width / 2);
|
|
1548
|
+
const centerY = Math.floor(size.height / 2);
|
|
1549
|
+
await clickPoint(
|
|
1550
|
+
robot,
|
|
1551
|
+
{ x: centerX, y: centerY },
|
|
1552
|
+
config,
|
|
1553
|
+
{ doubleClick: true }
|
|
1554
|
+
);
|
|
1555
|
+
},
|
|
1556
|
+
|
|
1557
|
+
async scroll(command) {
|
|
1558
|
+
const amount = Number(command.command);
|
|
1559
|
+
if (!Number.isFinite(amount)) {
|
|
1560
|
+
throw new Error('scroll_requires_numeric_command');
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
const size = robot.getScreenSize();
|
|
1564
|
+
const centerX = Math.floor(size.width / 2);
|
|
1565
|
+
const centerY = Math.floor(size.height / 2);
|
|
1566
|
+
|
|
1567
|
+
await smoothMove(robot, centerX, centerY, config.moveSteps, config.moveDelay);
|
|
1568
|
+
await smoothScroll(robot, amount, config.scrollSteps, config.scrollDelay);
|
|
1569
|
+
},
|
|
1570
|
+
|
|
1571
|
+
async move_sequence(command) {
|
|
1572
|
+
const points = resolveSequencePoints(command);
|
|
1573
|
+
const interval = resolveSequenceInterval(command);
|
|
1574
|
+
const clickMode = resolveSequenceClickMode(command);
|
|
1575
|
+
const clickDelay = resolveSequenceClickDelay(command);
|
|
1576
|
+
const doubleClick = resolveSequenceDoubleClick(command);
|
|
1577
|
+
|
|
1578
|
+
for (let index = 0; index < points.length; index++) {
|
|
1579
|
+
const point = points[index];
|
|
1580
|
+
await smoothMove(
|
|
1581
|
+
robot,
|
|
1582
|
+
point.x,
|
|
1583
|
+
point.y,
|
|
1584
|
+
config.moveSteps,
|
|
1585
|
+
config.moveDelay
|
|
1586
|
+
);
|
|
1587
|
+
|
|
1588
|
+
if (shouldClickOnStep(clickMode, index, points.length)) {
|
|
1589
|
+
if (clickDelay > 0) {
|
|
1590
|
+
await sleep(clickDelay);
|
|
1591
|
+
}
|
|
1592
|
+
clickCurrentPosition(robot, doubleClick);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
if (index < points.length - 1 && interval > 0) {
|
|
1596
|
+
await sleep(interval);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
},
|
|
1600
|
+
|
|
1601
|
+
async open_app(command) {
|
|
1602
|
+
const mapStore = await getMapStore();
|
|
1603
|
+
const appRef = resolveAppInMap(mapStore, command.command);
|
|
1604
|
+
if (!appRef.app) {
|
|
1605
|
+
throw new Error(`unknown_app:${appRef.appName}`);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
await openAppIfNeeded(appRef.appName, appRef.app, mapStore);
|
|
1609
|
+
|
|
1610
|
+
if (command.button) {
|
|
1611
|
+
const mappedButton = resolveMappedButton(appRef.app, command.button);
|
|
1612
|
+
if (!mappedButton) {
|
|
1613
|
+
throw new Error(
|
|
1614
|
+
`unknown_button:${appRef.appName}:${resolveButtonKey(command.button)}`
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
await clickTarget(robot, mappedButton, config);
|
|
1619
|
+
if (mappedButton.setState) {
|
|
1620
|
+
await updateAppState(mapStore, appRef.appName, mappedButton.setState);
|
|
1621
|
+
}
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
if (command.extra) {
|
|
1626
|
+
if (appRef.app.searchIcon && appRef.app.searchIcon.position) {
|
|
1627
|
+
await clickTarget(robot, appRef.app.searchIcon, config);
|
|
1628
|
+
}
|
|
1629
|
+
robot.typeStringDelayed(String(command.extra), config.typeDelay);
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
|
|
1633
|
+
async close_app(command) {
|
|
1634
|
+
const mapStore = await getMapStore();
|
|
1635
|
+
const appRef = resolveAppInMap(mapStore, command.command);
|
|
1636
|
+
if (!appRef.app) {
|
|
1637
|
+
throw new Error(`unknown_app:${appRef.appName}`);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
if (getAppState(appRef.app) === APP_STATE.CLOSED) {
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const mappedButton = resolveMappedButton(
|
|
1645
|
+
appRef.app,
|
|
1646
|
+
command.button || 'fechar'
|
|
1647
|
+
);
|
|
1648
|
+
|
|
1649
|
+
if (mappedButton) {
|
|
1650
|
+
await clickTarget(robot, mappedButton, config);
|
|
1651
|
+
await updateAppState(
|
|
1652
|
+
mapStore,
|
|
1653
|
+
appRef.appName,
|
|
1654
|
+
mappedButton.setState || APP_STATE.CLOSED
|
|
1655
|
+
);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const modifier =
|
|
1660
|
+
config.launchKey === 'command' ? 'command' : config.launchKey || 'command';
|
|
1661
|
+
robot.keyTap('w', modifier);
|
|
1662
|
+
await updateAppState(mapStore, appRef.appName, APP_STATE.CLOSED);
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
async function getRobot() {
|
|
1668
|
+
if (robotCache) {
|
|
1669
|
+
return robotCache;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
let module;
|
|
1673
|
+
try {
|
|
1674
|
+
module = await import('robotjs');
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
throw new Error(
|
|
1677
|
+
'robotjs_not_available: install dependencies and run in a supported desktop environment.'
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
const robot = module && module.default ? module.default : module;
|
|
1682
|
+
if (!robot || typeof robot.mouseClick !== 'function') {
|
|
1683
|
+
throw new Error(
|
|
1684
|
+
'robotjs_not_available: install dependencies and run in a supported desktop environment.'
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
robotCache = robot;
|
|
1689
|
+
return robot;
|
|
1690
|
+
}
|