B2B网络软件

标题: 七猫阅读赚钱自动刷框架 [打印本页]

作者: YYPOST群发软件    时间: 半小时前
标题: 七猫阅读赚钱自动刷框架
七猫阅读赚钱自动刷框架


七猫阅读赚钱自动刷框架 B2B网络软件

七猫阅读赚钱自动刷框架 B2B网络软件
七猫阅读赚钱自动刷框架 B2B网络软件

  1. // =============================================================================
  2. //  通用阅读赚钱自动刷框架(七猫/番茄/快音等通用版)
  3. //  平台: AIWROK Android (ES5 严格语法)
  4. //  原理: 翻页阅读 + 看广告领奖励 + 防检测随机化
  5. //  特点: 每个App只需配置包名和翻页方向,框架自动处理其余逻辑
  6. //
  7. //  支持App:
  8. //    1. 七猫小说     com.kmxs.reader          翻页方向: horizontal(左右)
  9. //    2. 番茄免费小说  com.dragon.read           翻页方向: horizontal(左右)
  10. //    3. 快音         com.kuaiyin.x             翻页方向: vertical(上下)
  11. //    4. 抖音极速版   com.ss.android.ugc.aweme.lite   翻页方向: vertical(上下)
  12. //    5. 自定义App    (在APP表里添加即可)
  13. //
  14. //  防检测策略:
  15. //    - 随机翻页间隔 (8~18秒,每个页面不同)
  16. //    - 随机停留时长 (在页面上多停一会再翻)
  17. //    - 随机滑动轨迹 (起点/终点都有微小偏移)
  18. //    - 随机滑动速度 (300~700ms之间)
  19. //    - 偶尔做"回翻"动作 (模拟真人回头看一下)
  20. //    - 偶尔做"点赞"动作 (模拟真人互动)
  21. //    - 每30分钟休息一次 (模拟真人离开)
  22. // =============================================================================

  23. print.log("╔══════════════════════════════════════════════╗");
  24. print.log("║  通用阅读赚钱自动刷框架 v1.0                  ║");
  25. print.log("║  七猫/番茄/快音等通用 | 防检测随机化            ║");
  26. print.log("╚══════════════════════════════════════════════╝");
  27. print.time();

  28. // =============================================================================
  29. //  第一部分:配置(只需改这里就能适配新App)
  30. // =============================================================================

  31. var CONFIG = {
  32.     // ====== 选择目标App (1~4) ======
  33.     目标App编号: 1,

  34.     // ====== 运行时长 ======
  35.     总运行分钟: 60,                 // 总共跑多久(分钟)
  36.     每隔N分钟休息: 30,              // 每跑N分钟休息一次
  37.     休息秒数: 30,                   // 每次休息多少秒

  38.     // ====== 翻页参数(随机范围) ======
  39.     最小停留秒: 8,                  // 每页最少停留秒数
  40.     最大停留秒: 18,                 // 每页最多停留秒数
  41.     滑动时长最小毫秒: 300,           // 滑动速度下限
  42.     滑动时长最大毫秒: 700,           // 滑动速度上限

  43.     // ====== 防检测概率 ======
  44.     回翻概率: 0.08,                 // 8%概率做一次回翻动作
  45.     点赞概率: 0.05,                 // 5%概率做一次双击点赞
  46.     休息概率: 0.03,                 // 3%概率额外多停一会

  47.     // ====== 看广告领奖励 ======
  48.     看广告间隔分钟: 5,               // 每N分钟去看一次广告领奖励
  49.     广告等待秒: 30,                  // 看广告等待秒数

  50.     // ====== 弹窗关键词 ======
  51.     弹窗关键词: [
  52.         "我知道了", "知道了", "暂不开启", "以后再说", "取消",
  53.         "暂不更新", "下次再说", "拒绝", "不允许", "跳过", "关闭",
  54.         "暂不领取", "稍后再说", "不再提示", "暂不签到", "继续阅读",
  55.         "暂不参加", "不了谢谢", "以后再领", "下次再领"
  56.     ],

  57.     // ====== 阅读页特征文字(多个满足一个即认为在阅读页) ======
  58.     阅读页特征: ["目录", "上一章", "下一章", "书签", "设置", "夜间", "亮度", "字体"],

  59.     // ====== 模式 ======
  60.     优先使用Agent模式: true,
  61.     调试模式: true,

  62.     // ====== App表(添加新App只需在这里加一行) ======
  63.     App表: [
  64.         {
  65.             编号: 1,
  66.             名称: "七猫小说",
  67.             包名: "com.kmxs.reader",
  68.             翻页方向: "horizontal",      // horizontal=左右翻页(小说), vertical=上下滑动(视频)
  69.             翻页起点X: 0.80,              // 水平翻页: 从右侧往左滑 (起点X) 七猫阅读区0~1.0
  70.             翻页起点Y: 0.45,              // 翻页起点Y 避开顶部菜单和底部进度条
  71.             翻页终点X: 0.20,              // 水平翻页: 终点X
  72.             翻页终点Y: 0.45,              // 翻页终点Y
  73.             福利页坐标: [0.5, 0.976],      // 底部"福利"Tab (实测XML: centerPercent=[0.5,0.9766])
  74.             广告按钮文字: "看视频领金币",    // 看广告按钮上的文字
  75.             启动后等待秒: 8,
  76.             阅读页检测文字: "目录",          // 在阅读页能看到的文字(判断是否在阅读界面)
  77.             需要找书: true,                 // 七猫书架可能为空,需要去书城找书
  78.             书城Tab坐标: [0.306, 0.976],    // 底部"书城"Tab (实测XML: centerPercent=[0.3056,0.9766])
  79.             书架Tab坐标: [0.111, 0.976],    // 底部"书架"Tab (实测XML: centerPercent=[0.1111,0.9766])
  80.             点击中央呼出菜单: false           // 七猫阅读页菜单是常驻的,不需要点中央呼出/隐藏
  81.         },
  82.         {
  83.             编号: 2,
  84.             名称: "番茄免费小说",
  85.             包名: "com.dragon.read",
  86.             翻页方向: "horizontal",
  87.             翻页起点X: 0.75,
  88.             翻页起点Y: 0.50,
  89.             翻页终点X: 0.25,
  90.             翻页终点Y: 0.50,
  91.             福利页坐标: [0.5, 0.976],
  92.             广告按钮文字: "看视频领金币",
  93.             启动后等待秒: 8,
  94.             阅读页检测文字: "目录",
  95.             需要找书: true,
  96.             书城Tab坐标: [0.3, 0.976],
  97.             书架Tab坐标: [0.1, 0.976],
  98.             点击中央呼出菜单: false
  99.         },
  100.         {
  101.             编号: 3,
  102.             名称: "快音",
  103.             包名: "com.kuaiyin.x",
  104.             翻页方向: "vertical",          // 上下滑动(短视频类)
  105.             翻页起点X: 0.35,                // 左侧滑动避开全屏按钮
  106.             翻页起点Y: 0.78,
  107.             翻页终点X: 0.35,
  108.             翻页终点Y: 0.22,
  109.             福利页坐标: [0.5, 0.976],
  110.             广告按钮文字: "看视频领金币",
  111.             启动后等待秒: 8,
  112.             阅读页检测文字: null,             // 短视频类不需要检测阅读页
  113.             需要找书: false,
  114.             书城Tab坐标: null,
  115.             书架Tab坐标: null,
  116.             点击中央呼出菜单: false
  117.         },
  118.         {
  119.             编号: 4,
  120.             名称: "抖音极速版",
  121.             包名: "com.ss.android.ugc.aweme.lite",
  122.             翻页方向: "vertical",
  123.             翻页起点X: 0.35,
  124.             翻页起点Y: 0.78,
  125.             翻页终点X: 0.35,
  126.             翻页终点Y: 0.22,
  127.             福利页坐标: [0.9, 0.5],          // 右侧红包挂件
  128.             广告按钮文字: "看视频赚金币",
  129.             启动后等待秒: 8,
  130.             阅读页检测文字: null,
  131.             需要找书: false,
  132.             书城Tab坐标: null,
  133.             书架Tab坐标: null,
  134.             点击中央呼出菜单: false
  135.         }
  136.     ]
  137. };

  138. // =============================================================================
  139. //  第二部分:引擎(Agent / 无碍 双模式自动切换)
  140. // =============================================================================

  141. var Engine = {
  142.     mode: "",
  143.     屏幕宽: 1080,
  144.     屏幕高: 2400,

  145.     init: function() {
  146.         print.log("\n【引擎初始化】检测模式...");
  147.         if (CONFIG.优先使用Agent模式) {
  148.             try { if (agent.isOpen()) { this.mode = "agent"; print.log("✓ Agent代理模式"); } } catch(e) {}
  149.         }
  150.         if (this.mode === "") {
  151.             try { if (typeof auto !== 'undefined') { this.mode = "auto"; print.log("✓ 无碍(Accessibility)模式"); } } catch(e) {}
  152.         }
  153.         if (this.mode === "") {
  154.             print.log("✗ 两个模式都不可用!请开启 Agent 或无障碍后再跑");
  155.             return false;
  156.         }

  157.         try {
  158.             if (this.mode === "agent") {
  159.                 var raw = "" + agent.shell("wm size");
  160.                 var m = raw.match(/(\d+)x(\d+)/);
  161.                 this.屏幕宽 = m ? parseInt(m[1]) : 1080;
  162.                 this.屏幕高 = m ? parseInt(m[2]) : 2400;
  163.             } else {
  164.                 this.屏幕宽 = screen.getScreenWidth();
  165.                 this.屏幕高 = screen.getScreenHeight();
  166.             }
  167.         } catch(e) {
  168.             this.屏幕宽 = 1080;
  169.             this.屏幕高 = 2400;
  170.         }
  171.         print.log("✓ 屏幕尺寸:" + this.屏幕宽 + " × " + this.屏幕高);
  172.         return true;
  173.     },

  174.     // 随机整数 [min, max)
  175.     randInt: function(min, max) {
  176.         return Math.floor(min + Math.random() * (max - min));
  177.     },

  178.     // 随机浮点 [0, 1)
  179.     randFloat: function() {
  180.         return Math.random();
  181.     },

  182.     // 随机偏移(在基准值上加微小随机量)
  183.     jitter: function(base, range) {
  184.         return base + (Math.random() - 0.5) * range;
  185.     },

  186.     // 翻页滑动(核心防检测:随机轨迹 + 随机速度 + 随机偏移)
  187.     nextPage: function(app) {
  188.         var x1, y1, x2, y2;
  189.         var dur = this.randInt(CONFIG.滑动时长最小毫秒, CONFIG.滑动时长最大毫秒);

  190.         if (app.翻页方向 === "horizontal") {
  191.             // 水平翻页:从右往左滑(下一页)
  192.             x1 = this.jitter(app.翻页起点X, 0.04);
  193.             y1 = this.jitter(app.翻页起点Y, 0.08);
  194.             x2 = this.jitter(app.翻页终点X, 0.04);
  195.             y2 = this.jitter(app.翻页终点Y, 0.08);
  196.         } else {
  197.             // 垂直翻页:从下往上滑(下一个视频)
  198.             x1 = this.jitter(app.翻页起点X, 0.06);
  199.             y1 = this.jitter(app.翻页起点Y, 0.04);
  200.             x2 = this.jitter(app.翻页终点X, 0.06);
  201.             y2 = this.jitter(app.翻页终点Y, 0.04);
  202.         }

  203.         // 确保坐标在合法范围 [0.02, 0.98]
  204.         x1 = Math.max(0.02, Math.min(0.98, x1));
  205.         y1 = Math.max(0.02, Math.min(0.98, y1));
  206.         x2 = Math.max(0.02, Math.min(0.98, x2));
  207.         y2 = Math.max(0.02, Math.min(0.98, y2));

  208.         if (CONFIG.调试模式) {
  209.             print.log("  → 翻页 [" + app.翻页方向 + "] (" +
  210.                 x1.toFixed(2) + "," + y1.toFixed(2) + ") → (" +
  211.                 x2.toFixed(2) + "," + y2.toFixed(2) + ") " + dur + "ms");
  212.         }

  213.         try {
  214.             auto.swipPercent_v2(x1, y1, x2, y2, dur, 0);
  215.         } catch(e) {
  216.             // 兜底:用 agent.shell input swipe
  217.             try {
  218.                 var px1 = Math.floor(this.屏幕宽 * x1);
  219.                 var py1 = Math.floor(this.屏幕高 * y1);
  220.                 var px2 = Math.floor(this.屏幕宽 * x2);
  221.                 var py2 = Math.floor(this.屏幕高 * y2);
  222.                 agent.shell("input swipe " + px1 + " " + py1 + " " + px2 + " " + py2 + " " + dur);
  223.             } catch(e2) {
  224.                 print.log("  ⚠ 翻页失败: " + e2.message);
  225.             }
  226.         }
  227.         sleep.millisecond(this.randInt(300, 600));

  228.         // 小说类App翻页后可能弹出广告/弹窗,关闭它
  229.         if (app.点击中央呼出菜单) {
  230.             // 不再点中央隐藏菜单——七猫阅读页菜单是常驻的(上一章/下一章/目录在底部)
  231.             // 只关闭可能弹出的广告弹窗
  232.             this.closePopup();
  233.         }
  234.     },

  235.     // 回翻(模拟真人回头看一下上一页)
  236.     backPage: function(app) {
  237.         print.log("  ↩ 回翻一页(模拟真人回头)");
  238.         var x1, y1, x2, y2;

  239.         if (app.翻页方向 === "horizontal") {
  240.             // 水平回翻:从左往右滑(上一页)
  241.             x1 = this.jitter(app.翻页终点X, 0.04);
  242.             y1 = this.jitter(app.翻页起点Y, 0.08);
  243.             x2 = this.jitter(app.翻页起点X, 0.04);
  244.             y2 = this.jitter(app.翻页终点Y, 0.08);
  245.         } else {
  246.             // 垂直回翻:从上往下滑(上一个视频)
  247.             x1 = this.jitter(app.翻页终点X, 0.06);
  248.             y1 = this.jitter(app.翻页终点Y, 0.04);
  249.             x2 = this.jitter(app.翻页起点X, 0.06);
  250.             y2 = this.jitter(app.翻页起点Y, 0.04);
  251.         }

  252.         try {
  253.             auto.swipPercent_v2(x1, y1, x2, y2, this.randInt(400, 800), 0);
  254.         } catch(e) {
  255.             try {
  256.                 var px1 = Math.floor(this.屏幕宽 * x1);
  257.                 var py1 = Math.floor(this.屏幕高 * y1);
  258.                 var px2 = Math.floor(this.屏幕宽 * x2);
  259.                 var py2 = Math.floor(this.屏幕高 * y2);
  260.                 agent.shell("input swipe " + px1 + " " + py1 + " " + px2 + " " + py2 + " 600");
  261.             } catch(e2) {}
  262.         }
  263.         sleep.millisecond(this.randInt(500, 1000));
  264.     },

  265.     // 双击点赞(模拟真人双击屏幕点赞)
  266.     doubleTap: function() {
  267.         print.log("  ❤ 双击点赞");
  268.         var x = this.jitter(0.5, 0.1);
  269.         var y = this.jitter(0.4, 0.1);
  270.         try {
  271.             auto.clickPercent(x, y);
  272.             sleep.millisecond(80);
  273.             auto.clickPercent(x, y);
  274.         } catch(e) {
  275.             try {
  276.                 var px = Math.floor(this.屏幕宽 * x);
  277.                 var py = Math.floor(this.屏幕高 * y);
  278.                 agent.shell("input tap " + px + " " + py);
  279.                 sleep.millisecond(80);
  280.                 agent.shell("input tap " + px + " " + py);
  281.             } catch(e2) {}
  282.         }
  283.         sleep.millisecond(this.randInt(300, 600));
  284.     },

  285.     // 点击百分比坐标
  286.     clickPercent: function(px, py) {
  287.         try {
  288.             auto.clickPercent(px, py);
  289.         } catch(e) {
  290.             try {
  291.                 agent.shell("input tap " + Math.floor(this.屏幕宽 * px) + " " + Math.floor(this.屏幕高 * py));
  292.             } catch(e2) {}
  293.         }
  294.         sleep.millisecond(this.randInt(600, 1000));
  295.     },

  296.     // 返回键
  297.     back: function() {
  298.         try {
  299.             if (this.mode === "agent") agent.shell("input keyevent 4");
  300.             else auto.back();
  301.         } catch(e) {}
  302.         sleep.second(1);
  303.     },

  304.     // Home键
  305.     home: function() {
  306.         try {
  307.             if (this.mode === "agent") agent.shell("input keyevent 3");
  308.             else auto.home();
  309.         } catch(e) {}
  310.         sleep.second(2);
  311.     },

  312.     // 启动 App
  313.     openApp: function(pkg, name) {
  314.         print.log("  → 启动:" + name + "(" + pkg + ")");
  315.         try {
  316.             app.resetApp(pkg);
  317.             print.log("✓ 启动APP:" + pkg);
  318.             return true;
  319.         } catch(e) {
  320.             print.log("  ⚠ app.resetApp 失败:" + e.message + ",尝试 monkey 兜底");
  321.             try {
  322.                 agent.shell("monkey -p " + pkg + " -c android.intent.category.LAUNCHER 1");
  323.                 return true;
  324.             } catch(e2) {
  325.                 print.log("  ✗ 启动完全失败:" + e2.message);
  326.                 return false;
  327.             }
  328.         }
  329.     },

  330.     // 关闭弹窗(全用 nodeSearch,不依赖 findText)
  331.     closePopup: function() {
  332.         var words = CONFIG.弹窗关键词;
  333.         try {
  334.             var nodes = auto.nodeSearch(1000);
  335.             if (!nodes) return false;

  336.             // 第一轮:精确匹配关键词
  337.             for (var i = 0; i < words.length; i++) {
  338.                 try {
  339.                     var filtered = nodes.text(words[i]);
  340.                     if (filtered && filtered.count() > 0) {
  341.                         var node = filtered.getNode(0);
  342.                         if (node && this.clickNode(node, words[i], "弹窗")) {
  343.                             return true;
  344.                         }
  345.                     }
  346.                 } catch(e2) {}
  347.             }

  348.             // 第二轮:模糊匹配关键词
  349.             for (var j = 0; j < words.length; j++) {
  350.                 try {
  351.                     var inc = nodes.textIncludes(words[j]);
  352.                     if (inc && inc.count() > 0) {
  353.                         var node2 = inc.getNode(0);
  354.                         if (node2 && this.clickNode(node2, words[j], "模糊弹窗")) {
  355.                             return true;
  356.                         }
  357.                     }
  358.                 } catch(e3) {}
  359.             }

  360.             // 第三轮:关闭悬浮广告/红包(通过节点ID,带包名前缀)
  361.             var pkg = "";
  362.             try { pkg = app.getTopPackName() || ""; } catch(e5) {}
  363.             var closeIds = [
  364.                 (pkg ? pkg + ":id/" : "") + "close_red_packet",
  365.                 (pkg ? pkg + ":id/" : "") + "close",
  366.                 (pkg ? pkg + ":id/" : "") + "iv_close",
  367.                 (pkg ? pkg + ":id/" : "") + "btn_close",
  368.                 (pkg ? pkg + ":id/" : "") + "img_close",
  369.                 "close_red_packet", "close", "iv_close", "btn_close", "img_close"
  370.             ];
  371.             for (var k = 0; k < closeIds.length; k++) {
  372.                 try {
  373.                     var idNode = nodes.id(closeIds[k]);
  374.                     if (idNode && idNode.count() > 0) {
  375.                         var node3 = idNode.getNode(0);
  376.                         if (node3 && this.clickNode(node3, closeIds[k], "浮窗ID")) {
  377.                             return true;
  378.                         }
  379.                     }
  380.                 } catch(e4) {}
  381.             }

  382.         } catch(e) {
  383.             if (CONFIG.调试模式) print.log("  - 弹窗检测异常: " + e.message);
  384.         }
  385.         return false;
  386.     },

  387.     // 查找并点击文字(精确→模糊→desc匹配,每个都先node.click再坐标fallback)
  388.     findAndClickText: function(keyword) {
  389.         try {
  390.             var nodes = auto.nodeSearch(2000);
  391.             if (!nodes) return false;

  392.             // 1. 精确匹配 text
  393.             try {
  394.                 var filtered = nodes.text(keyword);
  395.                 if (filtered && filtered.count() > 0) {
  396.                     var node = filtered.getNode(0);
  397.                     if (node && this.clickNode(node, keyword)) return true;
  398.                 }
  399.             } catch(e1) {}

  400.             // 2. 模糊匹配 textIncludes
  401.             try {
  402.                 var inc = nodes.textIncludes(keyword);
  403.                 if (inc && inc.count() > 0) {
  404.                     var node2 = inc.getNode(0);
  405.                     if (node2 && this.clickNode(node2, keyword)) return true;
  406.                 }
  407.             } catch(e2) {}

  408.             // 3. desc 精确匹配
  409.             try {
  410.                 var descNode = nodes.desc(keyword);
  411.                 if (descNode && descNode.count() > 0) {
  412.                     var node3 = descNode.getNode(0);
  413.                     if (node3 && this.clickNode(node3, keyword, "desc")) return true;
  414.                 }
  415.             } catch(e3) {}

  416.             // 4. descIncludes 模糊匹配
  417.             try {
  418.                 var descInc = nodes.descIncludes(keyword);
  419.                 if (descInc && descInc.count() > 0) {
  420.                     var node4 = descInc.getNode(0);
  421.                     if (node4 && this.clickNode(node4, keyword, "desc模糊")) return true;
  422.                 }
  423.             } catch(e4) {}
  424.         } catch(e) {
  425.             if (CONFIG.调试模式) print.log("  - findAndClickText 异常: " + e.message);
  426.         }
  427.         return false;
  428.     },

  429.     // 点击节点(先尝试node.click(),失败则用坐标点击)
  430.     // AIWROK节点对象的 text/centerPercent/center 都是属性不是方法,用node.xxx不是node.xxx()
  431.     clickNode: function(node, label, matchType) {
  432.         matchType = matchType || "text";
  433.         try {
  434.             // 先尝试 node.click()
  435.             node.click();
  436.             print.log("  ✓ 点击" + matchType + "【" + label + "】(node.click)");
  437.             sleep.second(1);
  438.             return true;
  439.         } catch(e) {
  440.             // node.click() 失败,用坐标点击
  441.             try {
  442.                 // 获取节点的 centerPercent 属性(注意:是属性不是方法)
  443.                 var cp = null;
  444.                 try { cp = node.centerPercent; } catch(e3) {}
  445.                 if (cp) {
  446.                     // centerPercent 格式 "[0.5,0.85]"
  447.                     var m = ("" + cp).match(/([\d.]+),([\d.]+)/);
  448.                     if (m) {
  449.                         var px = parseFloat(m[1]);
  450.                         var py = parseFloat(m[2]);
  451.                         this.clickPercent(px, py);
  452.                         print.log("  ✓ 坐标点击" + matchType + "【" + label + "】(" + px + "," + py + ")");
  453.                         return true;
  454.                     }
  455.                 }
  456.                 // 兜底:用 center 属性
  457.                 var ct = null;
  458.                 try { ct = node.center; } catch(e4) {}
  459.                 if (ct) {
  460.                     var m2 = ("" + ct).match(/([\d.]+),([\d.]+)/);
  461.                     if (m2) {
  462.                         var cx = parseInt(m2[1]);
  463.                         var cy = parseInt(m2[2]);
  464.                         var px2 = cx / this.屏幕宽;
  465.                         var py2 = cy / this.屏幕高;
  466.                         this.clickPercent(px2, py2);
  467.                         print.log("  ✓ 坐标点击" + matchType + "【" + label + "】(" + px2 + "," + py2 + ")");
  468.                         return true;
  469.                     }
  470.                 }
  471.                 // 兜底2:用 boundsPercent 属性
  472.                 var bp = null;
  473.                 try { bp = node.boundsPercent; } catch(e5) {}
  474.                 if (bp) {
  475.                     // boundsPercent 格式 "[0.1,0.2][0.5,0.8]" 取中心
  476.                     var m3 = ("" + bp).match(/\[([\d.]+),([\d.]+)\]\[([\d.]+),([\d.]+)\]/);
  477.                     if (m3) {
  478.                         var px3 = (parseFloat(m3[1]) + parseFloat(m3[3])) / 2;
  479.                         var py3 = (parseFloat(m3[2]) + parseFloat(m3[4])) / 2;
  480.                         this.clickPercent(px3, py3);
  481.                         print.log("  ✓ 坐标点击" + matchType + "【" + label + "】(" + px3 + "," + py3 + ")");
  482.                         return true;
  483.                     }
  484.                 }
  485.             } catch(e2) {
  486.                 if (CONFIG.调试模式) print.log("  - clickNode坐标fallback也失败: " + e2.message);
  487.             }
  488.         }
  489.         return false;
  490.     },

  491.     // 判断当前是否在阅读页/视频页
  492.     // 优先用阅读页特有的ID检测(最准确),文字特征只作为辅助
  493.     isOnReadingPage: function(app) {
  494.         if (!app.阅读页检测文字) return true; // 不需要检测的直接返回true
  495.         try {
  496.             var nodes = auto.nodeSearch(1500);
  497.             if (!nodes) return true; // nodeSearch失败时不阻断流程

  498.             // 1. 优先检测阅读页特有的ID(最准确,不会误判)
  499.             try {
  500.                 var pkg = app.包名;
  501.                 var ids = [
  502.                     pkg + ":id/read_progress_layout",
  503.                     pkg + ":id/book_directory",
  504.                     pkg + ":id/btn_progress_left",
  505.                     pkg + ":id/btn_progress_right",
  506.                     pkg + ":id/read_blank_area",
  507.                     pkg + ":id/read_page_container",
  508.                     pkg + ":id/read_view",
  509.                     pkg + ":id/tv_read_progress"
  510.                 ];
  511.                 for (var j = 0; j < ids.length; j++) {
  512.                     var idNode = nodes.id(ids[j]);
  513.                     if (idNode && idNode.count() > 0) return true;
  514.                 }
  515.             } catch(e3) {}

  516.             // 2. 再用文字特征辅助判断(注意:要同时满足多个特征才算,避免误判)
  517.             var matchCount = 0;
  518.             var features = CONFIG.阅读页特征;
  519.             for (var i = 0; i < features.length; i++) {
  520.                 try {
  521.                     var matched = nodes.text(features[i]);
  522.                     if (matched && matched.count() > 0) {
  523.                         matchCount++;
  524.                         // 至少匹配2个阅读页特征文字才认定(减少误判)
  525.                         if (matchCount >= 2) return true;
  526.                     }
  527.                 } catch(e) {}
  528.             }

  529.             // 3. 检测主检测文字 + 至少1个其他特征
  530.             try {
  531.                 var main = nodes.text(app.阅读页检测文字);
  532.                 if (main && main.count() > 0 && matchCount >= 1) return true;
  533.             } catch(e2) {}

  534.             return false;
  535.         } catch(e) {
  536.             return true; // 检测失败时假设在阅读页(不中断流程)
  537.         }
  538.     },

  539.     // 等待一段时间,期间周期性关闭弹窗
  540.     smartSleep: function(totalSec, intervalSec) {
  541.         var remain = totalSec;
  542.         while (remain > 0) {
  543.             var step = Math.min(intervalSec, remain);
  544.             sleep.second(step);
  545.             remain -= step;
  546.             this.closePopup();
  547.         }
  548.     }
  549. };

  550. // =============================================================================
  551. //  第三部分:阅读赚钱核心模块
  552. // =============================================================================

  553. var ReadingBot = {
  554.     app: null,
  555.     totalReads: 0,           // 总翻页次数
  556.     totalAds: 0,             // 看广告次数
  557.     totalPopups: 0,          // 关弹窗次数
  558.     totalBackReads: 0,       // 回翻次数
  559.     totalLikes: 0,           // 点赞次数
  560.     totalRests: 0,           // 休息次数
  561.     startTime: 0,
  562.     lastAdTime: 0,           // 上次看广告的时间戳

  563.     run: function(app) {
  564.         this.app = app;
  565.         this.startTime = Date.now();
  566.         this.lastAdTime = this.startTime;

  567.         var maxMs = CONFIG.总运行分钟 * 60 * 1000;

  568.         print.log("\n╔══════════ 开始阅读赚钱 ══════════╗");
  569.         print.log("  · App:" + app.名称 + "(" + app.包名 + ")");
  570.         print.log("  · 翻页方向:" + app.翻页方向);
  571.         print.log("  · 总时长:" + CONFIG.总运行分钟 + " 分钟");
  572.         print.log("  · 停留:" + CONFIG.最小停留秒 + "~" + CONFIG.最大停留秒 + " 秒");
  573.         print.log("  · 看广告间隔:" + CONFIG.看广告间隔分钟 + " 分钟");
  574.         print.log("╚══════════════════════════════════╝");

  575.         // 1. 启动App
  576.         print.log("\n[步骤1] 启动App...");
  577.         if (!Engine.openApp(app.包名, app.名称)) {
  578.             print.log("✗ 启动失败,退出");
  579.             return;
  580.         }
  581.         sleep.second(app.启动后等待秒 || 8);
  582.         Engine.closePopup();

  583.         // 2. 确保在阅读页/视频页
  584.         print.log("[步骤2] 确认阅读页面...");

  585.         if (app.需要找书 && !Engine.isOnReadingPage(app)) {
  586.             // 书架可能为空或不在阅读页,需要找一本书
  587.             print.log("  → 不在阅读页,需要找一本书进入阅读...");
  588.             this.findBookAndRead(app);
  589.         } else if (!Engine.isOnReadingPage(app)) {
  590.             // 不需要找书的App(如短视频),点中央进入
  591.             print.log("  ⚠ 不在阅读页,尝试点击屏幕中央进入...");
  592.             Engine.clickPercent(0.5, 0.5);
  593.             sleep.second(2);
  594.             Engine.closePopup();
  595.         }

  596.         if (Engine.isOnReadingPage(app)) {
  597.             print.log("✓ 已进入阅读页面");
  598.         } else {
  599.             print.log("⚠ 未确认在阅读页,但仍继续尝试翻页...");
  600.         }

  601.         // 3. 主循环
  602.         print.log("\n[步骤3] 开始自动翻页阅读...\n");

  603.         while ((Date.now() - this.startTime) < maxMs) {
  604.             var elapsedMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
  605.             var remainMin = (CONFIG.总运行分钟 - elapsedMin).toFixed(1);

  606.             // === A. 检查是否该休息了 ===
  607.             if (CONFIG.每隔N分钟休息 > 0 &&
  608.                 (Date.now() - this.startTime) > CONFIG.每隔N分钟 * 60 * 1000 * (this.totalRests + 1)) {
  609.                 this.totalRests++;
  610.                 var restSec = Engine.randInt(CONFIG.休息秒数, CONFIG.休息秒数 + 20);
  611.                 print.log("\n[" + elapsedMin + "m] ☕ 第" + this.totalRests + "次休息 " + restSec + "秒");
  612.                 Engine.smartSleep(restSec, 10);
  613.                 print.log("  ✓ 休息完毕,继续阅读");
  614.                 continue;
  615.             }

  616.             // === B. 检查是否该看广告了 ===
  617.             var sinceAd = Date.now() - this.lastAdTime;
  618.             if (sinceAd >= CONFIG.看广告间隔分钟 * 60 * 1000) {
  619.                 this.watchAd();
  620.                 this.lastAdTime = Date.now();
  621.                 // 看完广告后确保回到阅读页
  622.                 this.ensureReadingPage();
  623.                 continue;
  624.             }

  625.             // === C. 随机停留看当前页 ===
  626.             var staySec = Engine.randInt(CONFIG.最小停留秒, CONFIG.最大停留秒);

  627.             // 小概率额外多停一会(模拟真人被内容吸引)
  628.             if (Engine.randFloat() < CONFIG.休息概率) {
  629.                 staySec += Engine.randInt(10, 30);
  630.                 if (CONFIG.调试模式) print.log("  &#128214; 这段内容不错,多看 " + (staySec - CONFIG.最小停留秒) + " 秒");
  631.             }

  632.             if (CONFIG.调试模式) {
  633.                 print.log("[" + elapsedMin + "m] 第" + (this.totalReads + 1) + "页 停留" + staySec + "s (剩" + remainMin + "m)");
  634.             } else if ((this.totalReads % 10) === 0) {
  635.                 print.log("[" + elapsedMin + "m] 已翻" + this.totalReads + "页 广告" + this.totalAds + "次 弹窗" + this.totalPopups + "次");
  636.             }

  637.             // 停留期间每5秒关一次弹窗
  638.             Engine.smartSleep(staySec, 5);

  639.             // === D. 随机防检测动作 ===
  640.             var dice = Engine.randFloat();

  641.             if (dice < CONFIG.回翻概率) {
  642.                 // 小概率回翻一页
  643.                 this.totalBackReads++;
  644.                 Engine.backPage(this.app);
  645.                 sleep.second(Engine.randInt(3, 6));
  646.                 // 再翻回来
  647.                 Engine.nextPage(this.app);
  648.                 this.totalReads++;
  649.             } else if (dice < CONFIG.回翻概率 + CONFIG.点赞概率) {
  650.                 // 小概率双击点赞
  651.                 this.totalLikes++;
  652.                 Engine.doubleTap();
  653.             } else {
  654.                 // 正常翻页
  655.                 Engine.nextPage(this.app);
  656.                 this.totalReads++;
  657.             }

  658.             // 每20页关一次弹窗
  659.             if ((this.totalReads % 20) === 0) {
  660.                 if (Engine.closePopup()) this.totalPopups++;
  661.             }

  662.             // 每50页确保在阅读页
  663.             if ((this.totalReads % 50) === 0) {
  664.                 if (!Engine.isOnReadingPage(this.app)) {
  665.                     print.log("  ⚠ 偏离阅读页,尝试恢复...");
  666.                     Engine.back();
  667.                     sleep.second(2);
  668.                     if (!Engine.isOnReadingPage(this.app)) {
  669.                         Engine.clickPercent(0.5, 0.5);
  670.                         sleep.second(2);
  671.                     }
  672.                     Engine.closePopup();
  673.                 }
  674.             }
  675.         }

  676.         // 4. 结束统计
  677.         var totalMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
  678.         print.log("\n╔══════════ 阅读结束 ══════════╗");
  679.         print.log("║  总翻页:" + this.totalReads + " 次");
  680.         print.log("║  回翻次数:" + this.totalBackReads + " 次");
  681.         print.log("║  点赞次数:" + this.totalLikes + " 次");
  682.         print.log("║  看广告:" + this.totalAds + " 次");
  683.         print.log("║  关弹窗:" + this.totalPopups + " 次");
  684.         print.log("║  休息次数:" + this.totalRests + " 次");
  685.         print.log("║  总时长:" + totalMin + " 分钟");
  686.         print.log("╚══════════════════════════════════╝");

  687.         Engine.home();
  688.     },

  689.     // 找一本书进入阅读页面(七猫/番茄等小说类App用)
  690.     // 基于实测XML节点结构编写,支持重试和多策略fallback
  691.     findBookAndRead: function(app) {
  692.         var maxRetry = 4; // 最多尝试4本书
  693.         var readBtns = ["开始阅读", "免费阅读", "立即阅读", "继续阅读", "免费读", "开始免费读"];
  694.         var addBtns = ["加入书架", "添加到书架", "收藏"];

  695.         for (var retry = 0; retry < maxRetry; retry++) {
  696.             print.log("\n  [找书] === 第" + (retry + 1) + "/" + maxRetry + "轮 ===");
  697.             try {
  698.                 // 0. 已在阅读页则直接返回
  699.                 if (Engine.isOnReadingPage(app)) {
  700.                     print.log("  [找书] ✓ 已在阅读页,无需找书");
  701.                     return true;
  702.                 }

  703.                 // 1. 确保在书城页(点书城Tab)
  704.                 if (retry === 0 && app.书城Tab坐标) {
  705.                     print.log("  [找书] 1. 点击书城Tab...");
  706.                     Engine.clickPercent(app.书城Tab坐标[0], app.书城Tab坐标[1]);
  707.                     sleep.second(3);
  708.                     Engine.closePopup();
  709.                 }

  710.                 // 2. 点击一本书:优先用节点ID,兜底用坐标
  711.                 var bookClicked = false;
  712.                 var bookTitle = "";

  713.                 // 策略A: 用 tv_book_title 节点点击(通过坐标fallback确保点击成功)
  714.                 try {
  715.                     var nodes = auto.nodeSearch(2000);
  716.                     if (nodes) {
  717.                         var titleNodes = nodes.id(app.包名 + ":id/tv_book_title");
  718.                         if (titleNodes && titleNodes.count() > 0) {
  719.                             var idx = Math.min(retry, titleNodes.count() - 1);
  720.                             var titleNode = titleNodes.getNode(idx);
  721.                             if (titleNode) {
  722.                                 // AIWROK节点text是属性不是方法
  723.                                 bookTitle = titleNode.text || ("书" + idx);
  724.                                 // tv_book_title 本身 clickable=false,用 clickNode 坐标fallback
  725.                                 if (Engine.clickNode(titleNode, bookTitle, "书名")) {
  726.                                     print.log("  [找书] 2A. 点击书名[" + bookTitle + "] idx=" + idx);
  727.                                     bookClicked = true;
  728.                                 }
  729.                             }
  730.                         }
  731.                     }
  732.                 } catch(e2a) {
  733.                     if (CONFIG.调试模式) print.log("  [找书] 2A异常: " + e2a.message);
  734.                 }

  735.                 // 策略B: 用 rank_book_cover 节点点击
  736.                 if (!bookClicked) {
  737.                     try {
  738.                         var nodes2 = auto.nodeSearch(2000);
  739.                         if (nodes2) {
  740.                             var coverIds = [
  741.                                 app.包名 + ":id/rank_book_cover",
  742.                                 "rank_book_cover",
  743.                                 "book_cover", "iv_cover", "item_book"
  744.                             ];
  745.                             for (var ci = 0; ci < coverIds.length; ci++) {
  746.                                 try {
  747.                                     var coverNodes = nodes2.id(coverIds[ci]);
  748.                                     if (coverNodes && coverNodes.count() > 0) {
  749.                                         var cIdx = Math.min(retry, coverNodes.count() - 1);
  750.                                         var coverNode = coverNodes.getNode(cIdx);
  751.                                         if (coverNode && Engine.clickNode(coverNode, "封面" + cIdx, "书籍")) {
  752.                                             print.log("  [找书] 2B. 点击封面 id=" + coverIds[ci] + " idx=" + cIdx);
  753.                                             bookClicked = true;
  754.                                             break;
  755.                                         }
  756.                                     }
  757.                                 } catch(e2b2) {}
  758.                             }
  759.                         }
  760.                     } catch(e2b) {
  761.                         if (CONFIG.调试模式) print.log("  [找书] 2B异常: " + e2b.message);
  762.                     }
  763.                 }

  764.                 // 策略C: 用坐标点击不同的书
  765.                 // XML实测: 第1本书 center=[0.1287,0.3932], 第5本书 center=[0.7148,0.3932]
  766.                 // 垂直方向间隔约0.1187 (0.3932→0.512→0.6307→0.7495)
  767.                 if (!bookClicked) {
  768.                     var bookPositions = [
  769.                         [0.13, 0.39], [0.13, 0.51], [0.13, 0.63], [0.13, 0.75],
  770.                         [0.71, 0.39], [0.71, 0.51], [0.71, 0.63], [0.71, 0.75]
  771.                     ];
  772.                     var posIdx = retry % bookPositions.length;
  773.                     print.log("  [找书] 2C. 坐标点击推荐书 (" + bookPositions[posIdx][0] + "," + bookPositions[posIdx][1] + ")");
  774.                     Engine.clickPercent(bookPositions[posIdx][0], bookPositions[posIdx][1]);
  775.                     bookClicked = true;
  776.                 }

  777.                 if (!bookClicked) {
  778.                     print.log("  [找书] ⚠ 无法找到可点击的书籍,重试...");
  779.                     sleep.second(2);
  780.                     continue;
  781.                 }

  782.                 // 3. 等待页面跳转,然后关闭弹窗
  783.                 sleep.second(3);
  784.                 Engine.closePopup();

  785.                 // 4. 检查是否直接进入了阅读页(有些App会直接打开)
  786.                 if (Engine.isOnReadingPage(app)) {
  787.                     print.log("  [找书] ✓ 直接进入阅读页");
  788.                     return true;
  789.                 }

  790.                 // 5. 在书籍详情页找"开始阅读"按钮或"左滑开始阅读"提示
  791.                 print.log("  [找书] 3. 查找阅读入口...");
  792.                 var found = false;

  793.                 // 5a. 先检测是否有"左滑开始阅读"提示(七猫特有)
  794.                 try {
  795.                     var nodesTip = auto.nodeSearch(2000);
  796.                     if (nodesTip) {
  797.                         // 七猫详情页左滑进入阅读
  798.                         var tipNode = nodesTip.id(app.包名 + ":id/tv_tip1");
  799.                         if (tipNode && tipNode.count() > 0) {
  800.                             print.log("  [找书] 检测到【左滑开始阅读】提示,执行左滑手势...");
  801.                             // 左滑:从右(0.8)到左(0.2),在屏幕中部
  802.                             auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
  803.                             sleep.second(3);
  804.                             Engine.closePopup();

  805.                             if (Engine.isOnReadingPage(app)) {
  806.                                 print.log("  [找书] ✓ 左滑后进入阅读页");
  807.                                 return true;
  808.                             }
  809.                             found = true; // 假设左滑成功了
  810.                         }
  811.                     }
  812.                 } catch(e5a) {
  813.                     if (CONFIG.调试模式) print.log("  [找书] 5a左滑检测异常: " + e5a.message);
  814.                 }

  815.                 // 5b. 精确匹配阅读按钮文字
  816.                 if (!found) {
  817.                     for (var i = 0; i < readBtns.length; i++) {
  818.                         if (Engine.findAndClickText(readBtns[i])) {
  819.                             print.log("  [找书] ✓ 点击了【" + readBtns[i] + "】");
  820.                             found = true;
  821.                             break;
  822.                         }
  823.                     }
  824.                 }

  825.                 // 5c. 模糊匹配(七猫按钮可能含图标符号)
  826.                 if (!found) {
  827.                     var readKeywords = ["免费读", "开始读", "立即读", "阅读", "左滑"];
  828.                     try {
  829.                         var nodes3 = auto.nodeSearch(2000);
  830.                         if (nodes3) {
  831.                             for (var ri = 0; ri < readKeywords.length; ri++) {
  832.                                 try {
  833.                                     var inc = nodes3.textIncludes(readKeywords[ri]);
  834.                                     if (inc && inc.count() > 0) {
  835.                                         var rn = inc.getNode(0);
  836.                                         if (rn && Engine.clickNode(rn, readKeywords[ri], "模糊阅读")) {
  837.                                             print.log("  [找书] ✓ 模糊点击【" + readKeywords[ri] + "】");
  838.                                             found = true;
  839.                                             break;
  840.                                         }
  841.                                     }
  842.                                 } catch(e3) {}
  843.                             }
  844.                         }
  845.                     } catch(e3b) {}
  846.                 }

  847.                 // 5d. 如果找到"左滑"文字但之前没处理,做左滑手势
  848.                 if (!found) {
  849.                     try {
  850.                         var nodes4 = auto.nodeSearch(2000);
  851.                         if (nodes4) {
  852.                             var swipeTip = nodes4.textIncludes("左滑");
  853.                             if (swipeTip && swipeTip.count() > 0) {
  854.                                 print.log("  [找书] 检测到【左滑】文字,执行左滑手势...");
  855.                                 auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
  856.                                 sleep.second(3);
  857.                                 Engine.closePopup();
  858.                                 found = true;
  859.                             }
  860.                         }
  861.                     } catch(e5d) {}
  862.                 }

  863.                 // 5e. 兜底:如果以上都没找到,先尝试左滑(七猫最常见的入口),再尝试点底部
  864.                 if (!found) {
  865.                     print.log("  [找书] 3b. 尝试左滑手势进入阅读...");
  866.                     auto.swipPercent_v2(0.8, 0.5, 0.2, 0.5, 500, 0);
  867.                     sleep.second(3);
  868.                     Engine.closePopup();

  869.                     if (Engine.isOnReadingPage(app)) {
  870.                         print.log("  [找书] ✓ 左滑后进入阅读页");
  871.                         return true;
  872.                     }

  873.                     // 左滑不行,再试点底部常见按钮位置
  874.                     print.log("  [找书] 3c. 左滑未成功,点底部按钮位置...");
  875.                     Engine.clickPercent(0.5, 0.88);
  876.                     sleep.second(2);
  877.                     if (!Engine.isOnReadingPage(app)) {
  878.                         Engine.clickPercent(0.5, 0.80);
  879.                         sleep.second(2);
  880.                     }
  881.                 }

  882.                 // 6. 等待进入阅读页
  883.                 sleep.second(3);
  884.                 Engine.closePopup();

  885.                 if (Engine.isOnReadingPage(app)) {
  886.                     print.log("  [找书] ✓ 已进入阅读页");
  887.                     return true;
  888.                 }

  889.                 // 7. 可能需要先加入书架才能阅读
  890.                 print.log("  [找书] 4. 尝试加入书架...");
  891.                 for (var j = 0; j < addBtns.length; j++) {
  892.                     if (Engine.findAndClickText(addBtns[j])) {
  893.                         print.log("  [找书] ✓ 点击了【" + addBtns[j] + "】");
  894.                         sleep.second(2);
  895.                         break;
  896.                     }
  897.                 }

  898.                 // 8. 加入书架后再找阅读按钮
  899.                 for (var k = 0; k < readBtns.length; k++) {
  900.                     if (Engine.findAndClickText(readBtns[k])) {
  901.                         print.log("  [找书] ✓ 加入书架后点击了【" + readBtns[k] + "】");
  902.                         found = true;
  903.                         break;
  904.                     }
  905.                 }

  906.                 sleep.second(3);
  907.                 Engine.closePopup();

  908.                 if (Engine.isOnReadingPage(app)) {
  909.                     print.log("  [找书] ✓ 通过加入书架进入阅读页");
  910.                     return true;
  911.                 }

  912.                 // 9. 本轮失败,返回书城重试
  913.                 print.log("  [找书] ⚠ 第" + (retry + 1) + "轮失败,返回书城重试...");
  914.                 Engine.back();
  915.                 sleep.second(2);
  916.                 if (app.书城Tab坐标) {
  917.                     Engine.clickPercent(app.书城Tab坐标[0], app.书城Tab坐标[1]);
  918.                     sleep.second(2);
  919.                 }

  920.             } catch(e) {
  921.                 print.log("  [找书] 第" + (retry + 1) + "轮异常: " + e.message);
  922.                 try { Engine.back(); sleep.second(2); } catch(e2) {}
  923.             }
  924.         }

  925.         print.log("  [找书] ⚠ " + maxRetry + "轮尝试后仍未进入阅读页");
  926.         return false;
  927.     },

  928.     // 看广告领奖励
  929.     watchAd: function() {
  930.         this.totalAds++;
  931.         var elapsedMin = ((Date.now() - this.startTime) / 60000).toFixed(1);
  932.         print.log("\n[" + elapsedMin + "m] &#128250; 第" + this.totalAds + "次看广告领奖励");

  933.         try {
  934.             // 0. 先关闭可能挡住的浮窗
  935.             Engine.closePopup();

  936.             // 1. 点底部福利/任务Tab
  937.             print.log("  [广告] 1. 点击福利Tab (" + this.app.福利页坐标[0] + "," + this.app.福利页坐标[1] + ")");
  938.             Engine.clickPercent(this.app.福利页坐标[0], this.app.福利页坐标[1]);
  939.             sleep.second(3);
  940.             Engine.closePopup();

  941.             // 2. 找"看视频领金币"按钮并点击
  942.             print.log("  [广告] 2. 查找【" + this.app.广告按钮文字 + "】");
  943.             var clicked = Engine.findAndClickText(this.app.广告按钮文字);

  944.             if (!clicked) {
  945.                 // 尝试其他常见的看广告按钮文字
  946.                 var adBtns = ["看视频", "看广告", "领金币", "领取", "看视频奖励", "立即领取"];
  947.                 for (var i = 0; i < adBtns.length; i++) {
  948.                     if (Engine.findAndClickText(adBtns[i])) {
  949.                         clicked = true;
  950.                         break;
  951.                     }
  952.                 }
  953.             }

  954.             if (!clicked) {
  955.                 // 兜底:点屏幕中下方常见位置
  956.                 print.log("  [广告] 2b. 文字未找到,点常见位置 (0.5, 0.65)");
  957.                 Engine.clickPercent(0.5, 0.65);
  958.                 sleep.second(2);
  959.             }

  960.             // 3. 等待广告播放
  961.             print.log("  [广告] 3. 等待广告播放 " + CONFIG.广告等待秒 + " 秒...");
  962.             Engine.smartSleep(CONFIG.广告等待秒, 5);

  963.             // 4. 关闭广告结束后的弹窗
  964.             print.log("  [广告] 4. 关闭广告弹窗");
  965.             Engine.closePopup();
  966.             sleep.second(2);

  967.             // 5. 返回阅读页
  968.             print.log("  [广告] 5. 返回阅读页");
  969.             Engine.back();
  970.             sleep.second(2);
  971.             Engine.closePopup();

  972.         } catch(e) {
  973.             print.log("  ⚠ 看广告异常: " + e.message);
  974.             // 尽力恢复
  975.             try { Engine.back(); sleep.second(2); } catch(e2) {}
  976.             try { Engine.closePopup(); } catch(e3) {}
  977.         }
  978.     },

  979.     // 确保回到阅读页
  980.     ensureReadingPage: function() {
  981.         if (Engine.isOnReadingPage(this.app)) return;

  982.         print.log("  [恢复] 不在阅读页,尝试恢复...");
  983.         var tryCount = 0;
  984.         while (tryCount < 3) {
  985.             tryCount++;
  986.             Engine.back();
  987.             sleep.second(2);
  988.             Engine.closePopup();
  989.             if (Engine.isOnReadingPage(this.app)) {
  990.                 print.log("  [恢复] ✓ 已回到阅读页");
  991.                 return;
  992.             }
  993.             // 点击屏幕中央尝试进入阅读
  994.             Engine.clickPercent(0.5, 0.5);
  995.             sleep.second(2);
  996.             if (Engine.isOnReadingPage(this.app)) {
  997.                 print.log("  [恢复] ✓ 已回到阅读页");
  998.                 return;
  999.             }
  1000.         }

  1001.         // 多次返回都不在阅读页,可能回退到了书架/书城
  1002.         // 尝试重新找书进入阅读
  1003.         if (this.app.需要找书) {
  1004.             print.log("  [恢复] 返回到非阅读页,尝试重新找书...");
  1005.             this.findBookAndRead(this.app);
  1006.             if (Engine.isOnReadingPage(this.app)) {
  1007.                 print.log("  [恢复] ✓ 重新进入阅读页");
  1008.                 return;
  1009.             }
  1010.         }

  1011.         print.log("  [恢复] ⚠ 多次尝试后仍未回到阅读页,继续翻页");
  1012.     }
  1013. };

  1014. // =============================================================================
  1015. //  第四部分:主入口
  1016. // =============================================================================

  1017. function main() {
  1018.     if (!Engine.init()) {
  1019.         print.log("✗ 引擎初始化失败,退出");
  1020.         return;
  1021.     }

  1022.     // 获取目标App配置
  1023.     var app = null;
  1024.     for (var i = 0; i < CONFIG.App表.length; i++) {
  1025.         if (CONFIG.App表[i].编号 === CONFIG.目标App编号) {
  1026.             app = CONFIG.App表[i];
  1027.             break;
  1028.         }
  1029.     }

  1030.     if (!app) {
  1031.         print.log("✗ 目标App编号 " + CONFIG.目标App编号 + " 不存在!");
  1032.         print.log("  可用编号:");
  1033.         for (var j = 0; j < CONFIG.App表.length; j++) {
  1034.             print.log("    " + CONFIG.App表[j].编号 + " = " + CONFIG.App表[j].名称 + "(" + CONFIG.App表[j].包名 + ")");
  1035.         }
  1036.         return;
  1037.     }

  1038.     // 先回桌面
  1039.     Engine.home();
  1040.     sleep.second(2);

  1041.     // 运行
  1042.     try {
  1043.         ReadingBot.run(app);
  1044.     } catch(e) {
  1045.         print.log("⚠ 主流程异常: " + e.message);
  1046.         print.log("  尝试恢复...");
  1047.         try { Engine.home(); } catch(e2) {}
  1048.     }

  1049.     print.log("\n✓ 脚本完成!");
  1050.     print.timeEnd();
  1051. }

  1052. // 启动
  1053. main();
复制代码







欢迎光临 B2B网络软件 (http://bbs.niubt.cn/) Powered by Discuz! X3.2