multi-automator
Version:
Multi terminal automation
363 lines (362 loc) • 10.5 kB
JavaScript
;
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;