B2B网络软件
标题:
AIWROK软件番茄小说自动阅读刷金币示例
[打印本页]
作者:
YYPOST群发软件
时间:
1 小时前
标题:
AIWROK软件番茄小说自动阅读刷金币示例
AIWROK软件番茄小说自动阅读刷金币示例
3.png
(1.79 MB, 下载次数: 2)
下载附件
1 小时前
上传
/*
* ============================================================
* AIWROK软件番茄小说自动阅读刷金币示例
* ============================================================
* 功能: 使用无障碍模式UI自动化实现番茄小说自动阅读和领取金币
* 流程: 启动APP → 关闭弹窗 → 检测登录 → 进入阅读 → 自动翻页 → 领取金币
* ============================================================
*/
print.log("===== 番茄小说自动阅读刷金币(无障碍模式)=====");
print.time();
// ========== 初始化 ==========
print.log("\n【步骤1】初始化无障碍模式...");
auto.setDetailedModel();
// ========== 配置参数 ==========
print.log("【步骤2】加载配置参数...");
var config = {
APP包名: "com.dragon.read", // 番茄小说包名
每轮阅读时长分钟: 30, // 每轮自动阅读时长
每页阅读时长秒: 8, // 每页停留阅读时间
滑动间隔秒: 12, // 滑动翻页间隔
金币领取间隔章节: 5, // 每阅读N章领取一次金币
最大重试次数: 3, // 操作失败最大重试次数
连续错误阈值: 3 // 连续错误次数上限,超过则重启APP
};
// ========== 获取屏幕尺寸 ==========
print.log("【步骤3】获取屏幕尺寸...");
var 屏幕宽 = screen.getScreenWidth();
var 屏幕高 = screen.getScreenHeight();
print.log("屏幕尺寸:" + 屏幕宽 + " x " + 屏幕高);
// ========== 工具函数:查找文字节点 ==========
// 功能: 根据关键词列表查找UI元素节点
// 参数: 关键词数组,依次尝试精确匹配和包含匹配
// 返回: 找到的节点对象或null
print.log("\n【步骤4】定义工具函数...");
function 查找文字节点(关键词列表) {
try {
auto.refresh();
var 节点集合 = auto.nodeSearch(2000);
if (节点集合 == '' || 节点集合 == null) {
return null;
}
for (var i = 0; i < 关键词列表.length; i++) {
var 关键词 = 关键词列表[i];
var 文本过滤 = 节点集合.text(关键词);
if (文本过滤 != '' && 文本过滤 != null && 文本过滤.count() > 0) {
var 节点 = 文本过滤.getNode(0);
if (节点 != '' && 节点 != null) {
return 节点;
}
}
var 包含过滤 = 节点集合.textIncludes(关键词);
if (包含过滤 != '' && 包含过滤 != null && 包含过滤.count() > 0) {
var 节点 = 包含过滤.getNode(0);
if (节点 != '' && 节点 != null) {
return 节点;
}
}
}
} catch (e) {
print.log(" ⚠ 查找文字节点出错:" + e.message);
}
return null;
}
// ========== 工具函数:检查文字是否存在 ==========
// 功能: 检测页面是否包含指定关键词(使用||表示或关系)
// 参数: 关键词数组
// 返回: 存在返回true,否则返回false
function 检查文字存在(关键词列表) {
var 规则 = 关键词列表.join("||");
return auto.findText(规则);
}
// ========== 工具函数:安全点击节点 ==========
// 功能: 三级降级策略点击节点,解决clickable=false问题
// 策略: 1)节点可点击→直接点击 2)父/祖父节点可点击→点击父级 3)兜底→坐标点击
// 参数: 要点击的节点对象
// 返回: 点击成功返回true,否则返回false
function 安全点击节点(节点) {
if (!节点 || 节点 == '' || 节点 == null) {
return false;
}
// 第一级:节点本身可点击,直接点击
try {
if (节点.isClickable && 节点.isClickable()) {
节点.click();
return true;
}
} catch (e) {
}
// 第二级:向上查找可点击的父节点或祖父节点
try {
var 父节点 = 节点.getParent();
if (父节点 != '' && 父节点 != null) {
if (父节点.isClickable && 父节点.isClickable()) {
父节点.click();
return true;
}
var 祖父节点 = 父节点.getParent();
if (祖父节点 != '' && 祖父节点 != null) {
if (祖父节点.isClickable && 祖父节点.isClickable()) {
祖父节点.click();
return true;
}
}
}
} catch (e2) {
}
// 第三级:兜底方案,使用坐标点击
try {
var 中心 = 节点.getCenter();
var x = 中心.x();
var y = 中心.y();
auto.clickPoint(x, y);
return true;
} catch (e3) {
print.log(" ⚠ 安全点击全部失败:" + e3.message);
return false;
}
}
// ========== 工具函数:关闭弹窗 ==========
// 功能: 自动识别并关闭各种弹窗和广告
// 处理: 1)点击赚钱浮窗(特殊处理) 2)通用弹窗(知道了、关闭等)
// 返回: 成功关闭返回true,否则返回false
function 关闭弹窗() {
var 弹窗关键词 = [
"我知道了", "知道了", "关闭", "暂不开启", "以后再说",
"取消", "暂不更新", "下次再说", "拒绝", "不允许", "跳过", "领取", "收下"
];
// 优先处理"点击赚钱"浮窗(需要查找带Close描述的可点击节点)
if (auto.findText("点击赚钱")) {
try {
var 节点集 = auto.nodeSearch(1000);
if (节点集 != '' && 节点集 != null) {
var 可点击节点 = 节点集.clickable(true);
if (可点击节点 != '' && 可点击节点 != null) {
for (var i = 0; i < 可点击节点.count(); i++) {
var n = 可点击节点.getNode(i);
if (n && n.getDesc) {
var d = "" + n.getDesc();
if (d.indexOf("Close") >= 0 || d.indexOf("关闭") >= 0) {
n.click();
print.log(" ✓ 关闭点击赚钱浮窗");
sleep.second(秒 = 1);
return true;
}
}
}
}
}
} catch (e) {
}
}
// 处理通用弹窗
var 检测结果 = auto.findText(弹窗关键词.join("||"));
if (!检测结果) {
return false;
}
for (var i = 0; i < 弹窗关键词.length; i++) {
var 关键词 = 弹窗关键词[i];
var 节点 = 查找文字节点([关键词]);
if (节点) {
try {
if (安全点击节点(节点)) {
print.log(" ✓ 关闭弹窗:点击【" + 关键词 + "】");
sleep.second(秒 = 1);
return true;
}
} catch (e) {
print.log(" ⚠ 点击弹窗按钮失败:" + e.message);
}
}
}
return false;
}
// ========== 工具函数:检测是否在阅读页 ==========
// 功能: 判断当前页面是否为小说阅读界面
// 依据: 检查章节进度(如"1/23351")、章节标题(如"第1章")、金币领取提示
// 返回: 在阅读页返回true,否则返回false
function 检测是否在阅读页() {
try {
// 检查章节进度文字(格式如 "1/23351")
var 进度结果 = auto.findText("/");
if (进度结果) {
// 进一步验证:检查是否有章节号
if (auto.findText("第")) {
return true;
}
}
// 检查金币领取提示
if (auto.findText("金币")) {
return true;
}
// 检查阅读页底部状态栏(章节进度)
try {
var 节点集合 = auto.nodeSearch(500);
if (节点集合 != '' && 节点集合 != null) {
// 检查j4g节点(章节进度)
var 进度节点 = 节点集合.id("com.dragon.read:id/j4g");
if (进度节点 != '' && 进度节点 != null && 进度节点.count() > 0) {
return true;
}
// 检查阅读页文本特征
var 文本进度 = 节点集合.textIncludes("/");
if (文本进度 != '' && 文本进度 != null && 文本进度.count() > 0) {
// 有斜杠可能是进度如 "1/23351"
var 节点 = 文本进度.getNode(0);
if (节点) {
var 文本 = "" + 节点.getText();
if (文本.indexOf("/") > 0 && 文本.length < 20) {
return true;
}
}
}
}
} catch (e) {
}
// 检查阅读页标题特征
if (auto.findText("章")) {
return true;
}
} catch (e) {
print.log(" ⚠ 检测阅读页出错:" + e.message);
}
return false;
}
// ========== 工具函数:检测是否在金币页面 ==========
// 功能: 判断当前页面是否为金币领取界面
// 返回: 在金币页返回true,否则返回false
function 检测是否在金币页面() {
try {
return 检查文字存在(["金币", "奖励", "领取金币", "去领取"]);
} catch (e) {
print.log(" ⚠ 检测金币页面出错:" + e.message);
return false;
}
}
// ========== 核心函数:领取金币 ==========
// 功能: 领取阅读页顶部的金币奖励
// 流程: 1)只点击"点击领"开头的节点 2)领取后自动返回
// 说明: 不点击"去赚更多",因为会进入赚钱页面
// 返回: 领取成功返回true,否则返回false
function 领取金币() {
print.log("\n📦 尝试领取金币");
// 只查找 "点击领" 开头的节点(这是直接领取金币的入口)
// "去赚更多" 会进入赚钱页面,不点击
var 金币入口节点 = null;
var 提示文字 = "";
// 方法1: 精准查找 "点击领" 开头的TextView
try {
var 节点集合 = auto.nodeSearch(3000);
if (节点集合 != '' && 节点集合 != null) {
// 精准匹配:textIncludes("点击领") + className("android.widget.TextView")
var 结果1 = 节点集合.textIncludes("点击领").className("android.widget.TextView");
if (结果1 != '' && 结果1 != null && 结果1.count() > 0) {
金币入口节点 = 结果1.getNode(0);
提示文字 = "" + 金币入口节点.getText();
print.log(" 找到金币入口:" + 提示文字);
}
}
} catch (e) {
print.log(" ⚠ 精准查找出错:" + e.message);
}
// 方法2: 如果没找到"点击领",检查是否在赚钱页面
if (!金币入口节点) {
// 检测是否在赚钱页面(特征:有"新人福利""提现""领现金"等文字)
var 在赚钱页面 = false;
try {
var 检查集合 = auto.nodeSearch(1000);
if (检查集合 != '' && 检查集合 != null) {
// 检测赚钱页面特征
var 新人福利节点 = 检查集合.textIncludes("新人福利");
var 提现节点 = 检查集合.textIncludes("提现");
var 领现金节点 = 检查集合.textIncludes("领现金");
if ((新人福利节点 != '' && 新人福利节点 != null && 新人福利节点.count() > 0) ||
(提现节点 != '' && 提现节点 != null && 提现节点.count() > 0) ||
(领现金节点 != '' && 领现金节点 != null && 领现金节点.count() > 0)) {
在赚钱页面 = true;
print.log(" ⚠ 当前在赚钱页面,返回阅读页");
}
}
} catch (e) {
}
// 如果在赚钱页面,返回后跳过本次领取
if (在赚钱页面) {
auto.back();
sleep.second(秒 = 1);
return false;
}
// 没找到金币入口,可能是已经领完了
print.log(" ℹ 暂无金币可领(已领完或未达到领取条件)");
return false;
}
// 找到了"点击领"节点,点击领取
try {
// 点击金币入口
安全点击节点(金币入口节点);
print.log("✓ 点击领取:" + 提示文字);
sleep.second(秒 = 2);
// 检查是否领取成功(查看是否还有"点击领"节点)
try {
var 验证集合 = auto.nodeSearch(1000);
if (验证集合 != '' && 验证集合 != null) {
var 验证结果 = 验证集合.textIncludes("点击领");
if (验证结果 == '' || 验证结果 == null || 验证结果.count() == 0) {
print.log("✓ 金币领取成功");
}
}
} catch (e) {
}
// 关闭领取后的弹窗(如果有)
关闭弹窗();
// 返回阅读页面
auto.back();
sleep.second(秒 = 2);
// 如果返回后还在赚钱页面,再按一次返回
try {
var 检查集合2 = auto.nodeSearch(1000);
if (检查集合2 != '' && 检查集合2 != null) {
var 新人福利节点2 = 检查集合2.textIncludes("新人福利");
if (新人福利节点2 != '' && 新人福利节点2 != null && 新人福利节点2.count() > 0) {
auto.back();
sleep.second(秒 = 1);
}
}
} catch (e) {
}
return true;
} catch (e) {
print.log("✗ 领取金币失败:" + e.message);
// 出错则返回
auto.back();
sleep.second(秒 = 2);
return false;
}
}
// ========== 核心函数:翻页阅读 ==========
// 功能: 向左滑动翻页到下一章
// 说明: 番茄小说阅读页采用左滑翻页方式
function 翻页阅读() {
print.log("▶ 翻页到下一章");
auto.drag(
Math.floor(屏幕宽 * 0.9), // 起点X:屏幕右侧
Math.floor(屏幕高 * 0.5), // 起点Y:屏幕垂直居中
Math.floor(屏幕宽 * 0.1), // 终点X:屏幕左侧
Math.floor(屏幕高 * 0.5), // 终点Y:保持垂直居中
300 // 滑动时长300毫秒
);
print.log("✓ 已翻页");
sleep.second(秒 = 1);
}
// ========== 核心函数:重启APP ==========
// 功能: 使用app.resetApp重启APP,用于异常恢复
// 说明: resetApp会强制停止并重新启动APP,比closeApp+openApp更彻底
// 返回: 重启成功返回true,否则返回false
function 重启APP() {
print.log("✗ 需要重启APP");
try {
// 使用resetApp强制重启(参数为包名)
app.resetApp(config.APP包名);
sleep.second(秒 = 8);
print.log("✓ APP重启成功");
return true;
} catch (e) {
print.log("✗ 重启失败:" + e.message);
return false;
}
}
// ========== 主流程:无限循环阅读 ==========
// 流程: 启动APP → 关闭弹窗 → 检测登录 → 进入阅读 → 循环翻页 → 领取金币 → 统计输出
print.log("\n【步骤5】开始主循环流程...\n");
// 重启标志:当错误恢复时,重启APP已完成打开,跳过步骤5.1的重复打开
var 刚重启过 = false;
for (var 当前循环 = 1; ; 当前循环++) {
print.log("\n===== 第 " + 当前循环 + " 次循环 =====");
// ===== 步骤5.1:启动APP =====
print.log("\n【步骤5.1】启动番茄小说...");
// 如果是刚重启过,直接跳过打开APP(重启APP已经打开了)
if (刚重启过) {
print.log(" (刚重启过,跳过打开APP)");
刚重启过 = false;
sleep.second(秒 = 3);
} else {
var 启动成功 = false;
for (var i = 0; i < config.最大重试次数; i++) {
try {
app.openApp(config.APP包名);
sleep.second(秒 = 6);
// 启动后立即关闭弹窗
关闭弹窗();
// 验证APP是否正常启动
if (检查文字存在(["番茄小说", "书架", "推荐", "阅读"])) {
启动成功 = true;
print.log("✓ APP启动成功");
break;
}
} catch (e) {
print.log("⚠ 启动失败:" + e.message);
sleep.second(秒 = 3);
}
}
}
// 启动失败则等待后重试
if (!启动成功) {
print.log("✗ 多次启动失败,等待5秒后重试");
sleep.second(秒 = 5);
continue;
}
sleep.second(秒 = 3);
// ===== 步骤5.2:检查登录状态 =====
print.log("\n【步骤5.2】检查登录状态...");
var 需要登录 = false;
try {
关闭弹窗();
sleep.millisecond(毫秒 = 500);
// 通过底部导航栏判断登录状态
if (检查文字存在(["书架", "推荐", "我的"])) {
print.log("✓ 已登录");
} else if (检查文字存在(["手机号", "验证码", "密码", "登录"])) {
print.log("⚠ 检测到登录页面,需要手动登录");
需要登录 = true;
}
} catch (e) {
print.log("⚠ 检查登录状态失败:" + e.message);
}
// 需要手动登录时暂停
if (需要登录) {
print.log("⚠ 请手动登录后重新运行");
sleep.second(秒 = 10);
continue;
}
// ===== 步骤5.3:进入阅读页面 =====
// 流程: 点击书城 → 点击书籍 → 左滑进入阅读
print.log("\n【步骤5.3】进入阅读页面...");
var 进入阅读成功 = false;
for (var i = 0; i < config.最大重试次数; i++) {
try {
// 步骤1:点击底部"书城"标签
print.log(" 点击书城...");
auto.back();
sleep.second(秒 = 1);
var 书城节点 = 查找文字节点(["书城"]);
if (!书城节点) {
print.log(" ⚠ 找不到书城,重试");
continue;
}
安全点击节点(书城节点);
print.log(" ✓ 已点击书城");
sleep.second(秒 = 3);
// 步骤2:点击一本书
print.log(" 查找书籍...");
var 书籍点击成功 = false;
try {
// 通过文本特征找书名:先排除榜单,再找实际书名
// 已知的书名节点特征:id="com.dragon.read:id/fuw"
// 榜单文字:推荐榜、完本榜、巅峰榜、新书榜、完整榜单
var 候选书名 = [];
// 方法1:用textIncludes查找所有fuw节点(书名节点的id都是fuw)
var 节点集合 = auto.nodeSearch(1000);
if (节点集合 != '' && 节点集合 != null) {
// 用id精确匹配fuw(书名节点)
var 书名节点列表 = 节点集合.id("com.dragon.read:id/fuw");
if (书名节点列表 != '' && 书名节点列表 != null && 书名节点列表.count() > 0) {
print.log(" 找到 " + 书名节点列表.count() + " 个fuw节点");
for (var j = 0; j < 书名节点列表.count(); j++) {
var 节点 = 书名节点列表.getNode(j);
if (节点) {
var 文字 = "" + 节点.getText();
// 跳过榜单文字,保留真正的书名
if (文字 && 文字.length > 1 &&
文字.indexOf("榜") < 0 &&
文字.indexOf("点评") < 0 &&
文字.indexOf("热度") < 0 &&
文字.indexOf("在看") < 0 &&
文字.indexOf("榜单") < 0) {
候选书名.push({节点: 节点, 文字: 文字});
}
}
}
}
}
// 方法2:直接用findText找一本书名(兜底方案)
if (候选书名.length == 0) {
print.log(" 用findText兜底查找...");
var 已知书名 = ["武极天尊", "九龙神帝", "绝鼎仙缘", "剑断仙缘"];
for (var k = 0; k < 已知书名.length; k++) {
if (auto.findText(已知书名[k])) {
var 节点 = auto.findText(已知书名[k]);
if (节点) {
候选书名.push({节点: 节点, 文字: 已知书名[k]});
print.log(" 找到书名:《" + 已知书名[k] + "》");
break;
}
}
}
}
// 点击找到的第一本书
if (候选书名.length > 0) {
var 书籍 = 候选书名[0];
print.log(" 点击书籍:《" + 书籍.文字 + "》");
// 点击父容器进入详情页
var 父容器 = 书籍.节点.getParent();
if (父容器) {
安全点击节点(父容器);
sleep.second(秒 = 2);
书籍点击成功 = true;
} else {
// 直接点击节点
安全点击节点(书籍.节点);
sleep.second(秒 = 2);
书籍点击成功 = true;
}
}
} catch (e) {
print.log(" ⚠ 查找书籍出错:" + e.message);
}
// 如果节点查找失败,用坐标点击兜底
if (!书籍点击成功) {
print.log(" 坐标点击兜底...");
auto.clickPoint(Math.floor(屏幕宽 * 0.3), Math.floor(屏幕高 * 0.35));
sleep.second(秒 = 2);
书籍点击成功 = true;
}
if (!书籍点击成功) {
print.log(" ⚠ 无法点击书籍,重试");
continue;
}
// 步骤3:在详情页左滑进入阅读
// 关键:番茄小说详情页需要"左滑开始阅读"
print.log(" 左滑进入阅读...");
auto.drag(
Math.floor(屏幕宽 * 0.8), // 起点X:右侧
Math.floor(屏幕高 * 0.5), // 起点Y:中间
Math.floor(屏幕宽 * 0.2), // 终点X:左侧
Math.floor(屏幕高 * 0.5), // 终点Y:中间
500 // 滑动时长
);
sleep.second(秒 = 3);
// 验证是否进入阅读页
if (检测是否在阅读页()) {
print.log("✓ 成功进入阅读页面");
进入阅读成功 = true;
break;
}
// 尝试点击"左滑开始阅读"按钮
var 左滑节点 = 查找文字节点(["左滑开始阅读", "左滑"]);
if (左滑节点) {
安全点击节点(左滑节点);
sleep.second(秒 = 2);
// 再次左滑
auto.drag(
Math.floor(屏幕宽 * 0.8),
Math.floor(屏幕高 * 0.5),
Math.floor(屏幕宽 * 0.2),
Math.floor(屏幕高 * 0.5),
500
);
sleep.second(秒 = 2);
if (检测是否在阅读页()) {
print.log("✓ 成功进入阅读页面");
进入阅读成功 = true;
break;
}
}
print.log(" ⚠ 未能进入阅读页,返回重试");
auto.back();
sleep.second(秒 = 1);
} catch (e) {
print.log("⚠ 进入阅读页面失败:" + e.message);
sleep.second(秒 = 3);
}
}
// 进入阅读失败则重启APP
if (!进入阅读成功) {
print.log("✗ 无法进入阅读页面,重启APP");
重启APP();
continue;
}
// ===== 步骤5.4:自动阅读循环 =====
print.log("\n【步骤5.4】开始自动阅读...");
var 已阅读章节数 = 0;
var 连续错误次数 = 0;
var 阅读开始时间 = Date.now();
var 目标阅读时长毫秒 = config.每轮阅读时长分钟 * 60 * 1000;
// 在目标时长内持续阅读
while (Date.now() - 阅读开始时间 < 目标阅读时长毫秒) {
已阅读章节数++;
print.log("\n--- 第 " + 已阅读章节数 + " 章 ---");
try {
// 步骤A:阅读当前页
print.log("⏳ 阅读中...");
sleep.second(秒 = config.每页阅读时长秒);
// 步骤B:关闭弹窗
关闭弹窗();
sleep.millisecond(毫秒 = 500);
// 步骤C:定时领取金币
if (已阅读章节数 % config.金币领取间隔章节 === 0) {
领取金币();
}
// 步骤D:翻页到下一章
翻页阅读();
// 步骤E:验证页面状态
if (!检测是否在阅读页()) {
print.log("⚠ 不在阅读页面,尝试返回");
auto.back();
sleep.second(秒 = 2);
// 返回后仍不在阅读页则抛异常
if (!检测是否在阅读页()) {
throw new Error("无法回到阅读页面");
}
}
// 阅读成功,重置错误计数
连续错误次数 = 0;
} catch (e) {
// 错误处理与恢复
连续错误次数++;
print.log("⚠ 出错(第" + 连续错误次数 + "次):" + e.message);
sleep.second(秒 = 3);
// 超过错误阈值则重启APP,从头开始
if (连续错误次数 >= config.连续错误阈值) {
print.log("✗ 连续错误" + config.连续错误阈值 + "次,重启APP重新开始");
重启APP();
刚重启过 = true; // 设置标志,下次循环跳过打开APP
连续错误次数 = 0;
print.log("✓ APP已重启,返回主循环重新开始");
break; // 跳出阅读循环,回到主循环重新开始
}
}
}
// ===== 步骤5.5:统计本轮结果 =====
var 实际阅读时长分钟 = (Date.now() - 阅读开始时间) / 1000 / 60;
print.log("\n【步骤5.5】本轮阅读结束统计");
print.log("📊 阅读统计:");
print.log(" 已阅读章节:" + 已阅读章节数 + " 章");
print.log(" 实际阅读时长:" + 实际阅读时长分钟.toFixed(1) + " 分钟");
print.log("\n等待5秒后开始下一轮...");
sleep.second(秒 = 5);
}
复制代码
欢迎光临 B2B网络软件 (http://bbs.niubt.cn/)
Powered by Discuz! X3.2