| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /**
- * 语句:adb / keyevent / scroll / swipe / locate / click / press / input / ocr 统一入口
- * type===adb 时按 method 分发到 adb/*.js;其余 type 在本文件内执行,公共逻辑见 utils.js
- */
- const { calculateSwipeCoordinates } = require('./utils.js')
- const types = ['adb', 'keyevent', 'scroll', 'swipe', 'locate', 'click', 'press', 'input', 'ocr']
- const METHOD_HANDLERS = {
- input: require('./input.js'),
- click: require('./click.js'),
- locate: require('./locate.js'),
- swipe: require('./swipe.js'),
- scroll: require('./scroll.js'),
- keyevent: require('./keyevent.js'),
- press: require('./press.js'),
- 'string-press': require('./string-press.js'),
- 'send-img-to-device': require('./send-img-to-device.js'),
- }
- /* ========== 解析入口 ========== */
- function parse(action, parseContext) {
- const type = action.type || 'adb'
- const { extractVarName, resolveValue } = parseContext
- const variableContext = parseContext.variableContext || {}
- const parsed = Object.assign({}, action, { type })
- if (type === 'adb') {
- parsed.method = action.method
- parsed.inVars = action.inVars && Array.isArray(action.inVars) ? action.inVars.map(v => extractVarName(v)) : []
- parsed.outVars = action.outVars && Array.isArray(action.outVars) ? action.outVars.map(v => extractVarName(v)) : []
- parsed.target = action.target
- parsed.value = action.value
- parsed.variable = action.variable
- parsed.clear = action.clear || false
- return parsed
- }
- if (type === 'locate' || type === 'click') return parsed
- if (type === 'input') {
- parsed.clear = action.clear || false
- return parsed
- }
- if (type === 'ocr') {
- parsed.area = action.area
- parsed.avatar = resolveValue(action.avatar, variableContext)
- return parsed
- }
- return parsed
- }
- /* ========== 执行入口 ========== */
- async function execute(action, ctx) {
- const {
- device,
- folderPath,
- resolution,
- variableContext,
- api,
- extractVarName,
- resolveValue,
- logOutVars,
- DEFAULT_SCROLL_DISTANCE = 100,
- } = ctx
- /* --- type: locate --- */
- if (action.type === 'locate') {
- const method = action.method || 'image'
- let position = null
- if (method === 'image') {
- const imagePath = action.target.startsWith('/') || action.target.includes(':') ? action.target : `${folderPath}/${action.target}`
- if (!api?.matchImageAndGetCoordinate) return { success: false, error: '图像匹配 API 不可用' }
- const matchResult = await api.matchImageAndGetCoordinate(device, imagePath, folderPath)
- if (!matchResult.success) return { success: false, error: `图像匹配失败: ${matchResult.error != null ? matchResult.error : 'unknown'}` }
- position = matchResult.clickPosition
- } else if (method === 'text') {
- if (!api?.findTextAndGetCoordinate) return { success: false, error: '文字识别 API 不可用' }
- const matchResult = await api.findTextAndGetCoordinate(device, action.target)
- if (!matchResult.success) return { success: false, error: `文字识别失败: ${matchResult.error != null ? matchResult.error : 'unknown'}` }
- position = matchResult.clickPosition
- } else if (method === 'coordinate') {
- position = Array.isArray(action.target) ? { x: action.target[0], y: action.target[1] } : action.target
- }
- if (action.variable && position) variableContext[action.variable] = position
- return { success: true, result: position }
- }
- /* --- type: click --- */
- if (action.type === 'click') {
- const method = action.method || 'position'
- let position = null
- if (method === 'position') position = resolveValue(action.target, variableContext)
- else if (method === 'image') {
- const imagePath = action.target.startsWith('/') || action.target.includes(':') ? action.target : `${folderPath}/${action.target}`
- if (!api?.matchImageAndGetCoordinate) return { success: false, error: '图像匹配 API 不可用' }
- const matchResult = await api.matchImageAndGetCoordinate(device, imagePath, folderPath)
- if (!matchResult.success) return { success: false, error: `图像匹配失败: ${matchResult.error != null ? matchResult.error : 'unknown'}` }
- position = matchResult.clickPosition
- } else if (method === 'text') {
- if (!api?.findTextAndGetCoordinate) return { success: false, error: '文字识别 API 不可用' }
- const matchResult = await api.findTextAndGetCoordinate(device, action.target)
- if (!matchResult.success) return { success: false, error: `文字识别失败: ${matchResult.error != null ? matchResult.error : 'unknown'}` }
- position = matchResult.clickPosition
- }
- if (!position?.x || !position?.y) return { success: false, error: '无法获取点击位置' }
- if (!api?.sendTap) return { success: false, error: '点击 API 不可用' }
- const tapResult = await api.sendTap(device, position.x, position.y)
- if (!tapResult.success) return { success: false, error: `点击失败: ${tapResult.error != null ? tapResult.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: press --- */
- if (action.type === 'press') {
- const imagePath = `${folderPath}/resources/${action.value}`
- if (!api?.matchImageAndGetCoordinate) return { success: false, error: '图像匹配 API 不可用' }
- const matchResult = await api.matchImageAndGetCoordinate(device, imagePath, folderPath)
- if (!matchResult.success) return { success: false, error: `图像匹配失败: ${matchResult.error != null ? matchResult.error : 'unknown'}` }
- const { x, y } = matchResult.clickPosition
- if (!api?.sendTap) return { success: false, error: '点击 API 不可用' }
- const tapResult = await api.sendTap(device, x, y)
- if (!tapResult.success) return { success: false, error: `点击失败: ${tapResult.error != null ? tapResult.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: input --- */
- if (action.type === 'input') {
- let inputValue = resolveValue(action.value, variableContext)
- if (!inputValue && action.target) {
- const resolvedTarget = resolveValue(action.target, variableContext)
- if (resolvedTarget !== action.target || !action.target.includes(' ')) inputValue = resolvedTarget
- }
- if (!inputValue) return { success: false, error: '输入内容为空' }
- if (!api?.sendText) return { success: false, error: '输入 API 不可用' }
- if (action.clear) {
- for (let i = 0; i < 200; i++) {
- const clearResult = await api.sendKeyEvent(device, '67')
- if (!clearResult.success) break
- await new Promise((r) => setTimeout(r, 10))
- }
- await new Promise((r) => setTimeout(r, 200))
- }
- const textResult = await api.sendText(device, inputValue)
- if (!textResult.success) return { success: false, error: `输入失败: ${textResult.error != null ? textResult.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: ocr --- */
- if (action.type === 'ocr') {
- if (!api?.ocrLastMessage) return { success: false, error: 'OCR API 不可用' }
- const method = action.method || 'full-screen'
- let avatarPath = null
- if (method === 'by-avatar' && action.avatar) {
- const avatarName = resolveValue(action.avatar, variableContext)
- if (avatarName) {
- const folderName = folderPath.split(/[/\\]/).pop()
- avatarPath = `${folderName}/${avatarName}`
- }
- }
- const ocrResult = await api.ocrLastMessage(device, method, avatarPath, action.area, folderPath)
- if (!ocrResult.success) return { success: false, error: `OCR识别失败: ${ocrResult.error != null ? ocrResult.error : 'unknown'}` }
- if (action.variable) variableContext[action.variable] = ocrResult.text || ''
- return { success: true, text: ocrResult.text, position: ocrResult.position }
- }
- /* --- type: keyevent --- */
- if (action.type === 'keyevent') {
- let keyCode = null
- const inVars = action.inVars || []
- if (inVars.length > 0) {
- const keyVar = extractVarName(inVars[0])
- keyCode = variableContext[keyVar] || keyVar
- } else if (action.value) {
- keyCode = resolveValue(action.value, variableContext)
- }
- if (!keyCode) return { success: false, error: 'keyevent 操作缺少按键代码参数' }
- if (keyCode === 'KEYCODE_BACK') keyCode = '4'
- const keyResult = api.sendSystemKey(device, String(keyCode))
- if (!keyResult.success) return { success: false, error: `按键失败: ${keyResult.error != null ? keyResult.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: scroll --- */
- if (action.type === 'scroll') {
- if (!api.sendScroll) return { success: false, error: '滚动 API 不可用' }
- const direction = action.value
- const r = await api.sendScroll(device, direction, resolution.width, resolution.height, DEFAULT_SCROLL_DISTANCE, 500)
- if (!r.success) return { success: false, error: `滚动失败: ${r.error != null ? r.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: swipe --- */
- if (action.type === 'swipe') {
- if (!api.sendSwipe) return { success: false, error: '滑动 API 不可用' }
- const { x1, y1, x2, y2 } = calculateSwipeCoordinates(action.value, resolution.width, resolution.height)
- const r = await api.sendSwipe(device, x1, y1, x2, y2, 300)
- if (!r.success) return { success: false, error: `滑动失败: ${r.error != null ? r.error : 'unknown'}` }
- return { success: true }
- }
- /* --- type: adb 按 method 分发到 adb/*.js --- */
- const method = action.method
- if (!method) return { success: false, error: 'adb 操作缺少 method 参数' }
- const handler = METHOD_HANDLERS[method]
- if (!handler || !handler.run) return { success: false, error: `未知的 adb method: ${method}` }
- return handler.run(action, ctx)
- }
- module.exports = { types, parse, execute }
|