|
|
七猫阅读赚钱自动刷框架
- // =============================================================================
- // 通用阅读赚钱自动刷框架(七猫/番茄/快音等通用版)
- // 平台: AIWROK Android (ES5 严格语法)
- // 原理: 翻页阅读 + 看广告领奖励 + 防检测随机化
- // 特点: 每个App只需配置包名和翻页方向,框架自动处理其余逻辑
- //
- // 支持App:
- // 1. 七猫小说 com.kmxs.reader 翻页方向: horizontal(左右)
- // 2. 番茄免费小说 com.dragon.read 翻页方向: horizontal(左右)
- // 3. 快音 com.kuaiyin.x 翻页方向: vertical(上下)
- // 4. 抖音极速版 com.ss.android.ugc.aweme.lite 翻页方向: vertical(上下)
- // 5. 自定义App (在APP表里添加即可)
- //
- // 防检测策略:
- // - 随机翻页间隔 (8~18秒,每个页面不同)
- // - 随机停留时长 (在页面上多停一会再翻)
- // - 随机滑动轨迹 (起点/终点都有微小偏移)
- // - 随机滑动速度 (300~700ms之间)
- // - 偶尔做"回翻"动作 (模拟真人回头看一下)
- // - 偶尔做"点赞"动作 (模拟真人互动)
- // - 每30分钟休息一次 (模拟真人离开)
- // =============================================================================
- print.log("╔══════════════════════════════════════════════╗");
- print.log("║ 通用阅读赚钱自动刷框架 v1.0 ║");
- print.log("║ 七猫/番茄/快音等通用 | 防检测随机化 ║");
- print.log("╚══════════════════════════════════════════════╝");
- print.time();
- // =============================================================================
- // 第一部分:配置(只需改这里就能适配新App)
- // =============================================================================
- var CONFIG = {
- // ====== 选择目标App (1~4) ======
- 目标App编号: 1,
- // ====== 运行时长 ======
- 总运行分钟: 60, // 总共跑多久(分钟)
- 每隔N分钟休息: 30, // 每跑N分钟休息一次
- 休息秒数: 30, // 每次休息多少秒
- // ====== 翻页参数(随机范围) ======
- 最小停留秒: 8, // 每页最少停留秒数
- 最大停留秒: 18, // 每页最多停留秒数
- 滑动时长最小毫秒: 300, // 滑动速度下限
- 滑动时长最大毫秒: 700, // 滑动速度上限
- // ====== 防检测概率 ======
- 回翻概率: 0.08, // 8%概率做一次回翻动作
- 点赞概率: 0.05, // 5%概率做一次双击点赞
- 休息概率: 0.03, // 3%概率额外多停一会
- // ====== 看广告领奖励 ======
- 看广告间隔分钟: 5, // 每N分钟去看一次广告领奖励
- 广告等待秒: 30, // 看广告等待秒数
- // ====== 弹窗关键词 ======
- 弹窗关键词: [
- "我知道了", "知道了", "暂不开启", "以后再说", "取消",
- "暂不更新", "下次再说", "拒绝", "不允许", "跳过", "关闭",
- "暂不领取", "稍后再说", "不再提示", "暂不签到", "继续阅读",
- "暂不参加", "不了谢谢", "以后再领", "下次再领"
- ],
- // ====== 阅读页特征文字(多个满足一个即认为在阅读页) ======
- 阅读页特征: ["目录", "上一章", "下一章", "书签", "设置", "夜间", "亮度", "字体"],
- // ====== 模式 ======
- 优先使用Agent模式: true,
- 调试模式: true,
- // ====== App表(添加新App只需在这里加一行) ======
- App表: [
- {
- 编号: 1,
- 名称: "七猫小说",
- 包名: "com.kmxs.reader",
- 翻页方向: "horizontal", // horizontal=左右翻页(小说), vertical=上下滑动(视频)
- 翻页起点X: 0.80, // 水平翻页: 从右侧往左滑 (起点X) 七猫阅读区0~1.0
- 翻页起点Y: 0.45, // 翻页起点Y 避开顶部菜单和底部进度条
- 翻页终点X: 0.20, // 水平翻页: 终点X
- 翻页终点Y: 0.45, // 翻页终点Y
- 福利页坐标: [0.5, 0.976], // 底部"福利"Tab (实测XML: centerPercent=[0.5,0.9766])
- 广告按钮文字: "看视频领金币", // 看广告按钮上的文字
- 启动后等待秒: 8,
- 阅读页检测文字: "目录", // 在阅读页能看到的文字(判断是否在阅读界面)
- 需要找书: true, // 七猫书架可能为空,需要去书城找书
- 书城Tab坐标: [0.306, 0.976], // 底部"书城"Tab (实测XML: centerPercent=[0.3056,0.9766])
- 书架Tab坐标: [0.111, 0.976], // 底部"书架"Tab (实测XML: centerPercent=[0.1111,0.9766])
- 点击中央呼出菜单: false // 七猫阅读页菜单是常驻的,不需要点中央呼出/隐藏
- },
- {
- 编号: 2,
- 名称: "番茄免费小说",
- 包名: "com.dragon.read",
- 翻页方向: "horizontal",
- 翻页起点X: 0.75,
- 翻页起点Y: 0.50,
- 翻页终点X: 0.25,
- 翻页终点Y: 0.50,
- 福利页坐标: [0.5, 0.976],
- 广告按钮文字: "看视频领金币",
- 启动后等待秒: 8,
- 阅读页检测文字: "目录",
- 需要找书: true,
- 书城Tab坐标: [0.3, 0.976],
- 书架Tab坐标: [0.1, 0.976],
- 点击中央呼出菜单: false
- },
- {
- 编号: 3,
- 名称: "快音",
- 包名: "com.kuaiyin.x",
- 翻页方向: "vertical", // 上下滑动(短视频类)
- 翻页起点X: 0.35, // 左侧滑动避开全屏按钮
- 翻页起点Y: 0.78,
- 翻页终点X: 0.35,
- 翻页终点Y: 0.22,
- 福利页坐标: [0.5, 0.976],
- 广告按钮文字: "看视频领金币",
- 启动后等待秒: 8,
- 阅读页检测文字: null, // 短视频类不需要检测阅读页
- 需要找书: false,
- 书城Tab坐标: null,
- 书架Tab坐标: null,
- 点击中央呼出菜单: false
- },
- {
- 编号: 4,
- 名称: "抖音极速版",
- 包名: "com.ss.android.ugc.aweme.lite",
- 翻页方向: "vertical",
- 翻页起点X: 0.35,
- 翻页起点Y: 0.78,
- 翻页终点X: 0.35,
- 翻页终点Y: 0.22,
- 福利页坐标: [0.9, 0.5], // 右侧红包挂件
- 广告按钮文字: "看视频赚金币",
- 启动后等待秒: 8,
- 阅读页检测文字: null,
- 需要找书: false,
- 书城Tab坐标: null,
- 书架Tab坐标: null,
- 点击中央呼出菜单: false
- }
- ]
- };
- // =============================================================================
- // 第二部分:引擎(Agent / 无碍 双模式自动切换)
- // =============================================================================
- var Engine = {
- mode: "",
- 屏幕宽: 1080,
- 屏幕高: 2400,
- init: function() {
- print.log("\n【引擎初始化】检测模式...");
- if (CONFIG.优先使用Agent模式) {
- try { if (agent.isOpen()) { this.mode = "agent"; print.log("✓ Agent代理模式"); } } catch(e) {}
- }
- if (this.mode === "") {
- try { if (typeof auto !== 'undefined') { this.mode = "auto"; print.log("✓ 无碍(Accessibility)模式"); } } catch(e) {}
- }
- if (this.mode === "") {
- print.log("✗ 两个模式都不可用!请开启 Agent 或无障碍后再跑");
- return false;
- }
- try {
- if (this.mode === "agent") {
- var raw = "" + agent.shell("wm size");
- var m = raw.match(/(\d+)x(\d+)/);
- this.屏幕宽 = m ? parseInt(m[1]) : 1080;
- this.屏幕高 = m ? parseInt(m[2]) : 2400;
- } else {
- this.屏幕宽 = screen.getScreenWidth();
- this.屏幕高 = screen.getScreenHeight();
- }
- } catch(e) {
- this.屏幕宽 = 1080;
- this.屏幕高 = 2400;
- }
- print.log("✓ 屏幕尺寸:" + this.屏幕宽 + " × " + this.屏幕高);
- return true;
- },
- // 随机整数 [min, max)
- randInt: function(min, max) {
- return Math.floor(min + Math.random() * (max - min));
- },
- // 随机浮点 [0, 1)
- randFloat: function() {
- return Math.random();
- },
- // 随机偏移(在基准值上加微小随机量)
- jitter: function(base, range) {
- return base + (Math.random() - 0.5) * range;
- },
- // 翻页滑动(核心防检测:随机轨迹 + 随机速度 + 随机偏移)
- nextPage: function(app) {
- var x1, y1, x2, y2;
- var dur = this.randInt(CONFIG.滑动时长最小毫秒, CONFIG.滑动时长最大毫秒);
- if (app.翻页方向 === "horizontal") {
- // 水平翻页:从右往左滑(下一页)
- x1 = this.jitter(app.翻页起点X, 0.04);
- y1 = this.jitter(app.翻页起点Y, 0.08);
- x2 = this.jitter(app.翻页终点X, 0.04);
- y2 = this.jitter(app.翻页终点Y, 0.08);
- } else {
- // 垂直翻页:从下往上滑(下一个视频)
- x1 = this.jitter(app.翻页起点X, 0.06);
- y1 = this.jitter(app.翻页起点Y, 0.04);
- x2 = this.jitter(app.翻页终点X, 0.06);
- y2 = this.jitter(app.翻页终点Y, 0.04);
- }
- // 确保坐标在合法范围 [0.02, 0.98]
- x1 = Math.max(0.02, Math.min(0.98, x1));
- y1 = Math.max(0.02, Math.min(0.98, y1));
- x2 = Math.max(0.02, Math.min(0.98, x2));
- y2 = Math.max(0.02, Math.min(0.98, y2));
- if (CONFIG.调试模式) {
- print.log(" → 翻页 [" + app.翻页方向 + "] (" +
- x1.toFixed(2) + "," + y1.toFixed(2) + ") → (" +
- x2.toFixed(2) + "," + y2.toFixed(2) + ") " + dur + "ms");
- }
- try {
- auto.swipPercent_v2(x1, y1, x2, y2, dur, 0);
- } catch(e) {
- // 兜底:用 agent.shell input swipe
- try {
- var px1 = Math.floor(this.屏幕宽 * x1);
- var py1 = Math.floor(this.屏幕高 * y1);
- var px2 = Math.floor(this.屏幕宽 * x2);
- var py2 = Math.floor(this.屏幕高 * y2);
- agent.shell("input swipe " + px1 + " " + py1 + " " + px2 + " " + py2 + " " + dur);
- } catch(e2) {
- print.log(" ⚠ 翻页失败: " + e2.message);
- }
- }
- sleep.millisecond(this.randInt(300, 600));
- // 小说类App翻页后可能弹出广告/弹窗,关闭它
- if (app.点击中央呼出菜单) {
- // 不再点中央隐藏菜单——七猫阅读页菜单是常驻的(上一章/下一章/目录在底部)
- // 只关闭可能弹出的广告弹窗
- this.closePopup();
- }
- },
- // 回翻(模拟真人回头看一下上一页)
- backPage: function(app) {
- print.log(" ↩ 回翻一页(模拟真人回头)");
- var x1, y1, x2, y2;
- if (app.翻页方向 === "horizontal") {
- // 水平回翻:从左往右滑(上一页)
- x1 = this.jitter(app.翻页终点X, 0.04);
- y1 = this.jitter(app.翻页起点Y, 0.08);
- x2 = this.jitter(app.翻页起点X, 0.04);
- y2 = this.jitter(app.翻页终点Y, 0.08);
- } else {
- // 垂直回翻:从上往下滑(上一个视频)
- x1 = this.jitter(app.翻页终点X, 0.06);
- y1 = this.jitter(app.翻页终点Y, 0.04);
- x2 = this.jitter(app.翻页起点X, 0.06);
- y2 = this.jitter(app.翻页起点Y, 0.04);
- }
- try {
- auto.swipPercent_v2(x1, y1, x2, y2, this.randInt(400, 800), 0);
- } catch(e) {
- try {
- var px1 = Math.floor(this.屏幕宽 * x1);
- var py1 = Math.floor(this.屏幕高 * y1);
- var px2 = Math.floor(this.屏幕宽 * x2);
- var py2 = Math.floor(this.屏幕高 * y2);
- agent.shell("input swipe " + px1 + " " + py1 + " " + px2 + " " + py2 + " 600");
- } catch(e2) {}
- }
- sleep.millisecond(this.randInt(500, 1000));
- },
- // 双击点赞(模拟真人双击屏幕点赞)
- doubleTap: function() {
- print.log(" ❤ 双击点赞");
- var x = this.jitter(0.5, 0.1);
- var y = this.jitter(0.4, 0.1);
- try {
- auto.clickPercent(x, y);
- sleep.millisecond(80);
- auto.clickPercent(x, y);
- } catch(e) {
- try {
- var px = Math.floor(this.屏幕宽 * x);
- var py = Math.floor(this.屏幕高 * y);
- agent.shell("input tap " + px + " " + py);
- sleep.millisecond(80);
- agent.shell("input tap " + px + " " + py);
- } catch(e2) {}
- }
- sleep.millisecond(this.randInt(300, 600));
- },
- // 点击百分比坐标
- clickPercent: function(px, py) {
- try {
- auto.clickPercent(px, py);
- } catch(e) {
- try {
- agent.shell("input tap " + Math.floor(this.屏幕宽 * px) + " " + Math.floor(this.屏幕高 * py));
- } catch(e2) {}
- }
- sleep.millisecond(this.randInt(600, 1000));
- },
- // 返回键
- back: function() {
- try {
- if (this.mode === "agent") agent.shell("input keyevent 4");
- else auto.back();
- } catch(e) {}
- sleep.second(1);
- },
- // Home键
- home: function() {
- try {
- if (this.mode === "agent") agent.shell("input keyevent 3");
- else auto.home();
- } catch(e) {}
- sleep.second(2);
- },
- // 启动 App
- openApp: function(pkg, name) {
- print.log(" → 启动:" + name + "(" + pkg + ")");
- try {
- app.resetApp(pkg);
- print.log("✓ 启动APP:" + pkg);
- return true;
- } catch(e) {
- print.log(" ⚠ app.resetApp 失败:" + e.message + ",尝试 monkey 兜底");
- try {
- agent.shell("monkey -p " + pkg + " -c android.intent.category.LAUNCHER 1");
- return true;
- } catch(e2) {
- print.log(" ✗ 启动完全失败:" + e2.message);
- return false;
- }
- }
- },
- // 关闭弹窗(全用 nodeSearch,不依赖 findText)
- closePopup: function() {
- var words = CONFIG.弹窗关键词;
- try {
- var nodes = auto.nodeSearch(1000);
- if (!nodes) return false;
- // 第一轮:精确匹配关键词
- for (var i = 0; i < words.length; i++) {
- try {
- var filtered = nodes.text(words[i]);
- if (filtered && filtered.count() > 0) {
- var node = filtered.getNode(0);
- if (node && this.clickNode(node, words[i], "弹窗")) {
- return true;
- }
- }
- } catch(e2) {}
- }
- // 第二轮:模糊匹配关键词
- for (var j = 0; j < words.length; j++) {
- try {
- var inc = nodes.textIncludes(words[j]);
- if (inc && inc.count() > 0) {
- var node2 = inc.getNode(0);
- if (node2 && this.clickNode(node2, words[j], "模糊弹窗")) {
- return true;
- }
- }
- } catch(e3) {}
- }
- // 第三轮:关闭悬浮广告/红包(通过节点ID,带包名前缀)
- var pkg = "";
- try { pkg = app.getTopPackName() || ""; } catch(e5) {}
- var closeIds = [
- (pkg ? pkg + ":id/" : "") + "close_red_packet",
- (pkg ? pkg + ":id/" : "") + "close",
- (pkg ? pkg + ":id/" : "") + "iv_close",
- (pkg ? pkg + ":id/" : "") + "btn_close",
- (pkg ? pkg + ":id/" : "") + "img_close",
- "close_red_packet", "close", "iv_close", "btn_close", "img_close"
- ];
- for (var k = 0; k < closeIds.length; k++) {
- try {
- var idNode = nodes.id(closeIds[k]);
- if (idNode && idNode.count() > 0) {
- var node3 = idNode.getNode(0);
- if (node3 && this.clickNode(node3, closeIds[k], "浮窗ID")) {
- return true;
- }
- }
- } catch(e4) {}
- }
- } catch(e) {
- if (CONFIG.调试模式) print.log(" - 弹窗检测异常: " + e.message);
- }
- return false;
- },
- // 查找并点击文字(精确→模糊→desc匹配,每个都先node.click再坐标fallback)
- findAndClickText: function(keyword) {
- try {
- var nodes = auto.nodeSearch(2000);
- if (!nodes) return false;
- // 1. 精确匹配 text
- try {
- var filtered = nodes.text(keyword);
- if (filtered && filtered.count() > 0) {
- var node = filtered.getNode(0);
- if (node && this.clickNode(node, keyword)) return true;
- }
- } catch(e1) {}
- // 2. 模糊匹配 textIncludes
- try {
- var inc = nodes.textIncludes(keyword);
- if (inc && inc.count() > 0) {
- var node2 = inc.getNode(0);
- if (node2 && this.clickNode(node2, keyword)) return true;
- }
- } catch(e2) {}
- // 3. desc 精确匹配
- try {
- var descNode = nodes.desc(keyword);
- if (descNode && descNode.count() > 0) {
- var node3 = descNode.getNode(0);
- if (node3 && this.clickNode(node3, keyword, "desc")) return true;
- }
- } catch(e3) {}
- // 4. descIncludes 模糊匹配
- try {
- var descInc = nodes.descIncludes(keyword);
- if (descInc && descInc.count() > 0) {
- var node4 = descInc.getNode(0);
- if (node4 && this.clickNode(node4, keyword, "desc模糊")) return true;
- }
- } catch(e4) {}
- } catch(e) {
- if (CONFIG.调试模式) print.log(" - findAndClickText 异常: " + e.message);
- }
- return false;
- },
- // 点击节点(先尝试node.click(),失败则用坐标点击)
- // AIWROK节点对象的 text/centerPercent/center 都是属性不是方法,用node.xxx不是node.xxx()
- clickNode: function(node, label, matchType) {
- matchType = matchType || "text";
- try {
- // 先尝试 node.click()
- node.click();
- print.log(" ✓ 点击" + matchType + "【" + label + "】(node.click)");
- sleep.second(1);
- return true;
- } catch(e) {
- // node.click() 失败,用坐标点击
- try {
- // 获取节点的 centerPercent 属性(注意:是属性不是方法)
- var cp = null;
- try { cp = node.centerPercent; } catch(e3) {}
- if (cp) {
- // centerPercent 格式 "[0.5,0.85]"
- var m = ("" + cp).match(/([\d.]+),([\d.]+)/);
- if (m) {
- var px = parseFloat(m[1]);
- var py = parseFloat(m[2]);
- this.clickPercent(px, py);
- print.log(" ✓ 坐标点击" + matchType + "【" + label + "】(" + px + "," + py + ")");
- return true;
- }
- }
- // 兜底:用 center 属性
- var ct = null;
- try { ct = node.center; } catch(e4) {}
- if (ct) {
- var m2 = ("" + ct).match(/([\d.]+),([\d.]+)/);
- if (m2) {
- var cx = parseInt(m2[1]);
- var cy = parseInt(m2[2]);
- var px2 = cx / this.屏幕宽;
- var py2 = cy / this.屏幕高;
- this.clickPercent(px2, py2);
- print.log(" ✓ 坐标点击" + matchType + "【" + label + "】(" + px2 + "," + py2 + ")");
- return true;
- }
- }
- // 兜底2:用 boundsPercent 属性
- var bp = null;
- try { bp = node.boundsPercent; } catch(e5) {}
- if (bp) {
- // boundsPercent 格式 "[0.1,0.2][0.5,0.8]" 取中心
- var m3 = ("" + bp).match(/\[([\d.]+),([\d.]+)\]\[([\d.]+),([\d.]+)\]/);
- if (m3) {
- var px3 = (parseFloat(m3[1]) + parseFloat(m3[3])) / 2;
- var py3 = (parseFloat(m3[2]) + parseFloat(m3[4])) / 2;
- this.clickPercent(px3, py3);
- print.log(" ✓ 坐标点击" + matchType + "【" + label + "】(" + px3 + "," + py3 + ")");
- return true;
- }
- }
- } catch(e2) {
- if (CONFIG.调试模式) print.log(" - clickNode坐标fallback也失败: " + e2.message);
- }
- }
- return false;
- },
- // 判断当前是否在阅读页/视频页
- // 优先用阅读页特有的ID检测(最准确),文字特征只作为辅助
- isOnReadingPage: function(app) {
- if (!app.阅读页检测文字) return true; // 不需要检测的直接返回true
- try {
- var nodes = auto.nodeSearch(1500);
- if (!nodes) return true; // nodeSearch失败时不阻断流程
- // 1. 优先检测阅读页特有的ID(最准确,不会误判)
- try {
- var pkg = app.包名;
- var ids = [
- pkg + ":id/read_progress_layout",
- pkg + ":id/book_directory",
- pkg + ":id/btn_progress_left",
- pkg + ":id/btn_progress_right",
- pkg + ":id/read_blank_area",
- pkg + ":id/read_page_container",
- pkg + ":id/read_view",
- pkg + ":id/tv_read_progress"
- ];
- for (var j = 0; j < ids.length; j++) {
- var idNode = nodes.id(ids[j]);
- if (idNode && idNode.count() > 0) return true;
- }
- } catch(e3) {}
- // 2. 再用文字特征辅助判断(注意:要同时满足多个特征才算,避免误判)
- var matchCount = 0;
- var features = CONFIG.阅读页特征;
- for (var i = 0; i < features.length; i++) {
- try {
- var matched = nodes.text(features[i]);
- if (matched && matched.count() > 0) {
- matchCount++;
- // 至少匹配2个阅读页特征文字才认定(减少误判)
- if (matchCount >= 2) return true;
- }
- } catch(e) {}
- }
- // 3. 检测主检测文字 + 至少1个其他特征
- try {
- var main = nodes.text(app.阅读页检测文字);
- if (main && main.count() > 0 && matchCount >= 1) return true;
- } catch(e2) {}
- return false;
- } catch(e) {
- return true; // 检测失败时假设在阅读页(不中断流程)
- }
- },
- // 等待一段时间,期间周期性关闭弹窗
- smartSleep: function(totalSec, intervalSec) {
- var remain = totalSec;
- while (remain > 0) {
- var step = Math.min(intervalSec, remain);
- sleep.second(step);
- remain -= step;
- this.closePopup();
- }
- }
- };
- // =============================================================================
- // 第三部分:阅读赚钱核心模块
- // =============================================================================
- var ReadingBot = {
- app: null,
- totalReads: 0, // 总翻页次数
- totalAds: 0, // 看广告次数
- totalPopups: 0, // 关弹窗次数
- totalBackReads: 0, // 回翻次数
- totalLikes: 0, // 点赞次数
- totalRests: 0, // 休息次数
- startTime: 0,
- lastAdTime: 0, // 上次看广告的时间戳
- run: function(app) {
- this.app = app;
- this.startTime = Date.now();
- this.lastAdTime = this.startTime;
- var maxMs = CONFIG.总运行分钟 * 60 * 1000;
- print.log("\n╔══════════ 开始阅读赚钱 ══════════╗");
- print.log(" · App:" + app.名称 + "(" + app.包名 + ")");
- print.log(" · 翻页方向:" + app.翻页方向);
- print.log(" · 总时长:" + CONFIG.总运行分钟 + " 分钟");
- print.log(" · 停留:" + CONFIG.最小停留秒 + "~" + CONFIG.最大停留秒 + " 秒");
- print.log(" · 看广告间隔:" + CONFIG.看广告间隔分钟 + " 分钟");
- print.log("╚══════════════════════════════════╝");
- // 1. 启动App
- print.log("\n[步骤1] 启动App...");
- if (!Engine.openApp(app.包名, app.名称)) {
- print.log("✗ 启动失败,退出");
- return;
- }
- sleep.second(app.启动后等待秒 || 8);
- Engine.closePopup();
- // 2. 确保在阅读页/视频页
- print.log("[步骤2] 确认阅读页面...");
- if (app.需要找书 && !Engine.isOnReadingPage(app)) {
- // 书架可能为空或不在阅读页,需要找一本书
- print.log(" → 不在阅读页,需要找一本书进入阅读...");
- this.findBookAndRead(app);
- } else if (!Engine.isOnReadingPage(app)) {
- // 不需要找书的App(如短视频),点中央进入
- print.log(" ⚠ 不在阅读页,尝试点击屏幕中央进入...");
- Engine.clickPercent(0.5, 0.5);
- sleep.second(2);
- Engine.closePopup();
- }
- if (Engine.isOnReadingPage(app)) {
- print.log("✓ 已进入阅读页面");
- } else {
- print.log("⚠ 未确认在阅读页,但仍继续尝试翻页...");
- }
- // 3. 主循环
- print.log("\n[步骤3] 开始自动翻页阅读...\n");
- while ((Date.now() - this.startTime) < maxMs) {
- var elapsedMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
- var remainMin = (CONFIG.总运行分钟 - elapsedMin).toFixed(1);
- // === A. 检查是否该休息了 ===
- if (CONFIG.每隔N分钟休息 > 0 &&
- (Date.now() - this.startTime) > CONFIG.每隔N分钟 * 60 * 1000 * (this.totalRests + 1)) {
- this.totalRests++;
- var restSec = Engine.randInt(CONFIG.休息秒数, CONFIG.休息秒数 + 20);
- print.log("\n[" + elapsedMin + "m] ☕ 第" + this.totalRests + "次休息 " + restSec + "秒");
- Engine.smartSleep(restSec, 10);
- print.log(" ✓ 休息完毕,继续阅读");
- continue;
- }
- // === B. 检查是否该看广告了 ===
- var sinceAd = Date.now() - this.lastAdTime;
- if (sinceAd >= CONFIG.看广告间隔分钟 * 60 * 1000) {
- this.watchAd();
- this.lastAdTime = Date.now();
- // 看完广告后确保回到阅读页
- this.ensureReadingPage();
- continue;
- }
- // === C. 随机停留看当前页 ===
- var staySec = Engine.randInt(CONFIG.最小停留秒, CONFIG.最大停留秒);
- // 小概率额外多停一会(模拟真人被内容吸引)
- if (Engine.randFloat() < CONFIG.休息概率) {
- staySec += Engine.randInt(10, 30);
- if (CONFIG.调试模式) print.log(" 📖 这段内容不错,多看 " + (staySec - CONFIG.最小停留秒) + " 秒");
- }
- if (CONFIG.调试模式) {
- print.log("[" + elapsedMin + "m] 第" + (this.totalReads + 1) + "页 停留" + staySec + "s (剩" + remainMin + "m)");
- } else if ((this.totalReads % 10) === 0) {
- print.log("[" + elapsedMin + "m] 已翻" + this.totalReads + "页 广告" + this.totalAds + "次 弹窗" + this.totalPopups + "次");
- }
- // 停留期间每5秒关一次弹窗
- Engine.smartSleep(staySec, 5);
- // === D. 随机防检测动作 ===
- var dice = Engine.randFloat();
- if (dice < CONFIG.回翻概率) {
- // 小概率回翻一页
- this.totalBackReads++;
- Engine.backPage(this.app);
- sleep.second(Engine.randInt(3, 6));
- // 再翻回来
- Engine.nextPage(this.app);
- this.totalReads++;
- } else if (dice < CONFIG.回翻概率 + CONFIG.点赞概率) {
- // 小概率双击点赞
- this.totalLikes++;
- Engine.doubleTap();
- } else {
- // 正常翻页
- Engine.nextPage(this.app);
- this.totalReads++;
- }
- // 每20页关一次弹窗
- if ((this.totalReads % 20) === 0) {
- if (Engine.closePopup()) this.totalPopups++;
- }
- // 每50页确保在阅读页
- if ((this.totalReads % 50) === 0) {
- if (!Engine.isOnReadingPage(this.app)) {
- print.log(" ⚠ 偏离阅读页,尝试恢复...");
- Engine.back();
- sleep.second(2);
- if (!Engine.isOnReadingPage(this.app)) {
- Engine.clickPercent(0.5, 0.5);
- sleep.second(2);
- }
- Engine.closePopup();
- }
- }
- }
- // 4. 结束统计
- var totalMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
- print.log("\n╔══════════ 阅读结束 ══════════╗");
- print.log("║ 总翻页:" + this.totalReads + " 次");
- print.log("║ 回翻次数:" + this.totalBackReads + " 次");
- print.log("║ 点赞次数:" + this.totalLikes + " 次");
- print.log("║ 看广告:" + this.totalAds + " 次");
- print.log("║ 关弹窗:" + this.totalPopups + " 次");
- print.log("║ 休息次数:" + this.totalRests + " 次");
- print.log("║ 总时长:" + totalMin + " 分钟");
- print.log("╚══════════════════════════════════╝");
- Engine.home();
- },
- // 找一本书进入阅读页面(七猫/番茄等小说类App用)
- // 基于实测XML节点结构编写,支持重试和多策略fallback
- findBookAndRead: function(app) {
- var maxRetry = 4; // 最多尝试4本书
- var readBtns = ["开始阅读", "免费阅读", "立即阅读", "继续阅读", "免费读", "开始免费读"];
- var addBtns = ["加入书架", "添加到书架", "收藏"];
- for (var retry = 0; retry < maxRetry; retry++) {
- print.log("\n [找书] === 第" + (retry + 1) + "/" + maxRetry + "轮 ===");
- try {
- // 0. 已在阅读页则直接返回
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 已在阅读页,无需找书");
- return true;
- }
- // 1. 确保在书城页(点书城Tab)
- if (retry === 0 && app.书城Tab坐标) {
- print.log(" [找书] 1. 点击书城Tab...");
- Engine.clickPercent(app.书城Tab坐标[0], app.书城Tab坐标[1]);
- sleep.second(3);
- Engine.closePopup();
- }
- // 2. 点击一本书:优先用节点ID,兜底用坐标
- var bookClicked = false;
- var bookTitle = "";
- // 策略A: 用 tv_book_title 节点点击(通过坐标fallback确保点击成功)
- try {
- var nodes = auto.nodeSearch(2000);
- if (nodes) {
- var titleNodes = nodes.id(app.包名 + ":id/tv_book_title");
- if (titleNodes && titleNodes.count() > 0) {
- var idx = Math.min(retry, titleNodes.count() - 1);
- var titleNode = titleNodes.getNode(idx);
- if (titleNode) {
- // AIWROK节点text是属性不是方法
- bookTitle = titleNode.text || ("书" + idx);
- // tv_book_title 本身 clickable=false,用 clickNode 坐标fallback
- if (Engine.clickNode(titleNode, bookTitle, "书名")) {
- print.log(" [找书] 2A. 点击书名[" + bookTitle + "] idx=" + idx);
- bookClicked = true;
- }
- }
- }
- }
- } catch(e2a) {
- if (CONFIG.调试模式) print.log(" [找书] 2A异常: " + e2a.message);
- }
- // 策略B: 用 rank_book_cover 节点点击
- if (!bookClicked) {
- try {
- var nodes2 = auto.nodeSearch(2000);
- if (nodes2) {
- var coverIds = [
- app.包名 + ":id/rank_book_cover",
- "rank_book_cover",
- "book_cover", "iv_cover", "item_book"
- ];
- for (var ci = 0; ci < coverIds.length; ci++) {
- try {
- var coverNodes = nodes2.id(coverIds[ci]);
- if (coverNodes && coverNodes.count() > 0) {
- var cIdx = Math.min(retry, coverNodes.count() - 1);
- var coverNode = coverNodes.getNode(cIdx);
- if (coverNode && Engine.clickNode(coverNode, "封面" + cIdx, "书籍")) {
- print.log(" [找书] 2B. 点击封面 id=" + coverIds[ci] + " idx=" + cIdx);
- bookClicked = true;
- break;
- }
- }
- } catch(e2b2) {}
- }
- }
- } catch(e2b) {
- if (CONFIG.调试模式) print.log(" [找书] 2B异常: " + e2b.message);
- }
- }
- // 策略C: 用坐标点击不同的书
- // XML实测: 第1本书 center=[0.1287,0.3932], 第5本书 center=[0.7148,0.3932]
- // 垂直方向间隔约0.1187 (0.3932→0.512→0.6307→0.7495)
- if (!bookClicked) {
- var bookPositions = [
- [0.13, 0.39], [0.13, 0.51], [0.13, 0.63], [0.13, 0.75],
- [0.71, 0.39], [0.71, 0.51], [0.71, 0.63], [0.71, 0.75]
- ];
- var posIdx = retry % bookPositions.length;
- print.log(" [找书] 2C. 坐标点击推荐书 (" + bookPositions[posIdx][0] + "," + bookPositions[posIdx][1] + ")");
- Engine.clickPercent(bookPositions[posIdx][0], bookPositions[posIdx][1]);
- bookClicked = true;
- }
- if (!bookClicked) {
- print.log(" [找书] ⚠ 无法找到可点击的书籍,重试...");
- sleep.second(2);
- continue;
- }
- // 3. 等待页面跳转,然后关闭弹窗
- sleep.second(3);
- Engine.closePopup();
- // 4. 检查是否直接进入了阅读页(有些App会直接打开)
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 直接进入阅读页");
- return true;
- }
- // 5. 在书籍详情页找"开始阅读"按钮或"左滑开始阅读"提示
- print.log(" [找书] 3. 查找阅读入口...");
- var found = false;
- // 5a. 先检测是否有"左滑开始阅读"提示(七猫特有)
- try {
- var nodesTip = auto.nodeSearch(2000);
- if (nodesTip) {
- // 七猫详情页左滑进入阅读
- var tipNode = nodesTip.id(app.包名 + ":id/tv_tip1");
- if (tipNode && tipNode.count() > 0) {
- print.log(" [找书] 检测到【左滑开始阅读】提示,执行左滑手势...");
- // 左滑:从右(0.8)到左(0.2),在屏幕中部
- auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
- sleep.second(3);
- Engine.closePopup();
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 左滑后进入阅读页");
- return true;
- }
- found = true; // 假设左滑成功了
- }
- }
- } catch(e5a) {
- if (CONFIG.调试模式) print.log(" [找书] 5a左滑检测异常: " + e5a.message);
- }
- // 5b. 精确匹配阅读按钮文字
- if (!found) {
- for (var i = 0; i < readBtns.length; i++) {
- if (Engine.findAndClickText(readBtns[i])) {
- print.log(" [找书] ✓ 点击了【" + readBtns[i] + "】");
- found = true;
- break;
- }
- }
- }
- // 5c. 模糊匹配(七猫按钮可能含图标符号)
- if (!found) {
- var readKeywords = ["免费读", "开始读", "立即读", "阅读", "左滑"];
- try {
- var nodes3 = auto.nodeSearch(2000);
- if (nodes3) {
- for (var ri = 0; ri < readKeywords.length; ri++) {
- try {
- var inc = nodes3.textIncludes(readKeywords[ri]);
- if (inc && inc.count() > 0) {
- var rn = inc.getNode(0);
- if (rn && Engine.clickNode(rn, readKeywords[ri], "模糊阅读")) {
- print.log(" [找书] ✓ 模糊点击【" + readKeywords[ri] + "】");
- found = true;
- break;
- }
- }
- } catch(e3) {}
- }
- }
- } catch(e3b) {}
- }
- // 5d. 如果找到"左滑"文字但之前没处理,做左滑手势
- if (!found) {
- try {
- var nodes4 = auto.nodeSearch(2000);
- if (nodes4) {
- var swipeTip = nodes4.textIncludes("左滑");
- if (swipeTip && swipeTip.count() > 0) {
- print.log(" [找书] 检测到【左滑】文字,执行左滑手势...");
- auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
- sleep.second(3);
- Engine.closePopup();
- found = true;
- }
- }
- } catch(e5d) {}
- }
- // 5e. 兜底:如果以上都没找到,先尝试左滑(七猫最常见的入口),再尝试点底部
- if (!found) {
- print.log(" [找书] 3b. 尝试左滑手势进入阅读...");
- auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
- sleep.second(3);
- Engine.closePopup();
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 左滑后进入阅读页");
- return true;
- }
- // 左滑不行,再试点底部常见按钮位置
- print.log(" [找书] 3c. 左滑未成功,点底部按钮位置...");
- Engine.clickPercent(0.5, 0.88);
- sleep.second(2);
- if (!Engine.isOnReadingPage(app)) {
- Engine.clickPercent(0.5, 0.80);
- sleep.second(2);
- }
- }
- // 6. 等待进入阅读页
- sleep.second(3);
- Engine.closePopup();
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 已进入阅读页");
- return true;
- }
- // 7. 可能需要先加入书架才能阅读
- print.log(" [找书] 4. 尝试加入书架...");
- for (var j = 0; j < addBtns.length; j++) {
- if (Engine.findAndClickText(addBtns[j])) {
- print.log(" [找书] ✓ 点击了【" + addBtns[j] + "】");
- sleep.second(2);
- break;
- }
- }
- // 8. 加入书架后再找阅读按钮
- for (var k = 0; k < readBtns.length; k++) {
- if (Engine.findAndClickText(readBtns[k])) {
- print.log(" [找书] ✓ 加入书架后点击了【" + readBtns[k] + "】");
- found = true;
- break;
- }
- }
- sleep.second(3);
- Engine.closePopup();
- if (Engine.isOnReadingPage(app)) {
- print.log(" [找书] ✓ 通过加入书架进入阅读页");
- return true;
- }
- // 9. 本轮失败,返回书城重试
- print.log(" [找书] ⚠ 第" + (retry + 1) + "轮失败,返回书城重试...");
- Engine.back();
- sleep.second(2);
- if (app.书城Tab坐标) {
- Engine.clickPercent(app.书城Tab坐标[0], app.书城Tab坐标[1]);
- sleep.second(2);
- }
- } catch(e) {
- print.log(" [找书] 第" + (retry + 1) + "轮异常: " + e.message);
- try { Engine.back(); sleep.second(2); } catch(e2) {}
- }
- }
- print.log(" [找书] ⚠ " + maxRetry + "轮尝试后仍未进入阅读页");
- return false;
- },
- // 看广告领奖励
- watchAd: function() {
- this.totalAds++;
- var elapsedMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
- print.log("\n[" + elapsedMin + "m] 📺 第" + this.totalAds + "次看广告领奖励");
- try {
- // 0. 先关闭可能挡住的浮窗
- Engine.closePopup();
- // 1. 点底部福利/任务Tab
- print.log(" [广告] 1. 点击福利Tab (" + this.app.福利页坐标[0] + "," + this.app.福利页坐标[1] + ")");
- Engine.clickPercent(this.app.福利页坐标[0], this.app.福利页坐标[1]);
- sleep.second(3);
- Engine.closePopup();
- // 2. 找"看视频领金币"按钮并点击
- print.log(" [广告] 2. 查找【" + this.app.广告按钮文字 + "】");
- var clicked = Engine.findAndClickText(this.app.广告按钮文字);
- if (!clicked) {
- // 尝试其他常见的看广告按钮文字
- var adBtns = ["看视频", "看广告", "领金币", "领取", "看视频奖励", "立即领取"];
- for (var i = 0; i < adBtns.length; i++) {
- if (Engine.findAndClickText(adBtns[i])) {
- clicked = true;
- break;
- }
- }
- }
- if (!clicked) {
- // 兜底:点屏幕中下方常见位置
- print.log(" [广告] 2b. 文字未找到,点常见位置 (0.5, 0.65)");
- Engine.clickPercent(0.5, 0.65);
- sleep.second(2);
- }
- // 3. 等待广告播放
- print.log(" [广告] 3. 等待广告播放 " + CONFIG.广告等待秒 + " 秒...");
- Engine.smartSleep(CONFIG.广告等待秒, 5);
- // 4. 关闭广告结束后的弹窗
- print.log(" [广告] 4. 关闭广告弹窗");
- Engine.closePopup();
- sleep.second(2);
- // 5. 返回阅读页
- print.log(" [广告] 5. 返回阅读页");
- Engine.back();
- sleep.second(2);
- Engine.closePopup();
- } catch(e) {
- print.log(" ⚠ 看广告异常: " + e.message);
- // 尽力恢复
- try { Engine.back(); sleep.second(2); } catch(e2) {}
- try { Engine.closePopup(); } catch(e3) {}
- }
- },
- // 确保回到阅读页
- ensureReadingPage: function() {
- if (Engine.isOnReadingPage(this.app)) return;
- print.log(" [恢复] 不在阅读页,尝试恢复...");
- var tryCount = 0;
- while (tryCount < 3) {
- tryCount++;
- Engine.back();
- sleep.second(2);
- Engine.closePopup();
- if (Engine.isOnReadingPage(this.app)) {
- print.log(" [恢复] ✓ 已回到阅读页");
- return;
- }
- // 点击屏幕中央尝试进入阅读
- Engine.clickPercent(0.5, 0.5);
- sleep.second(2);
- if (Engine.isOnReadingPage(this.app)) {
- print.log(" [恢复] ✓ 已回到阅读页");
- return;
- }
- }
- // 多次返回都不在阅读页,可能回退到了书架/书城
- // 尝试重新找书进入阅读
- if (this.app.需要找书) {
- print.log(" [恢复] 返回到非阅读页,尝试重新找书...");
- this.findBookAndRead(this.app);
- if (Engine.isOnReadingPage(this.app)) {
- print.log(" [恢复] ✓ 重新进入阅读页");
- return;
- }
- }
- print.log(" [恢复] ⚠ 多次尝试后仍未回到阅读页,继续翻页");
- }
- };
- // =============================================================================
- // 第四部分:主入口
- // =============================================================================
- function main() {
- if (!Engine.init()) {
- print.log("✗ 引擎初始化失败,退出");
- return;
- }
- // 获取目标App配置
- var app = null;
- for (var i = 0; i < CONFIG.App表.length; i++) {
- if (CONFIG.App表[i].编号 === CONFIG.目标App编号) {
- app = CONFIG.App表[i];
- break;
- }
- }
- if (!app) {
- print.log("✗ 目标App编号 " + CONFIG.目标App编号 + " 不存在!");
- print.log(" 可用编号:");
- for (var j = 0; j < CONFIG.App表.length; j++) {
- print.log(" " + CONFIG.App表[j].编号 + " = " + CONFIG.App表[j].名称 + "(" + CONFIG.App表[j].包名 + ")");
- }
- return;
- }
- // 先回桌面
- Engine.home();
- sleep.second(2);
- // 运行
- try {
- ReadingBot.run(app);
- } catch(e) {
- print.log("⚠ 主流程异常: " + e.message);
- print.log(" 尝试恢复...");
- try { Engine.home(); } catch(e2) {}
- }
- print.log("\n✓ 脚本完成!");
- print.timeEnd();
- }
- // 启动
- main();
复制代码
|
|