140 lines
5.1 KiB
JavaScript
140 lines
5.1 KiB
JavaScript
const { exec } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
// Lưu ý: Cần cài đặt thư viện fast-xml-parser để phân tích XML dễ dàng hơn: npm install fast-xml-parser
|
|
const { XMLParser } = require('fast-xml-parser');
|
|
|
|
// Hàm chạy lệnh shell/adb
|
|
function runCommand(cmd) {
|
|
return new Promise((resolve, reject) => {
|
|
exec(cmd, (error, stdout, stderr) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
resolve(stdout.trim());
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 1. Chụp màn hình và lấy cấu trúc giao diện XML từ điện thoại
|
|
async function dumpScreenUI() {
|
|
try {
|
|
console.log("Đang quét màn hình điện thoại...");
|
|
// Dump giao diện hiện tại thành file XML trên điện thoại
|
|
await runCommand('adb shell uiautomator dump /sdcard/window_dump.xml');
|
|
|
|
// Tải file XML đó về thư mục trên Armbian/máy tính
|
|
const localPath = path.join(__dirname, 'window_dump.xml');
|
|
await runCommand(`adb pull /sdcard/window_dump.xml "${localPath}"`);
|
|
|
|
console.log(`Đã tải cấu trúc màn hình về: ${localPath}`);
|
|
return localPath;
|
|
} catch (error) {
|
|
console.error("Lỗi khi dump màn hình:", error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// 2. Đọc và phân tích file XML để tìm tọa độ của chữ/nút trên màn hình
|
|
function findElementCoordinates(xmlFilePath, targetText) {
|
|
const xmlData = fs.readFileSync(xmlFilePath, 'utf8');
|
|
const parser = new XMLParser({ ignoreAttributes: false });
|
|
const jsonObj = parser.parse(xmlData);
|
|
|
|
let foundBounds = null;
|
|
|
|
// Hàm đệ quy duyệt qua toàn bộ các node XML để tìm text trùng khớp
|
|
function traverse(node) {
|
|
if (!node) return;
|
|
|
|
// Kiểm tra node hiện tại có chứa text mục tiêu không
|
|
const textAttr = node['@_text'] || '';
|
|
const descAttr = node['@_content-desc'] || '';
|
|
|
|
if (textAttr.toLowerCase().includes(targetText.toLowerCase()) ||
|
|
descAttr.toLowerCase().includes(targetText.toLowerCase())) {
|
|
foundBounds = node['@_bounds'];
|
|
return;
|
|
}
|
|
|
|
// Nếu node có các node con (node có thể là mảng hoặc object đơn lẻ)
|
|
const children = node.node;
|
|
if (children) {
|
|
if (Array.isArray(children)) {
|
|
for (let child of children) {
|
|
traverse(child);
|
|
if (foundBounds) break;
|
|
}
|
|
} else {
|
|
traverse(children);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bắt đầu duyệt từ node gốc hierarchy
|
|
if (jsonObj.hierarchy && jsonObj.hierarchy.node) {
|
|
traverse(jsonObj.hierarchy.node);
|
|
}
|
|
|
|
if (foundBounds) {
|
|
// bounds có định dạng: "[x1,y1][x2,y2]" (Ví dụ: "[100,200][300,400]")
|
|
const matches = foundBounds.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
|
if (matches) {
|
|
const x1 = parseInt(matches[1]);
|
|
const y1 = parseInt(matches[2]);
|
|
const x2 = parseInt(matches[3]);
|
|
const y2 = parseInt(matches[4]);
|
|
|
|
// Tính tọa độ trung tâm của phần tử để click
|
|
const centerX = Math.floor((x1 + x2) / 2);
|
|
const centerY = Math.floor((y1 + y2) / 2);
|
|
return { x: centerX, y: centerY };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// 3. Điều khiển điện thoại bằng ADB
|
|
async function clickAt(x, y) {
|
|
console.log(`Đang click vào tọa độ: X=${x}, Y=${y}`);
|
|
await runCommand(`adb shell input tap ${x} ${y}`);
|
|
}
|
|
|
|
async function swipe(x1, y1, x2, y2, duration = 300) {
|
|
console.log(`Đang cuộn màn hình từ (${x1}, ${y1}) tới (${x2}, ${y2})`);
|
|
await runCommand(`adb shell input swipe ${x1} ${y1} ${x2} ${y2} ${duration}`);
|
|
}
|
|
|
|
async function inputText(text) {
|
|
console.log(`Đang nhập văn bản: "${text}"`);
|
|
// Gửi text qua ADB shell (Lưu ý: ADB mặc định không hỗ trợ tiếng Việt có dấu tốt)
|
|
await runCommand(`adb shell input text "${text}"`);
|
|
}
|
|
|
|
// 4. Kịch bản chạy mẫu (Ví dụ: Tìm nút "Mua ngay" trên Shopee và click vào)
|
|
async function startAutomation() {
|
|
try {
|
|
// Bước 1: Dump giao diện hiện tại
|
|
const xmlFile = await dumpScreenUI();
|
|
|
|
// Bước 2: Tìm tọa độ của nút có chữ "Mua ngay" hoặc "Mua Với Voucher"
|
|
const coords = findElementCoordinates(xmlFile, "Mua ngay");
|
|
|
|
if (coords) {
|
|
console.log(`Đã tìm thấy phần tử tại tọa độ:`, coords);
|
|
// Bước 3: Click vào phần tử đó
|
|
await clickAt(coords.x, coords.y);
|
|
} else {
|
|
console.log("Không tìm thấy chữ 'Mua ngay' trên màn hình hiện tại.");
|
|
// Thử cuộn màn hình xuống nếu không thấy
|
|
await swipe(500, 1500, 500, 500);
|
|
}
|
|
} catch (error) {
|
|
console.error("Lỗi kịch bản:", error);
|
|
}
|
|
}
|
|
|
|
// Chạy thử nghiệm
|
|
startAutomation();
|