AI_UIAutomation/scripts/reset-except.ts

67 lines
3.9 KiB
TypeScript

/**
* 首页长按卡片→多选→批量删除(快),保留指定设备(woan 房间:Lock 6D / Lock Pro DE / Blind Tilt 42)。
* 每轮:长按第一个"非保留"卡进多选 → 勾选同屏其它非保留卡(跨房间会让 Delete 消失,取消该卡)→ Delete → 确认。循环到只剩保留项。
* 安全开关:RESET_EXCEPT=1。 用法:RESET_EXCEPT=1 PLATFORM=android npx ts-node scripts/reset-except.ts
*/
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../.env') });
import { createDriver } from '../drivers/factory';
const KEEP = (process.env.KEEP_DEVICES || 'Lock 6D,Lock Pro DE,Blind Tilt 42').split(',').map(s => s.trim());
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
// 首页设备卡名 + 中心坐标(nameText / nameTextMeter)
function devices(src: string): { name: string; cx: number; cy: number }[] {
const out: { name: string; cx: number; cy: number }[] = [];
for (const n of src.split('<')) {
if (!/resource-id="[^"]*\/(nameText|nameTextMeter)"/.test(n)) continue;
const t = (n.match(/text="([^"]*)"/) || [])[1];
const b = n.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
if (t && t.trim() && b) out.push({ name: t.trim(), cx: Math.round((+b[1] + +b[3]) / 2), cy: Math.round((+b[2] + +b[4]) / 2) });
}
return out;
}
const isKept = (name: string) => KEEP.some(k => name === k);
async function main() {
if (process.env.RESET_EXCEPT !== '1') { console.error('已阻止。RESET_EXCEPT=1 重跑。'); process.exit(2); }
const driver = createDriver();
await driver.createSession();
const d: any = driver;
const hasDelete = () => driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Delete")');
const exitSelect = async () => { const f = await driver.findElementRaw('-android uiautomator', 'new UiSelector().text("Finish")'); if (f) { await driver.tapElement(f); await sleep(700); } };
try {
console.log('保留:', KEEP.join(', '));
let total = 0;
for (let round = 0; round < 30; round++) {
await driver.goBackToHomepage(); await sleep(1800);
await driver.dismissPopupIfPresent();
const all = devices(await driver.getSource());
const nonKept = all.filter(x => !isKept(x.name));
if (!nonKept.length) { console.log('只剩保留项,结束'); break; }
// 长按第一个非保留卡进多选
const first = nonKept[0];
await d.longPress(first.cx, first.cy, 1.0); await sleep(1000);
if (!(await hasDelete())) { await exitSelect(); console.log(`长按 ${first.name} 未进多选,跳过本轮`); continue; }
let batch = [first.name];
// 勾选同屏其它非保留卡(重新读坐标,避免多选模式下偏移)
for (const x of devices(await driver.getSource()).filter(o => !isKept(o.name) && o.name !== first.name)) {
await driver.tap(x.cx, x.cy); await sleep(400);
if (await hasDelete()) { batch.push(x.name); }
else { await driver.tap(x.cx, x.cy); await sleep(300); } // 跨房间→取消该卡
}
const del = await hasDelete();
if (!del) { await exitSelect(); continue; }
await driver.tapElement(del); await sleep(900);
for (let r = 0; r < 4; r++) { const s = await driver.getSource(); if (/Cancel|取消|delete|删除/i.test(s)) { let c: string | null = null; for (const t of ['Delete', 'OK', '删除', 'Confirm']) { c = await driver.findElementRaw('-android uiautomator', `new UiSelector().text("${t}")`); if (c) break; } if (c) { await driver.tapElement(c); break; } } await sleep(500); }
await sleep(2000);
total += batch.length;
console.log(`本批删 ${batch.length}: ${batch.join(', ')}`);
}
await driver.goBackToHomepage(); await sleep(1500);
console.log(`\n共删 ${total}。剩余:`, devices(await driver.getSource()).map(x => x.name).join(', ') || '(无)');
} finally { await driver.destroySession(); }
}
main().catch(e => { console.error('ERR:', e.message); process.exit(1); });