通用找图OCR点字完整脚本骨架
通用找图OCR点字完整脚本骨架
// ============================================================================
//AIWROK 通用「找图 + OCR 点字」完整脚本骨架(案例 #02 配套)
// ============================================================================
//这才是一条正常在跑的脚本:复位 → 主循环 → 每轮 找图/OCR → 命中就点 → 下滑 → 随机等待 → 打日志。
//会持续运行(默认 20 轮、每轮等 1.5~4 秒,几十秒~几分钟),不是一秒结束的自检。
//
//用法:
// 1) 只改【配置区 CFG】:targets 里填你的按钮模板(base64,没有就留 null),words 里填界面真实文字;
// 2) 直接运行,看日志:每一轮做什么、点没点、坐标多少、下一轮什么时候开,全打出来;
// 3) 里面 findPic / ocrFind / doClick / doSwipe 都是可抠走的通用函数,避坑写法已焊死:
// 判 null 不崩、百分比坐标 percentToWidth/Height 转像素、截图用完 recycle。
//
//安全:默认 RUN_CLICK=false 时只“找到并打印坐标”不真点,先空跑验证再放开,防误点。
// ============================================================================
// ============================ 一、配置区(只改这里) ============================
var CFG = {
rounds: 20, // 跑多少轮后自动结束(想一直跑就写很大,如 999999)
runClick: true, // ★先 false 空跑看日志,确认找得对再改 true 真点
scroll: true, // 每轮结束后下滑一屏(养机/信息流常用)
waitMin: 1500, waitMax: 4000, // 每轮随机等待(ms),防封,别设固定值
idleStop: 6, // 连续这么多轮啥都没命中就提前收工(0=不停)
// 模板找图:{名字, base64模板(没有填 null), 搜索区域比例}
targets: [
{ name: "关注按钮", tpl: null, region: },
{ name: "关闭广告X", tpl: null, region: },
],
// OCR 点字兜底:界面里出现这些文字就点它(换成你 App 的真实按钮文案)
words: ["关注", "点赞", "下一条"],
threshold: 0.82, // 找图阈值,找不到先降到 0.75 再试
};
// ============================ 二、通用函数(可整段复制进工程) ============================
function log(msg) { printl("[" + new Date().getTime() % 100000 + "] " + msg); }// printl 才有输出
function getScr() {
var s = { w: 1080, h: 2400 };
try { s.w = screen.getScreenWidth(); s.h = screen.getScreenHeight(); } catch (e) {}
return s;
}
function rand(a, b) { return Math.floor(a + Math.random() * (b - a + 1)); }
function sleepMs(ms) { try { sleep.millisecond(ms); } catch (e) { java.lang.Thread.sleep(ms); } }
// 点击:无障碍 auto.clickPoint → 全局 click 两级降级(不用HID);runClick=false 时只打印
function doClick(x, y, tag) {
x = Math.max(1, Math.min(getScr().w - 1, x));
y = Math.max(1, Math.min(getScr().h - 1, y));
if (!CFG.runClick) { log("占位不点[" + tag + "] (" + x + "," + y + ") —— runClick=false"); return false; }
var ok = false;
try { if (typeof auto !== "undefined" && auto.clickPoint) { auto.clickPoint(x, y); ok = true; } } catch (e) {}
if (!ok) { try { click(x, y); ok = true; } catch (e) {} }
log("点击[" + tag + "] (" + x + "," + y + ") ok=" + ok);
return ok;
}
// 下滑一屏:auto.swip → auto.dispatchGesture 两级降级(无障碍,不用HID)
function doSwipe() {
if (!CFG.scroll) return;
var s = getScr();
var cx = Math.round(s.w * 0.5), y1 = Math.round(s.h * 0.75), y2 = Math.round(s.h * 0.30);
var ok = false;
try { if (typeof auto !== "undefined" && auto.swip) { auto.swip(cx, y1, cx, y2, 600, 0); ok = true; } } catch (e) {}
if (!ok) { try { if (typeof auto !== "undefined" && auto.dispatchGesture) { auto.dispatchGesture(cx, y1, cx, y2, 600); ok = true; } } catch (e) {} }
log("下滑 (" + y1 + "→" + y2 + ") ok=" + ok);
}
// 找图:★判 null + ★百分比转像素(案例 #02 两条命门都在这)
function findPic(templateB64, regionPct, threshold) {
if (!templateB64) return null;
var region = regionPct || ;
var detects = null;
try { detects = auto.findImages(, threshold == null ? CFG.threshold : threshold, 0, 1, region); }
catch (e) { log("findImages异常(当null): " + e); return null; }
if (!detects || detects.length === 0) return null; // 命门①:先判空,找不到返回 null 不崩
var cx = detects.getRect().getCenterX(); // 命门②:这是百分比 0~1
var cy = detects.getRect().getCenterY();
var px = screen.percentToWidth(cx), py = screen.percentToHeight(cy); // 必须换算成像素
return { px: Math.round(px), py: Math.round(py), count: detects.length };
}
// OCR 找字并点击(换分辨率/改版都能点,找图的最佳兜底)
function ocrFindAndClick(word) {
var img = screen.screenShotFull();
try {
var ocr = img.paddleOCR(640, );
var hit = (ocr && ocr.findIncludeText) ? ocr.findIncludeText(word) : null;
if (!hit) return null;
var r = (hit.getRect) ? hit.getRect() : null;
var x, y;
if (r) { x = screen.percentToWidth(r.getCenterX()); y = screen.percentToHeight(r.getCenterY()); }
else { x = null; y = null; }
if (CFG.runClick) { try { hit.click(); } catch (e) {} }
return { x: x, y: y };
} catch (e) { return null; }
finally { try { img.recycle(); } catch (e) {} } // 命门③:用完回收
}
// ============================ 三、主循环(真在跑的那段) ============================
function reset() {
log("复位:回到桌面清残留,再进目标页");
try { if (typeof auto !== "undefined" && auto.home) auto.home(); } catch (e) {}
sleepMs(800);
try { if (typeof back !== "undefined") back(); } catch (e) {}
sleepMs(500);
}
function oneRound(i) {
log("— 第 " + i + " 轮 开始(屏 " + getScr().w + "x" + getScr().h + ",runClick=" + CFG.runClick + ")");
var acted = false;
// 1) 先按模板找图
for (var t = 0; t < CFG.targets.length; t++) {
var tg = CFG.targets;
if (!tg.tpl) continue; // 没配模板跳过
var f = findPic(tg.tpl, tg.region, CFG.threshold);
if (f) { log("找图命中[" + tg.name + "] 共" + f.count + "处 → 像素(" + f.px + "," + f.py + ")"); doClick(f.px, f.py, tg.name); acted = true; }
else { log("找图未命中[" + tg.name + "](不崩,继续)"); }
}
// 2) 找图没中 → OCR 点字兜底
if (!acted) {
for (var w = 0; w < CFG.words.length; w++) {
var o = ocrFindAndClick(CFG.words);
if (o) { log("OCR命中文字「" + CFG.words + "」→ " + (o.x == null ? "已点(未取坐标)" : "像素(" + o.x + "," + o.y + ")")); acted = true; break; }
}
}
if (!acted) log("本轮没找到任何目标 → 下滑换内容");
// 3) 下滑 + 随机等待
doSwipe();
var wait = rand(CFG.waitMin, CFG.waitMax);
log("本轮结束,等待 " + wait + "ms 后进入下一轮\n");
sleepMs(wait);
return acted;
}
function main() {
var s = getScr();
log("========================================");
log(" 启动 通用找图/OCR点击脚本本机 " + s.w + "x" + s.h + "共 " + CFG.rounds + " 轮");
log(" runClick=" + CFG.runClick + "(false=空跑只看日志不真点,验证OK再改true)");
log("========================================");
reset();
var idle = 0;
for (var i = 1; i <= CFG.rounds; i++) {
var acted = oneRound(i);
if (acted) idle = 0; else idle++;
if (CFG.idleStop > 0 && idle >= CFG.idleStop) { log("连续 " + idle + " 轮无命中,提前收工。多半是:目标文字/模板不对,或分辨率变了模板失配 → 见案例 #02 第6节"); break; }
}
log("全部结束。");
}
main();
页:
[1]