ninwa/libs/keyfilter.js

284 lines
10 KiB
JavaScript
Raw Normal View History

const logger = require('./logger.js');
2022-03-03 03:35:34 +01:00
const LINE_START = 'Event: time';
2022-02-15 04:33:19 +01:00
const ACTION_KEYUP = { id: 0, action: 'keyup' };
const ACTION_KEYDOWN = { id: 1, action: 'keydown' };
const ACTION_KEYHOLD = { id: 2, action: 'keyhold' };
class Keyfilter {
2022-03-11 15:47:48 +01:00
constructor(config) {
if (config === undefined || config.length === 0) {
2022-02-15 04:33:19 +01:00
return;
}
this.actions = new Map();
this.registered = {
keys: [],
events: [],
types: []
}
2022-03-11 15:47:48 +01:00
for (let index = 0; index < config.length; index++) {
this.registerAction(config[index]);
2022-02-15 04:33:19 +01:00
}
2022-03-03 03:35:34 +01:00
this.currentCombo = undefined;
2022-02-15 04:33:19 +01:00
}
2022-03-11 15:47:48 +01:00
getCommand(command) {
2022-03-28 16:08:33 +02:00
if (command === undefined || global.config?.commands === undefined) {
2022-03-11 15:47:48 +01:00
return;
}
2022-03-28 16:08:33 +02:00
const result = global.config.commands[command];
2022-03-11 15:47:48 +01:00
if (result === undefined) {
return;
}
result.name = command;
return result;
}
getActionType(type) {
let result = ACTION_KEYDOWN;
if (isNaN(type)) {
switch (type.toLowerCase()) {
case ACTION_KEYUP.action:
result = ACTION_KEYUP;
break;
case ACTION_KEYHOLD.action:
result = ACTION_KEYHOLD;
break;
}
} else {
switch (type) {
case ACTION_KEYUP.id:
result = ACTION_KEYUP;
break;
case ACTION_KEYHOLD.id:
result = ACTION_KEYHOLD;
break;
}
2022-02-15 04:33:19 +01:00
}
2022-03-11 15:47:48 +01:00
return result;
}
registerAction(config) {
let key = config.key.toUpperCase();
if (config.combo !== undefined && config.combo.length > 0) {
const tmp = JSON.parse(JSON.stringify(config.combo));
tmp.unshift(config.key);
key = tmp.toString().toUpperCase();
}
if (Array.from(this.actions.keys()).includes(key)) {
logger.warn('skipping already registered key(s) \'' + key + '\'...');
return;
}
this.actions.set(key,
2022-03-03 03:35:34 +01:00
{
2022-03-11 15:47:48 +01:00
type: this.getActionType(config.type),
2022-03-03 03:35:34 +01:00
event: config.event,
2022-03-11 15:47:48 +01:00
command: this.getCommand(config.command),
2022-03-03 03:35:34 +01:00
combo: config.combo,
delay: function () {
if (config.combo === undefined) {
return config.delay;
}
return config.delay || 1000;
}(),
}
);
const singleKeys = key.split(',');
for (let index = 0; index < singleKeys.length; index++) {
const singleKey = singleKeys[index];
if (!this.registered.keys.includes(singleKey)) {
this.registered.keys.push(singleKey);
}
}
if (!this.registered.events.includes(this.actions.get(key).event)) {
this.registered.events.push(this.actions.get(key).event);
}
if (!this.registered.types.includes(this.actions.get(key).type.id)) {
this.registered.types.push(this.actions.get(key).type.id);
}
2022-02-15 04:33:19 +01:00
}
filter(input) {
2022-03-03 03:35:34 +01:00
if (input === undefined || input.length === 0) {
2022-02-15 04:33:19 +01:00
return;
}
const parsedEvent = this.parseLine(input);
if (this.parsedEventIsUnknown(parsedEvent)) {
return;
}
if (this.isPartOfCombo(parsedEvent)) {
if (parsedEvent.ignore) {
return;
2022-02-15 04:33:19 +01:00
}
return this.setComboResult(this.getFilterResult(parsedEvent.key, parsedEvent.type.action));
}
if (this.isStartOfCombo(parsedEvent)) {
return this.setComboResult(this.getFilterResult(parsedEvent.key, parsedEvent.type.action));
}
for (let [key, event] of this.actions) {
if (!this.parsedEventIsValid(key, event, parsedEvent)) {
2022-03-03 03:35:34 +01:00
continue;
}
const result = this.getFilterResult(parsedEvent.key, event.type.action, event.command);
if (this.shouldBeDelayed(event)) {
result.delayed = true;
return result;
2022-02-15 04:33:19 +01:00
}
event.captured = new Date().getTime();
return result;
2022-02-15 04:33:19 +01:00
}
}
parsedEventIsUnknown(parsedEvent) {
return parsedEvent === undefined || !this.registered.keys.includes(parsedEvent.key) || !this.registered.events.includes(parsedEvent.event) || !this.registered.types.includes(parsedEvent.type.id);
}
parsedEventIsValid(key, value, parsed) {
2022-03-28 16:08:33 +02:00
if (value.event !== parsed.event || value.type.id !== parsed.type.id) {
return false;
}
if (value.combo === undefined || value.combo.length === 0) {
return key === parsed.key;
}
if (this.currentCombo === undefined) {
return key.startsWith(parsed.key);
}
2022-03-28 16:08:33 +02:00
for (let index = 0; index < this.currentCombo.possibilities.length; index++) {
if (this.currentCombo.possibilities[index].combo[0].toUpperCase() === parsed.key) {
this.resetCurrentCombo();
return true;
}
}
return false;
2022-03-03 03:35:34 +01:00
}
2022-03-11 15:47:48 +01:00
setComboResult(result) {
if (result === undefined || this.currentCombo === undefined) {
2022-03-03 03:35:34 +01:00
return;
}
2022-03-11 15:47:48 +01:00
if (!this.currentCombo.finished) {
result.combo = { possibilities: this.currentCombo.possibilities.length };
} else {
result.combo = {
finished: true,
done: this.currentCombo.done
};
result.command = this.currentCombo.command;
}
return result
}
getFilterResult(key, type, command) {
if (key === undefined || type === undefined) {
return;
}
return {
key: key,
2022-03-11 15:47:48 +01:00
type: type,
command: command
};
2022-03-03 03:35:34 +01:00
}
parseLine(line) {
if (line === undefined || line.length === 0 || !line.startsWith(LINE_START)) {
return;
}
2022-03-03 03:35:34 +01:00
try {
const parts = line.split(',');
const event = parts[1].substring(parts[1].indexOf('(') + 1, parts[1].lastIndexOf(')'));
const key = parts[2].substring(parts[2].indexOf('(') + 1, parts[2].indexOf(')'));
2022-03-11 15:47:48 +01:00
const type = this.getActionType(parseInt(parts[3].split(' ').pop()));
2022-03-03 03:35:34 +01:00
return { event: event, key: key, type: type };
} catch (err) {
return;
}
}
shouldBeDelayed(event) {
if (event.delay === undefined || event.delay === 0 || event.captured === undefined) {
return false;
}
return new Date().getTime() - event.captured < event.delay;
}
2022-03-11 15:47:48 +01:00
isStartOfCombo(parsedEvent) {
let possibilities = [];
for (let [actionKey, actionEvent] of this.actions) {
if (actionEvent.combo === undefined || actionEvent.combo.length === 0) {
continue;
}
if (!actionKey.toUpperCase().startsWith(parsedEvent.key) || actionEvent.type.id !== parsedEvent.type.id || actionEvent.event !== parsedEvent.event) {
2022-03-11 15:47:48 +01:00
continue;
}
2022-03-11 15:47:48 +01:00
possibilities.push(JSON.parse(JSON.stringify(actionEvent)));
}
if (possibilities.length === 0) {
return false;
}
2022-03-03 03:35:34 +01:00
this.currentCombo = {
2022-03-11 15:47:48 +01:00
key: parsedEvent.key.toUpperCase(),
2022-03-03 03:52:34 +01:00
timestamp: new Date().getTime(),
2022-03-11 15:47:48 +01:00
type: parsedEvent.type,
event: parsedEvent.event,
done: [parsedEvent.key],
2022-03-11 15:47:48 +01:00
possibilities: possibilities
2022-03-03 03:35:34 +01:00
};
this.setComboTimeout();
2022-03-03 03:35:34 +01:00
return true;
}
isPartOfCombo(parsedEvent) {
2022-03-11 15:47:48 +01:00
if (this.currentCombo === undefined || this.currentCombo.type.id !== parsedEvent.type.id || this.currentCombo.event !== parsedEvent.event) {
2022-03-03 03:35:34 +01:00
return false;
}
2022-03-11 15:47:48 +01:00
let possibilities = [];
for (let index = 0; index < this.currentCombo.possibilities.length; index++) {
const possibility = this.currentCombo.possibilities[index];
2022-03-11 15:47:48 +01:00
if (possibility.combo.length === 0) {
continue;
2022-03-11 15:47:48 +01:00
}
if (possibility.combo[0].toUpperCase() !== parsedEvent.key) {
continue;
}
2022-03-11 15:47:48 +01:00
possibility.combo.shift();
possibilities.push(possibility);
}
2022-03-11 15:47:48 +01:00
if (possibilities.length === 0) {
2022-03-03 03:35:34 +01:00
return false;
}
2022-03-03 03:52:34 +01:00
this.currentCombo.timestamp = new Date().getTime();
2022-03-03 03:35:34 +01:00
this.currentCombo.done.push(parsedEvent.key);
2022-03-11 15:47:48 +01:00
this.currentCombo.possibilities = possibilities;
this.currentCombo.finished = false;
if (this.currentCombo.possibilities.length === 1 && this.currentCombo.possibilities[0].combo.length === 0) {
const tmp = this.currentCombo.possibilities[0];
delete tmp.combo;
tmp.finished = true;
tmp.timestamp = this.currentCombo.timestamp;
tmp.done = this.currentCombo.done;
this.currentCombo = tmp;
}
this.setComboTimeout();
2022-03-03 03:35:34 +01:00
return true;
}
setComboTimeout() {
if (this.currentCombo === undefined || global.config?.combos?.timeout === undefined || isNaN(global.config?.combos?.timeout)) {
return;
}
this.clearComboTimeout();
logger.debug('setting timeout for current combo to ' + parseInt(global.config.combos.timeout) + 'ms');
this.comboTimeout = setTimeout(() => {
this.resetCurrentCombo()
}, parseInt(global.config.combos.timeout));
}
clearComboTimeout() {
if (this.comboTimeout === undefined) {
return;
}
logger.debug('clearing timeout for current combo');
clearTimeout(this.comboTimeout);
2022-03-03 03:35:34 +01:00
}
resetCurrentCombo() {
this.clearComboTimeout();
logger.debug('resetting current combo');
2022-03-03 03:35:34 +01:00
this.currentCombo = undefined;
2022-02-15 04:33:19 +01:00
}
isValid() {
return this.actions != undefined && this.actions.size > 0;
}
2022-03-10 14:05:08 +01:00
2022-02-15 04:33:19 +01:00
}
2022-03-11 15:47:48 +01:00
2022-03-03 03:35:34 +01:00
2022-02-15 04:33:19 +01:00
module.exports = Keyfilter;