UNPKG

multi-automator

Version:
363 lines (362 loc) 10.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.back = exports.enter = exports.changeKeyboard = exports.getDefaultKeyboard = exports.getScreenSize = exports.screenshot = exports.pull = exports.killAppProcess = exports.isAppProcessExist = exports.uninstall = exports.install = exports.startActivity = exports.home = exports.ip = exports.forward = exports.fileExist = exports.isInstalled = exports.checkDeviceSet = exports.checkConnect = exports.devices = exports.shellAsync = exports.shell = exports.getAdb = exports.init = void 0; /** * @desc: adb * @author: john_chen * @date: 2025.02.22 */ const bat_adbkit_1 = __importDefault(require("bat-adbkit")); const config_1 = require("../config"); const ADB_PATH = 'adb'; let adb = null; let ipMap = {}; let screenSizeMap = {}; // 错误处理函数 function handleError(message, err) { throw new Error(`${message}: ${err.message}`); } /** * 初始化 adb client * * @param {string} bin * @param {string} host * @param {number} port * @returns {Object} adb */ function init(bin = ADB_PATH, host = '127.0.0.1', port = 5037) { adb = bat_adbkit_1.default.createClient({ bin, host, port }); return adb; } exports.init = init; /** * 获取 adb client * * @returns {Object} adb */ function getAdb() { if (adb === null) { init(); } return adb; } exports.getAdb = getAdb; /** * 执行 shell 命令 */ async function shell(deviceId, command) { try { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } const res = await adbClient.shell(deviceId, command).then(bat_adbkit_1.default.util.readAll); return (await res.toString()).trim(); } catch (err) { throw new Error(`adb shell fail: ${deviceId}, ${command}, ${err.message}`); } } exports.shell = shell; ; /** * 异步执行 shell 命令 */ async function shellAsync(deviceId, command) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } return await adbClient.shell(deviceId, command); } exports.shellAsync = shellAsync; /** * 获取当前在连的设备列表 * * @return {Promise<DevicesMap>} deviceConnectMap */ async function devices() { try { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } const deviceList = await adbClient.listDevices(); return deviceList.reduce((map, device) => { if (device === null || device === void 0 ? void 0 : device.id) { map[device.id] = { status: 0 }; } return map; }, {}); } catch (err) { handleError('Failed to get devices', err); return {}; } } exports.devices = devices; /** * 检查设备是否可用 */ async function checkConnect(deviceId) { let deviceMap = await devices(); if (!deviceMap[deviceId]) { throw new Error('未连接该设备'); } } exports.checkConnect = checkConnect; ; /** * 检查设备是否开启 USB 调试模式 */ async function checkDeviceSet(deviceId) { try { await shell(deviceId, '[ -r /dev/input/event0 ]'); } catch (err) { throw new Error('设备未开启 `USB 调试(安全设置)`'); } } exports.checkDeviceSet = checkDeviceSet; ; /** * 检查应用是否安装 */ async function isInstalled(deviceId, packageName) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } const res = await adbClient.isInstalled(deviceId, packageName); return res; } exports.isInstalled = isInstalled; /** * 检查文件是否存在 */ async function fileExist(deviceId, path) { try { const res = await shell(deviceId, `ls ${path}`); return res === path; } catch (err) { config_1.logger.error(`[adb.fileExist] Error checking file existence: ${err}`); return false; } } exports.fileExist = fileExist; /** * Forwards socket connections from the ADB server host (local) to the device (remote). */ async function forward(deviceId, local, remote) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } await adbClient.forward(deviceId, local, remote); } exports.forward = forward; /** * 获取本机 IP */ async function ip(deviceId) { if (!ipMap[deviceId]) { let ifconfig = await shell(deviceId, 'ifconfig | grep Bcast'); try { ipMap[deviceId] = ifconfig.split('addr:')[1].split(' Bcast')[0]; } catch (err) { const error = err; throw new Error(`[adb.ip] ${error.stack}`); } } return ipMap[deviceId]; } exports.ip = ip; /** * 返回主页 */ async function home(deviceId) { return await shell(deviceId, 'input keyevent 3'); } exports.home = home; /** * 启动 Activity */ async function startActivity(deviceId, options) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } let res = await adbClient.startActivity(deviceId, options); return res; } exports.startActivity = startActivity; /** * 安装应用 */ async function install(deviceId, appPath) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } await adbClient.install(deviceId, appPath); } exports.install = install; /** * 卸载应用 */ async function uninstall(deviceId, appId) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } await adbClient.uninstall(deviceId, appId); } exports.uninstall = uninstall; /** * 检查进程是否存在 */ async function isAppProcessExist(deviceId, processName) { const psCommands = ['ps', 'ps -ef']; for (const cmd of psCommands) { try { const command = `${cmd} | grep -v 'grep' | grep '${processName}'`; const res = await shell(deviceId, command); if (res) { return true; } } catch (err) { config_1.logger.error(`Error checking process with command "${cmd}": ${err.message}`); } } return false; } exports.isAppProcessExist = isAppProcessExist; /** * 终止 APP 进程 */ async function killAppProcess(deviceId, processName) { await shell(deviceId, `am force-stop ${processName}`); } exports.killAppProcess = killAppProcess; ; /** * 拉取文件 */ async function pull(deviceId, path) { const adbClient = getAdb(); if (!adbClient) { throw new Error('ADB client not initialized'); } return new Promise((resolve, reject) => adbClient .pull(deviceId, path) .then((transfer) => { const bufferList = []; transfer.on('data', (buffer) => bufferList.push(buffer)); transfer.on('error', reject); transfer.on('end', () => resolve(Buffer.concat(bufferList))); }) .catch((err) => reject(err))); } exports.pull = pull; ; /** * 截图 */ async function screenshot(deviceId) { let deviceScreenshotPath = '/sdcard/screenshot.png'; await shell(deviceId, `screencap -p ${deviceScreenshotPath}`); return await pull(deviceId, deviceScreenshotPath); } exports.screenshot = screenshot; ; /** * 获取设备屏幕尺寸 */ async function getScreenSize(deviceId) { if (screenSizeMap[deviceId]) { return screenSizeMap[deviceId]; } else { let res = await shell(deviceId, 'dumpsys window displays | head -n 10'); try { let parts = res.split(' rng=')[0].split(' '); // app=1080x2264: app 占用宽高 let displayInfo = parts[parts.length - 1].split('=')[1].split('x'); let displayWidth = parseInt(displayInfo[0], 10); let displayHeight = parseInt(displayInfo[1], 10); // cur=1080x2340: 屏幕物理宽高 let screenInfo = parts[parts.length - 2].split('=')[1].split('x'); let width = parseInt(screenInfo[0], 10); let height = parseInt(screenInfo[1], 10); let statusBarHeight = height - displayHeight; screenSizeMap[deviceId] = { width, height, displayWidth, displayHeight, statusBarHeight }; return screenSizeMap[deviceId]; } catch (err) { throw new Error(`Get device screen size failed: ${err}`); } } } exports.getScreenSize = getScreenSize; ; /** * 获取默认输入法 */ async function getDefaultKeyboard(deviceId) { let cmd = 'settings get secure default_input_method'; return await shell(deviceId, cmd); } exports.getDefaultKeyboard = getDefaultKeyboard; ; /** * 切换输入法 */ async function changeKeyboard(deviceId, keyboard = 'com.android.adbkeyboard/.AdbIME') { const cmd = `settings put secure default_input_method ${keyboard}`; try { const res = await shell(deviceId, cmd); if (res.includes('Security')) { const errorMessage = '由于权限受限,切换输入法到 adbkeyboard 失败'; config_1.logger.error(`[adb.changeKeyboard] ${errorMessage}`); throw new Error(errorMessage); } return res; } catch (error) { config_1.logger.error(`[adb.changeKeyboard] 执行命令失败: ${error instanceof Error ? error.message : String(error)}`); throw new Error('切换输入法时发生错误'); } } exports.changeKeyboard = changeKeyboard; /** * 回车 */ async function enter(deviceId) { let res = await shell(deviceId, 'input keyevent 66'); if (res) { config_1.logger.error(`[adb.enter] 执行命令: input keyevent 66 失败: ${res}`); return false; } config_1.logger.info(`[adb.enter] 执行命令: input keyevent 66 成功`); return true; } exports.enter = enter; /** * 返回 */ async function back(deviceId) { let res = await shell(deviceId, 'input keyevent 4'); if (res) { config_1.logger.error(`[adb.back] 执行命令: input keyevent 4 失败: ${res}`); return false; } config_1.logger.info(`[adb.back] 执行命令: input keyevent 4 成功`); return true; } exports.back = back;