| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- /**
- * Node.js 环境下的 API 桥接,替代 window.electronAPI
- */
- const path = require('path')
- const fs = require('fs')
- const os = require('os')
- const { spawnSync } = require('child_process')
- const projectRoot = path.resolve(__dirname, '..', '..')
- const adbInteractPath = path.join(projectRoot, 'nodejs', 'adb', 'adb-interact.js')
- const imageMatchScriptPath = path.join(projectRoot, 'python', 'scripts', 'image-match.py')
- const config = require(path.join(projectRoot, 'configs', 'config.js'))
- function runAdb(action, args = [], deviceId = '') {
- const result = spawnSync('node', [adbInteractPath, action, ...args, deviceId], {
- encoding: 'utf-8',
- timeout: 10000
- })
- return { success: result.status === 0, error: result.stderr }
- }
- async function sendTap(device, x, y) {
- const r = runAdb('tap', [String(x), String(y)], device)
- return r
- }
- async function sendSwipe(device, x1, y1, x2, y2, duration) {
- const r = runAdb('swipe-coords', [String(x1), String(y1), String(x2), String(y2), String(duration || 300)], device)
- return r
- }
- async function sendKeyEvent(device, keyCode) {
- const r = runAdb('keyevent', [String(keyCode)], device)
- return r
- }
- async function sendText(device, text) {
- const r = runAdb('text', [String(text)], device)
- return r
- }
- async function matchImageAndGetCoordinate(device, imagePath) {
- const templatePath = path.isAbsolute(imagePath) ? imagePath : path.resolve(projectRoot, imagePath)
- if (!fs.existsSync(templatePath)) {
- return { success: false, error: `模板图片不存在: ${templatePath}` }
- }
- const ts = Date.now()
- const screenshotPath = path.join(os.tmpdir(), `ef-screenshot-${ts}.png`)
- const templateCopyPath = path.join(os.tmpdir(), `ef-template-${ts}.png`)
- try {
- fs.copyFileSync(templatePath, templateCopyPath)
- } catch (e) {
- return { success: false, error: `复制模板失败: ${e.message}` }
- }
- const venvPython = path.join(projectRoot, 'python', 'env', 'Scripts', 'python.exe')
- const hasVenv = fs.existsSync(venvPython)
- const pythonPath = hasVenv
- ? venvPython
- : (config.pythonPath?.path ? path.join(config.pythonPath.path, 'python.exe') : 'python')
- const adbPathRel = config.adbPath?.path || 'lib/scrcpy-adb/adb.exe'
- const adbPath = path.isAbsolute(adbPathRel) ? adbPathRel : path.join(projectRoot, adbPathRel)
- const screenshotPathNorm = screenshotPath.replace(/\\/g, '/')
- const templateCopyPathNorm = templateCopyPath.replace(/\\/g, '/')
- const matchResult = spawnSync(pythonPath, [imageMatchScriptPath, '--adb', adbPath, '--device', device, '--screenshot', screenshotPathNorm, '--template', templateCopyPathNorm], {
- encoding: 'utf-8',
- timeout: 20000,
- env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
- cwd: projectRoot
- })
- try { fs.unlinkSync(screenshotPath) } catch (_) {}
- try { fs.unlinkSync(templateCopyPath) } catch (_) {}
- if (matchResult.status !== 0) {
- const errMsg = (matchResult.stderr || matchResult.stdout || '').trim()
- return { success: false, error: errMsg || '图像匹配失败' }
- }
- let out
- try {
- out = JSON.parse(matchResult.stdout.trim())
- } catch (e) {
- return { success: false, error: `解析匹配结果失败: ${matchResult.stdout}` }
- }
- if (!out.success) {
- return { success: false, error: out.error || '未找到匹配' }
- }
- return {
- success: true,
- coordinate: { x: out.x, y: out.y, width: out.width, height: out.height },
- clickPosition: { x: out.center_x, y: out.center_y }
- }
- }
- async function findTextAndGetCoordinate(device, targetText) {
- return { success: false, error: 'findTextAndGetCoordinate 需在主进程实现' }
- }
- async function appendLog(folderPath, message) {
- const logPath = path.join(folderPath, 'log.txt')
- fs.appendFileSync(logPath, message + '\n')
- return Promise.resolve()
- }
- function readTextFile(filePath) {
- const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath)
- const content = fs.readFileSync(fullPath, 'utf8')
- return { success: true, content }
- }
- function writeTextFile(filePath, content) {
- const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath)
- const dir = path.dirname(fullPath)
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
- fs.writeFileSync(fullPath, content)
- return { success: true }
- }
- async function stub(name) {
- return { success: false, error: `${name} 需在主进程实现` }
- }
- const nodeApi = {
- sendTap,
- sendSwipe,
- sendKeyEvent,
- sendText,
- matchImageAndGetCoordinate,
- findTextAndGetCoordinate,
- appendLog,
- readTextFile,
- writeTextFile,
- saveChatHistory: () => stub('saveChatHistory'),
- readChatHistory: () => stub('readChatHistory'),
- readAllChatHistory: () => stub('readAllChatHistory'),
- saveChatHistorySummary: () => stub('saveChatHistorySummary'),
- getChatHistorySummary: () => stub('getChatHistorySummary'),
- saveChatHistoryTxt: () => stub('saveChatHistoryTxt'),
- extractChatHistory: () => stub('extractChatHistory'),
- readLastMessage: () => stub('readLastMessage'),
- ocrLastMessage: () => stub('ocrLastMessage'),
- getCachedScreenshot: () => stub('getCachedScreenshot'),
- captureScreenshot: () => stub('captureScreenshot'),
- async readImageFileAsBase64(filePath) {
- try {
- const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath)
- const buf = fs.readFileSync(fullPath)
- const data = buf.toString('base64')
- return { success: true, data }
- } catch (e) {
- return { success: false, error: e.message }
- }
- },
- matchImageRegionLocation: () => stub('matchImageRegionLocation'),
- cropAndSaveImage: () => stub('cropAndSaveImage'),
- async saveBase64Image(base64, filePath) {
- try {
- const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectRoot, filePath)
- const dir = path.dirname(fullPath)
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
- const buf = Buffer.from(base64, 'base64')
- fs.writeFileSync(fullPath, buf)
- return { success: true }
- } catch (e) {
- return { success: false, error: e.message }
- }
- }
- }
- module.exports = nodeApi
|