Initial commit

This commit is contained in:
2026-03-08 01:34:54 +08:00
commit 1f104f73c8
441 changed files with 64911 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
class InputService {
constructor() {
if (this.constructor === InputService) {
throw new Error('InputService is an abstract class and cannot be instantiated directly');
}
}
async mouseMove(x, y) {
throw new Error('Method mouseMove() must be implemented');
}
async mouseDown(button = 'left') {
throw new Error('Method mouseDown() must be implemented');
}
async mouseUp(button = 'left') {
throw new Error('Method mouseUp() must be implemented');
}
async mouseClick(button = 'left') {
throw new Error('Method mouseClick() must be implemented');
}
async mouseWheel(delta) {
throw new Error('Method mouseWheel() must be implemented');
}
async keyDown(key) {
throw new Error('Method keyDown() must be implemented');
}
async keyUp(key) {
throw new Error('Method keyUp() must be implemented');
}
async keyPress(key) {
throw new Error('Method keyPress() must be implemented');
}
async start() {
throw new Error('Method start() must be implemented');
}
async stop() {
throw new Error('Method stop() must be implemented');
}
isReady() {
throw new Error('Method isReady() must be implemented');
}
}
module.exports = InputService;

View File

@@ -0,0 +1,275 @@
const { spawn } = require('child_process');
const InputService = require('./InputService');
const logger = require('../../utils/logger');
const config = require('../../config');
const VK_CODES = {
'enter': 13, 'backspace': 8, 'tab': 9, 'escape': 27,
'delete': 46, 'home': 36, 'end': 35, 'pageup': 33,
'pagedown': 34, 'up': 38, 'down': 40, 'left': 37, 'right': 39,
'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115,
'f5': 116, 'f6': 117, 'f7': 118, 'f8': 119,
'f9': 120, 'f10': 121, 'f11': 122, 'f12': 123,
'ctrl': 17, 'alt': 18, 'shift': 16, 'win': 91,
'space': 32,
',': 188, '.': 190, '/': 191, ';': 186, "'": 222,
'[': 219, ']': 221, '\\': 220, '-': 189, '=': 187,
'`': 192
};
const POWERSHELL_SCRIPT = `
$env:PSModulePath += ';.'
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public class Input {
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vKey);
}
'@ -Language CSharp -ErrorAction SilentlyContinue
while ($true) {
$line = [Console]::In.ReadLine()
if ($line -eq $null) { break }
if ($line -eq '') { continue }
try {
$parts = $line -split ' '
$cmd = $parts[0]
switch ($cmd) {
'move' {
$x = [int]$parts[1]
$y = [int]$parts[2]
[Input]::SetCursorPos($x, $y)
}
'down' {
$btn = $parts[1]
$flag = if ($btn -eq 'right') { 8 } else { 2 }
[Input]::mouse_event($flag, 0, 0, 0, 0)
}
'up' {
$btn = $parts[1]
$flag = if ($btn -eq 'right') { 16 } else { 4 }
[Input]::mouse_event($flag, 0, 0, 0, 0)
}
'wheel' {
$delta = [int]$parts[1]
[Input]::mouse_event(2048, 0, 0, $delta, 0)
}
'kdown' {
$vk = [int]$parts[1]
[Input]::keybd_event([byte]$vk, 0, 0, 0)
}
'kup' {
$vk = [int]$parts[1]
[Input]::keybd_event([byte]$vk, 0, 2, 0)
}
}
} catch {
Write-Error $_.Exception.Message
}
}
`;
class PowerShellInput extends InputService {
constructor(options = {}) {
super();
this.inputConfig = config.getSection('input') || {
mouseEnabled: true,
keyboardEnabled: true,
sensitivity: 1.0
};
this.mouseEnabled = options.mouseEnabled !== undefined
? options.mouseEnabled
: this.inputConfig.mouseEnabled;
this.keyboardEnabled = options.keyboardEnabled !== undefined
? options.keyboardEnabled
: this.inputConfig.keyboardEnabled;
this.psProcess = null;
this._isReady = false;
this._isStopping = false;
this._restartTimer = null;
}
_startProcess() {
if (this.psProcess) {
this._stopProcess();
}
this.psProcess = spawn('powershell', [
'-NoProfile',
'-ExecutionPolicy', 'Bypass',
'-Command', POWERSHELL_SCRIPT
], {
stdio: ['pipe', 'pipe', 'pipe']
});
this.psProcess.stdout.on('data', (data) => {
logger.debug('PowerShell stdout', { output: data.toString().trim() });
});
this.psProcess.stderr.on('data', (data) => {
logger.error('PowerShell stderr', { error: data.toString().trim() });
});
this.psProcess.on('close', (code) => {
logger.warn('PowerShell process closed', { code });
this._isReady = false;
this.psProcess = null;
this._scheduleRestart();
});
this.psProcess.on('error', (error) => {
logger.error('PowerShell process error', { error: error.message });
this._isReady = false;
});
this._isReady = true;
logger.info('PowerShellInput process started');
}
_stopProcess() {
if (this.psProcess) {
this.psProcess.kill();
this.psProcess = null;
this._isReady = false;
}
if (this._restartTimer) {
clearTimeout(this._restartTimer);
this._restartTimer = null;
}
}
_scheduleRestart() {
if (this._isStopping) {
return;
}
if (this._restartTimer) {
clearTimeout(this._restartTimer);
}
this._restartTimer = setTimeout(() => {
if (!this._isStopping && (this.mouseEnabled || this.keyboardEnabled)) {
logger.info('Restarting PowerShell process after crash');
this._startProcess();
}
}, 1000);
}
async _sendCommand(cmd) {
if (!this._isReady || !this.psProcess) {
return;
}
return new Promise((resolve, reject) => {
try {
this.psProcess.stdin.write(cmd + '\n');
resolve();
} catch (error) {
logger.error('Failed to send command', { cmd, error: error.message });
reject(error);
}
});
}
_getVkCode(key) {
const lowerKey = key.toLowerCase();
if (VK_CODES[lowerKey]) {
return VK_CODES[lowerKey];
}
if (key.length === 1) {
if (VK_CODES[key]) {
return VK_CODES[key];
}
return key.toUpperCase().charCodeAt(0);
}
return null;
}
async mouseMove(x, y) {
if (!this.mouseEnabled) return;
await this._sendCommand(`move ${Math.floor(x)} ${Math.floor(y)}`);
}
async mouseDown(button = 'left') {
if (!this.mouseEnabled) return;
await this._sendCommand(`down ${button}`);
}
async mouseUp(button = 'left') {
if (!this.mouseEnabled) return;
await this._sendCommand(`up ${button}`);
}
async mouseClick(button = 'left') {
if (!this.mouseEnabled) return;
await this.mouseDown(button);
await new Promise(r => setTimeout(r, 10));
await this.mouseUp(button);
}
async mouseWheel(delta) {
if (!this.mouseEnabled) return;
await this._sendCommand(`wheel ${Math.floor(delta)}`);
}
async keyDown(key) {
if (!this.keyboardEnabled) return;
const vk = this._getVkCode(key);
if (vk) {
await this._sendCommand(`kdown ${vk}`);
}
}
async keyUp(key) {
if (!this.keyboardEnabled) return;
const vk = this._getVkCode(key);
if (vk) {
await this._sendCommand(`kup ${vk}`);
}
}
async keyPress(key) {
if (!this.keyboardEnabled) return;
await this.keyDown(key);
await new Promise(r => setTimeout(r, 10));
await this.keyUp(key);
}
async start() {
if (this.mouseEnabled || this.keyboardEnabled) {
this._isStopping = false;
this._startProcess();
}
logger.info('PowerShellInput started', {
mouseEnabled: this.mouseEnabled,
keyboardEnabled: this.keyboardEnabled
});
}
async stop() {
this._isStopping = true;
this._stopProcess();
logger.info('PowerShellInput stopped');
}
isReady() {
return this._isReady;
}
}
module.exports = PowerShellInput;

View File

@@ -0,0 +1,7 @@
const InputService = require('./InputService');
const PowerShellInput = require('./PowerShellInput');
module.exports = {
InputService,
PowerShellInput
};