diff --git a/assistant/bun.config.mjs b/assistant/bun.config.mjs index c82b78b..a7a7974 100644 --- a/assistant/bun.config.mjs +++ b/assistant/bun.config.mjs @@ -6,7 +6,7 @@ import fs from 'node:fs'; // bun run src/index.ts -- import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const external = ['pm2', '@kevisual/hot-api']; +const external = ['pm2', '@kevisual/hot-api', '@nut-tree-fork/nut-js']; /** * * @param {string} p diff --git a/assistant/src/routes/hot-api/key-sender/index.ts b/assistant/src/routes/hot-api/key-sender/index.ts index b3e36cb..90ac701 100644 --- a/assistant/src/routes/hot-api/key-sender/index.ts +++ b/assistant/src/routes/hot-api/key-sender/index.ts @@ -1,19 +1,20 @@ import { app } from '@/app.ts'; // import { Hotkeys } from '@kevisual/hot-api'; -// import { useContextKey } from '@kevisual/context'; -// app.route({ -// path: 'key-sender', -// // middleware: ['admin-auth'] -// }).define(async (ctx) => { -// let keys = ctx.query.keys; -// if (keys.includes(' ')) { -// keys = keys.replace(/\s+/g, '+'); -// } -// const hotKeys: Hotkeys = useContextKey('hotkeys', () => new Hotkeys()); -// if (typeof keys === 'string') { -// await hotKeys.pressHotkey({ -// hotkey: keys, -// }); -// } -// ctx.body = 'ok'; -// }).addTo(app); \ No newline at end of file +import { Hotkeys } from './lib.ts'; +import { useContextKey } from '@kevisual/context'; +app.route({ + path: 'key-sender', + // middleware: ['admin-auth'] +}).define(async (ctx) => { + let keys = ctx.query.keys; + if (keys.includes(' ')) { + keys = keys.replace(/\s+/g, '+'); + } + const hotKeys: Hotkeys = useContextKey('hotkeys', () => new Hotkeys()); + if (typeof keys === 'string') { + await hotKeys.pressHotkey({ + hotkey: keys, + }); + } + ctx.body = 'ok'; +}).addTo(app); diff --git a/assistant/src/routes/hot-api/key-sender/lib.ts b/assistant/src/routes/hot-api/key-sender/lib.ts new file mode 100644 index 0000000..7f0285b --- /dev/null +++ b/assistant/src/routes/hot-api/key-sender/lib.ts @@ -0,0 +1,89 @@ +import { keyboard, Key } from "@nut-tree-fork/nut-js"; + +/** + * 控制功能部分的案件映射 + */ +export const keyMap: Record = { + 'ctrl': Key.LeftControl, + 'leftctrl': Key.LeftControl, + 'rightctrl': Key.RightControl, + 'alt': Key.LeftAlt, + 'leftalt': Key.LeftAlt, + 'rightalt': Key.RightAlt, + 'shift': Key.LeftShift, + 'leftshift': Key.LeftShift, + 'rightshift': Key.RightShift, + 'meta': Key.LeftSuper, + 'cmd': Key.LeftCmd, + 'win': Key.LeftWin, + // 根据操作系统选择 Ctrl 或 Command 键 + 'ctrlorcommand': process.platform === 'darwin' ? Key.LeftCmd : Key.LeftControl, +}; + +/** + * 将快捷键字符串转换为 Key 枚举值 + * @param hotkey + * @returns + */ +export const parseHotkey = (hotkey: string): Key[] => { + return hotkey + .toLowerCase() + .split('+') + .map(key => { + const trimmed = key.trim().toLowerCase(); + // 如果是修饰键,从映射表中获取 + if (keyMap[trimmed]) { + return keyMap[trimmed]; + } + // 如果是字母,转换为大写并查找对应的 Key + if (trimmed.length === 1 && /[a-z]/.test(trimmed)) { + const upperKey = trimmed.toUpperCase(); + return Key[upperKey as keyof typeof Key] as Key; + } + // 其他情况直接查找 + return Key[trimmed as keyof typeof Key] as Key; + }) + .filter((key): key is Key => key !== undefined); +} + +type PressHostKeysOptions = { + hotkey: string; + durationMs?: number; +} +export const pressHotkey = async (opts: PressHostKeysOptions): Promise => { + const { hotkey, durationMs = 100 } = opts; + const keys = parseHotkey(hotkey); + + console.log('准备模拟按下快捷键:', hotkey); + // 同时按下所有键 + await keyboard.pressKey(...keys); + // 短暂延迟后释放 + await new Promise(resolve => setTimeout(resolve, durationMs)); + // 释放所有键 + await keyboard.releaseKey(...keys); + return true +} + +/** + * 模拟按下一组快捷键,支持逗号分隔的多个快捷键 + * @param opts + * @returns + */ +export const pressHotkeys = async (opts: PressHostKeysOptions): Promise => { + let { hotkey } = opts; + hotkey = hotkey.replace(/\s+/g, ''); // 去除所有空格 + const hotkeyList = hotkey.split(',').map(hk => hk.trim()); + if (hotkeyList.length === 0) { + return await pressHotkey({ ...opts, hotkey }); + } + for (const hk of hotkeyList) { + await pressHotkey({ ...opts, hotkey: hk }); + // 每个快捷键之间稍作延迟 + await new Promise(resolve => setTimeout(resolve, 200)); + } + return true; +} +export class Hotkeys { + pressHotkey = pressHotkey; + pressHotkeys = pressHotkeys; +} \ No newline at end of file diff --git a/assistant/src/server.ts b/assistant/src/server.ts index aa0d566..bbda4df 100644 --- a/assistant/src/server.ts +++ b/assistant/src/server.ts @@ -9,7 +9,7 @@ import path from 'node:path' import chalk from 'chalk'; import { AssistantApp } from './lib.ts'; import { getBunPath } from './module/get-bun-path.ts'; -export const runServer = async (port?: number, listenPath = '127.0.0.1') => { +export const runServer = async (port: number = 51015, listenPath = '127.0.0.1') => { let _port: number | undefined; if (port) { _port = await getPort({ port });