YYPOST群发软件 发表于 2026-8-4 06:50:45

京东每日签到领京豆脚本

京东每日签到领京豆脚本

/*
* ============================================================
* AIWROK 京东每日签到领京豆脚本 - 无障碍AUTO模式
* ============================================================
* 功能: 自动打开京东APP,进入签到页面完成每日签到领京豆
* 流程: 启动京东 → 进入"我的" → 点击签到入口 → 完成签到 →
*       领取奖励 → 关闭弹窗 → 统计结果
*
* 技术特点:
*   1. 纯无障碍节点解析,不依赖固定坐标
*   2. 三级安全点击策略(节点直点→父节点→坐标兜底)
*   3. 多路径签到入口查找(签到卡片→领京豆→签到按钮)
*   4. 自动关闭广告弹窗和奖励弹窗
*   5. 签到状态检测(已签到/未签到/签到成功)
*   6. 支持定时循环执行(每日自动签到)
*   7. 连续错误自动恢复与APP重启
*
* 兼容性: Rhino ES5 引擎 / AIWROK 安卓自动化平台
* ============================================================
*/

print.log("===== 京东每日签到领京豆(无障碍AUTO模式)=====");
print.time();

// ============================================================
// 一、配置参数
// ============================================================
var config = {
    APP包名: "com.jingdong.app.mall",    // 京东商城包名
    页面加载等待秒: 5,                     // 页面切换等待秒数
    操作间隔毫秒: 1000,                   // 操作间隔毫秒
    最大重试次数: 3,                      // 单步操作最大重试次数
    最大连续错误: 5,                      // 连续错误上限
    调试模式: 'test',                     // 调试模式:'debug'=只输出页面节点摘要不签到, 'test'=执行完整签到流程, 'false'=正常模式
    循环签到: false,                      // 是否循环签到(定时模式)
    循环间隔分钟: 30,                     // 循环间隔分钟数
    // 签到入口关键词:京东签到入口在"我的"页面"领88豆"按钮
    // 实际可点击节点desc="领88豆京豆最高",text="领88豆"(不可点击,需父节点遍历)
    // 数字每天变化,用textIncludes匹配"豆"或desc匹配"领"+"豆"
    首页签到入口: ["领京豆", "签到", "签到领豆", "领金币", "双签", "每日签到"],
    我的页签到入口: ["每日签到", "领京豆", "签到", "签到领豆", "做任务领京豆", "签到中心", "领金币", "领88豆"]
};

// ============================================================
// 二、初始化
// ============================================================
print.log("\n【初始化】设置无障碍详细模式...");
auto.setDetailedModel();

var 屏幕宽 = screen.getScreenWidth();
var 屏幕高 = screen.getScreenHeight();
print.log("屏幕尺寸:" + 屏幕宽 + " x " + 屏幕高);

// ============================================================
// 三、通用工具函数
// ============================================================

/**
* 查找文字节点(同时搜索text和desc属性)
* @param {Array} 关键词列表 - 关键词数组,依次尝试精确匹配和包含匹配
* @return {Object} 找到的节点对象或null
*/
function 查找文字节点(关键词列表) {
    try {
      var 节点集合 = auto.nodeSearch(2000);
      if (节点集合 == '' || 节点集合 == null) {
            return null;
      }

      for (var i = 0; i < 关键词列表.length; i++) {
            var 关键词 = 关键词列表;

            // 精确文本匹配
            var 文本过滤 = 节点集合.text(关键词);
            if (文本过滤 != '' && 文本过滤 != null && 文本过滤.count() > 0) {
                var 节点 = 文本过滤.getNode(0);
                if (节点 != '' && 节点 != null) {
                  return 节点;
                }
            }

            // 包含文本匹配
            var 包含过滤 = 节点集合.textIncludes(关键词);
            if (包含过滤 != '' && 包含过滤 != null && 包含过滤.count() > 0) {
                var 节点2 = 包含过滤.getNode(0);
                if (节点2 != '' && 节点2 != null) {
                  return 节点2;
                }
            }
      }

      // desc属性兜底搜索(京东签到按钮的可点击节点desc含"领88豆京豆最高"但text为空)
      for (var j = 0; j < 关键词列表.length; j++) {
            var 关键词2 = 关键词列表;
            var 节点 = 查找描述节点([关键词2]);
            if (节点) {
                return 节点;
            }
      }
    } catch (e) {
      print.log("⚠ 查找文字节点出错:" + e.message);
    }
    return null;
}

/**
* 查找描述节点(通过desc属性搜索)
* 遍历所有可点击节点,检查desc是否包含关键词
* @param {Array} 关键词列表 - 关键词数组
* @return {Object} 找到的节点对象或null
*/
function 查找描述节点(关键词列表) {
    try {
      var 节点集合 = auto.nodeSearch(2000);
      if (节点集合 == '' || 节点集合 == null) {
            return null;
      }

      // 遍历所有节点检查desc
      var 总数 = 节点集合.count();
      for (var i = 0; i < 总数; i++) {
            var 节点 = 节点集合.getNode(i);
            if (节点 == '' || 节点 == null) {
                continue;
            }
            try {
                var desc = "" + 节点.getDesc();
                if (!desc || desc == "null" || desc.length == 0) {
                  continue;
                }
                for (var j = 0; j < 关键词列表.length; j++) {
                  var 关键词 = 关键词列表;
                  if (desc.indexOf(关键词) >= 0) {
                        return 节点;
                  }
                }
            } catch (e) {
            }
      }
    } catch (e) {
      print.log("⚠ 查找描述节点出错:" + e.message);
    }
    return null;
}

/**
* 查找desc中同时包含多个关键词的节点
* 例如: 查找描述节点含多词(["领", "豆"]) 会匹配 desc="领88豆京豆最高"
* @param {Array} 关键词列表 - 需要同时包含的关键词数组
* @return {Object} 找到的节点对象或null
*/
function 查找描述节点含多词(关键词列表) {
    try {
      var 节点集合 = auto.nodeSearch(2000);
      if (节点集合 == '' || 节点集合 == null) {
            return null;
      }

      var 总数 = 节点集合.count();
      for (var i = 0; i < 总数; i++) {
            var 节点 = 节点集合.getNode(i);
            if (节点 == '' || 节点 == null) {
                continue;
            }
            try {
                var desc = "" + 节点.getDesc();
                if (!desc || desc == "null" || desc.length == 0) {
                  continue;
                }
                // 检查是否同时包含所有关键词
                var 全部匹配 = true;
                for (var j = 0; j < 关键词列表.length; j++) {
                  if (desc.indexOf(关键词列表) < 0) {
                        全部匹配 = false;
                        break;
                  }
                }
                if (全部匹配) {
                  print.log(" desc=[" + desc + "]");
                  return 节点;
                }
            } catch (e) {
            }
      }
    } catch (e) {
      print.log("⚠ 查找描述节点含多词出错:" + e.message);
    }
    return null;
}

/**
* 检查文字是否存在
* @param {Array} 关键词列表 - 关键词数组
* @return {boolean} 存在返回true
*/
function 检查文字存在(关键词列表) {
    var 规则 = 关键词列表.join("||");
    return auto.findText(规则);
}

/**
* 安全点击节点 - 三级降级策略
* 策略: 1)节点可点击→直接点击 2)父/祖父节点可点击→点击父级 3)兜底→坐标点击
* @param {Object} 节点 - 要点击的节点对象
* @return {boolean} 点击成功返回true
*/
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;
    }
}

/**
* 关闭弹窗 - 自动识别并关闭各种弹窗、广告、奖励提示
* @return {boolean} 成功关闭返回true
*/
function 关闭弹窗() {
    var 弹窗关键词 = [
      "我知道了", "知道了", "关闭", "暂不开启", "以后再说",
      "取消", "暂不更新", "下次再说", "拒绝", "不允许", "跳过",
      "稍后", "确定", "不了谢谢", "残忍拒绝", "收下", "好的"
    ];

    var 检测结果 = auto.findText(弹窗关键词.join("||"));
    if (!检测结果) {
      return false;
    }

    for (var i = 0; i < 弹窗关键词.length; i++) {
      var 关键词 = 弹窗关键词;
      var 节点 = 查找文字节点([关键词]);
      if (节点) {
            try {
                if (安全点击节点(节点)) {
                  print.log("✓ 关闭弹窗:点击【" + 关键词 + "】");
                  sleep.millisecond(毫秒 = config.操作间隔毫秒);
                  return true;
                }
            } catch (e) {
            }
      }
    }

    // 尝试点击右上角关闭按钮(X图标)
    try {
      var 节点集合 = auto.nodeSearch(1000);
      if (节点集合 != '' && 节点集合 != null) {
            var 可点击 = 节点集合.clickable(true);
            if (可点击 != '' && 可点击 != null) {
                for (var j = 0; j < 可点击.count(); j++) {
                  var n = 可点击.getNode(j);
                  if (n) {
                        var desc = "" + n.getDesc();
                        if (desc.indexOf("关闭") >= 0 || desc.indexOf("close") >= 0 ||
                            desc.indexOf("Close") >= 0) {
                            n.click();
                            print.log("✓ 点击关闭按钮(描述匹配)");
                            sleep.millisecond(毫秒 = config.操作间隔毫秒);
                            return true;
                        }
                  }
                }
            }
      }
    } catch (e) {
    }

    return false;
}

/**
* 调试输出 - 输出当前页面节点摘要
*/
function 输出页面摘要() {
    if (!config.调试模式) {
      return;
    }
    try {
      var 节点集合 = auto.nodeSearch(1000);
      if (节点集合 == '' || 节点集合 == null) {
            print.log("[调试] 无节点");
            return;
      }
      var 总数 = 节点集合.count();
      print.log("[调试] 页面节点总数:" + 总数);

      var 文本视图 = 节点集合.className("android.widget.TextView");
      if (文本视图 != '' && 文本视图 != null) {
            var 文本数 = Math.min(15, 文本视图.count());
            print.log("[调试] 前" + 文本数 + "个文本节点:");
            for (var i = 0; i < 文本数; i++) {
                var 节点 = 文本视图.getNode(i);
                if (节点) {
                  var 文本 = "";
                  try { 文本 = "" + 节点.getText(); } catch (e) {}
                  var 描述 = "";
                  try { 描述 = "" + 节点.getDesc(); } catch (e) {}
                  print.log("    " + (i + 1) + ". text=" + 文本 + " desc=" + 描述);
                }
            }
      }
    } catch (e) {
      print.log("[调试] 输出页面摘要出错:" + e.message);
    }
}

/**
* 等待页面加载并检测特征文字
* @param {Array} 特征文字列表 - 页面特征文字
* @param {number} 超时秒 - 超时秒数
* @return {boolean} 检测到特征返回true
*/
function 等待页面(特征文字列表, 超时秒) {
    var 超时毫秒 = 超时秒 * 1000;
    var 开始时间 = Date.now();

    while (Date.now() - 开始时间 < 超时毫秒) {
      if (检查文字存在(特征文字列表)) {
            return true;
      }
      sleep.millisecond(毫秒 = 500);
    }

    if (config.调试模式) {
      print.log("[调试] 等待页面超时,未检测到:" + 特征文字列表.join(", "));
      输出页面摘要();
    }
    return false;
}

// ============================================================
// 四、核心功能函数
// ============================================================

/**
* 启动京东APP
* @return {boolean} 启动成功返回true
*/
function 启动京东() {
    print.log("\n【启动】正在启动京东...");

    for (var i = 0; i < config.最大重试次数; i++) {
      try {
            app.openApp(config.APP包名);
            sleep.second(秒 = config.页面加载等待秒);

            关闭弹窗();

            // 验证京东是否正常启动(首页特征文字)
            if (检查文字存在(["首页", "分类", "发现", "购物车", "我的"])) {
                print.log("✓ 京东启动成功");
                return true;
            }

            // 兜底验证:检查是否有搜索框
            if (auto.findText("搜索")) {
                print.log("✓ 京东启动成功(搜索框检测)");
                return true;
            }
      } catch (e) {
            print.log("⚠ 启动失败(第" + (i + 1) + "次):" + e.message);
            sleep.second(秒 = 3);
      }
    }

    print.log("✗ 京东启动失败");
    return false;
}

/**
* 进入「我的」页面
* @return {boolean} 成功进入返回true
*/
function 进入我的页面() {
    print.log("\n【导航】进入「我的」页面...");

    // 方法1:通过文字查找「我的」标签
    var 我的节点 = 查找文字节点(["我的"]);
    if (我的节点) {
      if (安全点击节点(我的节点)) {
            print.log("✓ 已点击【我的】");
            sleep.second(秒 = 2);
            关闭弹窗();

            // 验证是否进入我的页面
            if (等待页面(["我的订单", "我的钱包", "账号管理", "京豆", "会员"], 3)) {
                print.log("✓ 已进入「我的」页面");
                return true;
            }
      }
    }

    // 方法2:底部导航栏坐标兜底("我的"通常在底部最右侧)
    print.log("⚠ 文字查找失败,尝试坐标点击");
    auto.clickPercent(0.9, 0.97);
    sleep.second(秒 = 2);
    关闭弹窗();

    if (等待页面(["我的订单", "我的钱包", "京豆", "会员"], 3)) {
      print.log("✓ 已进入「我的」页面(坐标点击)");
      return true;
    }

    print.log("✗ 无法进入「我的」页面");
    输出页面摘要();
    return false;
}

/**
* 输出页面完整节点摘要(调试用)
* 无论调试模式是否开启都强制输出,用于排查节点问题
*/
function 输出完整页面摘要(标签) {
    try {
      var 节点集合 = auto.nodeSearch(2000);
      if (节点集合 == '' || 节点集合 == null) {
            print.log("【页面摘要-" + 标签 + "】无节点");
            return;
      }
      var 总数 = 节点集合.count();
      print.log("【页面摘要-" + 标签 + "】节点总数:" + 总数);

      // 输出所有文本节点
      var 文本视图 = 节点集合.className("android.widget.TextView");
      if (文本视图 != '' && 文本视图 != null) {
            var 文本数 = 文本视图.count();
            print.log("【页面摘要-" + 标签 + "】文本节点数:" + 文本数);
            var 输出数 = Math.min(30, 文本数);
            for (var i = 0; i < 输出数; i++) {
                var 节点 = 文本视图.getNode(i);
                if (节点) {
                  var 文本 = "";
                  try { 文本 = "" + 节点.getText(); } catch (e) {}
                  var 描述 = "";
                  try { 描述 = "" + 节点.getDesc(); } catch (e) {}
                  var 可点击 = "";
                  try { 可点击 = 节点.isClickable() ? "可点击" : "不可点击"; } catch (e) {}
                  print.log("    " + (i + 1) + ". text=[" + 文本 + "] desc=[" + 描述 + "] " + 可点击);
                }
            }
      }

      // 输出所有可点击节点
      var 可点击节点 = 节点集合.clickable(true);
      if (可点击节点 != '' && 可点击节点 != null) {
            var 可点击数 = 可点击节点.count();
            print.log("【页面摘要-" + 标签 + "】可点击节点数:" + 可点击数);
            var 输出数2 = Math.min(20, 可点击数);
            for (var j = 0; j < 输出数2; j++) {
                var cn = 可点击节点.getNode(j);
                if (cn) {
                  var c文本 = "";
                  try { c文本 = "" + cn.getText(); } catch (e) {}
                  var c描述 = "";
                  try { c描述 = "" + cn.getDesc(); } catch (e) {}
                  var cID = "";
                  try { cID = "" + cn.getID(); } catch (e) {}
                  print.log("    点击" + (j + 1) + ". text=[" + c文本 + "] desc=[" + c描述 + "] id=[" + cID + "]");
                }
            }
      }
    } catch (e) {
      print.log("【页面摘要】输出出错:" + e.message);
    }
}

/**
* 在首页查找签到入口
* 京东签到入口通常在首页顶部"领京豆"图标
* @return {boolean} 成功进入签到页面返回true
*/
function 首页查找签到() {
    print.log("\n【签到】在首页查找签到入口...");
    关闭弹窗();
    sleep.millisecond(毫秒 = 500);

    // 调试模式下输出首页节点摘要
    if (config.调试模式 == 'debug') {
      print.log("\n>>> 首页节点摘要 <<<");
      输出完整页面摘要("首页");
    }

    for (var i = 0; i < config.首页签到入口.length; i++) {
      var 关键词 = config.首页签到入口;
      var 节点 = 查找文字节点([关键词]);
      if (节点) {
            print.log("✓ 首页找到签到入口:【" + 关键词 + "】");
            if (安全点击节点(节点)) {
                sleep.second(秒 = config.页面加载等待秒);
                关闭弹窗();

                // 验证是否进入签到页面
                if (检查文字存在(["签到", "立即签到", "签到成功", "已签到", "连签", "京豆", "领取"])) {
                  print.log("✓ 已进入签到页面(首页入口)");
                  return true;
                }

                sleep.second(秒 = 2);
                if (检查文字存在(["签到", "立即签到", "已签到", "连签"])) {
                  print.log("✓ 已进入签到页面(延迟检测)");
                  return true;
                }

                // 可能点击后进入了其他页面,返回首页重试
                print.log("⚠ 点击后未进入签到页面,返回重试");
                auto.back();
                sleep.second(秒 = 2);
            }
      }
    }

    // 首页顶部可能有签到图标但没有文字,尝试下滑一点找
    print.log("首页顶部未找到,下滑查找...");
    auto.swipPercent_v2(0.5, 0.3, 0.5, 0.6, 300, 100);
    sleep.second(秒 = 1);

    for (var j = 0; j < config.首页签到入口.length; j++) {
      var 关键词2 = config.首页签到入口;
      var 节点2 = 查找文字节点([关键词2]);
      if (节点2) {
            print.log("✓ 下滑后找到签到入口:【" + 关键词2 + "】");
            if (安全点击节点(节点2)) {
                sleep.second(秒 = config.页面加载等待秒);
                关闭弹窗();
                if (检查文字存在(["签到", "立即签到", "已签到", "连签", "京豆"])) {
                  print.log("✓ 已进入签到页面(下滑后)");
                  return true;
                }
            }
      }
    }

    return false;
}

/**
* 在「我的」页面查找签到入口
* @return {boolean} 成功进入签到页面返回true
*/
function 我的页面查找签到() {
    print.log("\n【签到】在「我的」页面查找签到入口...");

    if (config.调试模式 == 'debug') {
      print.log("\n>>> 我的页面节点摘要 <<<");
      输出完整页面摘要("我的页面");
    }

    for (var 尝试次数 = 0; 尝试次数 < config.最大重试次数; 尝试次数++) {
      print.log("第" + (尝试次数 + 1) + "次在「我的」页面查找...");

      关闭弹窗();
      sleep.millisecond(毫秒 = 500);

      // 方法1:遍历关键词列表查找(text + desc)
      for (var i = 0; i < config.我的页签到入口.length; i++) {
            var 关键词 = config.我的页签到入口;
            var 节点 = 查找文字节点([关键词]);
            if (节点) {
                print.log("✓ 「我的」页面找到签到入口:【" + 关键词 + "】");
                if (安全点击节点(节点)) {
                  sleep.second(秒 = config.页面加载等待秒);
                  关闭弹窗();

                  if (检查文字存在(["签到", "立即签到", "签到成功", "已签到", "连签", "京豆"])) {
                        print.log("✓ 已进入签到页面(我的页入口)");
                        return true;
                  }

                  sleep.second(秒 = 2);
                  if (检查文字存在(["签到", "立即签到", "已签到", "连签"])) {
                        print.log("✓ 已进入签到页面(延迟检测)");
                        return true;
                  }
                }
            }
      }

      // 方法2:desc组合搜索"领"+"豆"(解决"领88豆"数字每天变化的问题)
      // 京东签到按钮desc格式如"领88豆京豆最高",数字会变但"领"和"豆"不变
      if (尝试次数 == 0) {
            print.log("关键词未匹配,尝试desc组合搜索「领」+「豆」...");
            var 豆节点 = 查找描述节点含多词(["领", "豆"]);
            if (豆节点) {
                print.log("✓ desc组合搜索找到签到按钮");
                if (安全点击节点(豆节点)) {
                  sleep.second(秒 = config.页面加载等待秒);
                  关闭弹窗();

                  if (检查文字存在(["签到", "立即签到", "签到成功", "已签到", "连签", "京豆"])) {
                        print.log("✓ 已进入签到页面(desc组合搜索)");
                        return true;
                  }
                }
            }
      }

      // 下滑查找
      if (尝试次数 < config.最大重试次数 - 1) {
            print.log("未找到签到入口,下滑查找...");
            auto.swipPercent_v2(0.5, 0.7, 0.5, 0.3, 400, 100);
            sleep.second(秒 = 1);
      }
    }

    return false;
}

/**
* 查找并点击签到入口(主入口函数)
* 查找顺序: 首页 → 我的页面 → 搜索兜底
* @return {boolean} 成功进入签到页面返回true
*/
function 进入签到页面() {
    print.log("\n【签到】开始查找签到入口...");
    auto.refresh();

    // 路径1:在首页直接查找签到入口(京东签到入口主要在首页顶部"领京豆")
    print.log("\n>> 路径1:首页查找");
    if (首页查找签到()) {
      return true;
    }

    // 路径2:进入「我的」页面查找
    print.log("\n>> 路径2:进入「我的」页面查找");
    if (进入我的页面()) {
      if (我的页面查找签到()) {
            return true;
      }
      // 没找到,返回首页
      auto.back();
      sleep.second(秒 = 2);
    }

    // 路径3:通过搜索进入签到页面
    print.log("\n>> 路径3:搜索进入签到");
    回到首页();

    var 搜索节点 = 查找文字节点(["搜索", "搜索商品"]);
    if (搜索节点) {
      安全点击节点(搜索节点);
      sleep.second(秒 = 2);

      try {
            input.sendString("京东签到领京豆");
      } catch (e) {
            print.log("⚠ 输入失败:" + e.message);
      }
      sleep.second(秒 = 2);

      // 点击搜索按钮
      auto.clickPercent(0.95, 0.05);
      sleep.second(秒 = 3);

      if (检查文字存在(["签到", "领京豆", "签到中心"])) {
            var 签到入口 = 查找文字节点(["签到", "领京豆", "签到中心"]);
            if (签到入口) {
                安全点击节点(签到入口);
                sleep.second(秒 = config.页面加载等待秒);
                if (检查文字存在(["签到", "立即签到", "已签到"])) {
                  print.log("✓ 通过搜索进入签到页面");
                  return true;
                }
            }
      }
    }

    print.log("✗ 所有路径均无法找到签到入口");
    print.log("\n>>> 最终页面节点摘要 <<<");
    输出完整页面摘要("最终页面");
    return false;
}

/**
* 执行签到操作
* 在签到页面中查找"签到"按钮并点击
* @return {number} 0=签到成功, 1=今日已签到, 2=签到失败
*/
function 执行签到() {
    print.log("\n【签到】执行签到操作...");
    关闭弹窗();
    sleep.millisecond(毫秒 = 500);

    // 检查是否已经签到过
    if (检查文字存在(["已签到", "明天再来", "今日已签到", "已连续签到"])) {
      // 需要区分「已签到」按钮和「已签到」状态文字
      // 如果页面有「已签到」但没有「立即签到」/「签到」按钮,说明今天已签过
      if (!检查文字存在(["立即签到", "签到"]) && 检查文字存在(["已签到", "今日已签到"])) {
            print.log("ℹ 今日已签到,无需重复操作");
            return 1;
      }
    }

    // 查找并点击"签到"按钮
    var 签到按钮关键词 = ["立即签到", "签到", "点击签到", "签到领取"];
    var 签到按钮 = 查找文字节点(签到按钮关键词);

    if (签到按钮) {
      print.log("✓ 找到签到按钮");
      if (安全点击节点(签到按钮)) {
            print.log("✓ 已点击签到按钮");
            sleep.second(秒 = 2);

            // 关闭签到后弹出的奖励弹窗
            关闭弹窗();
            sleep.millisecond(毫秒 = 500);

            // 验证签到是否成功
            if (检查文字存在(["签到成功", "签到+", "获得", "领取成功", "已签到"])) {
                print.log("&#127881; 签到成功!");
                return 0;
            }

            // 再等待一下检测
            sleep.second(秒 = 2);
            关闭弹窗();

            if (检查文字存在(["签到成功", "已签到", "明天见", "连签"])) {
                print.log("&#127881; 签到成功!(延迟检测)");
                return 0;
            }

            // 即使没有明确成功提示,如果没有"立即签到"按钮了也算成功
            if (!检查文字存在(["立即签到"])) {
                print.log("&#127881; 签到成功(按钮消失验证)");
                return 0;
            }
      }
    } else {
      print.log("⚠ 未找到签到按钮");
      输出页面摘要();
    }

    // 二次尝试:下滑查找签到按钮
    print.log("下滑查找签到按钮...");
    auto.swipPercent_v2(0.5, 0.6, 0.5, 0.3, 300, 100);
    sleep.second(秒 = 1);
    关闭弹窗();

    var 签到按钮2 = 查找文字节点(签到按钮关键词);
    if (签到按钮2) {
      print.log("✓ 下滑后找到签到按钮");
      if (安全点击节点(签到按钮2)) {
            sleep.second(秒 = 2);
            关闭弹窗();
            if (检查文字存在(["签到成功", "已签到", "连签"])) {
                print.log("&#127881; 签到成功!(二次尝试)");
                return 0;
            }
      }
    }

    print.log("✗ 签到失败");
    return 2;
}

/**
* 领取签到奖励
* 签到后可能有额外奖励需要手动领取
* @return {boolean} 领取成功返回true
*/
function 领取奖励() {
    print.log("\n【奖励】检查并领取额外奖励...");
    sleep.second(秒 = 1);

    var 领取关键词 = ["领取", "立即领取", "收下", "领豆", "去领取", "点击领取"];
    var 领取节点 = 查找文字节点(领取关键词);

    if (领取节点) {
      print.log("✓ 找到奖励领取入口");
      if (安全点击节点(领取节点)) {
            sleep.second(秒 = 2);
            关闭弹窗();
            print.log("&#127873; 已领取奖励");
            return true;
      }
    }

    print.log("ℹ 暂无额外奖励可领取");
    return false;
}

/**
* 回到京东首页
* @return {boolean} 成功返回首页返回true
*/
function 回到首页() {
    print.log("\n【返回】回到京东首页...");

    for (var i = 0; i < 3; i++) {
      auto.back();
      sleep.second(秒 = 1);

      if (检查文字存在(["首页", "分类", "购物车"])) {
            print.log("✓ 已回到首页");
            return true;
      }
    }

    // 兜底:回到桌面重新打开
    print.log("返回失败,重新打开京东...");
    auto.home();
    sleep.second(秒 = 2);
    if (启动京东()) {
      return true;
    }
    return false;
}

/**
* 重启京东APP
* @return {boolean} 重启成功返回true
*/
function 重启京东() {
    print.log("✗ 需要重启京东...");
    try {
      app.resetApp(config.APP包名);
      sleep.second(秒 = 8);
      关闭弹窗();
      print.log("✓ 京东重启成功");
      return true;
    } catch (e) {
      print.log("✗ 重启失败:" + e.message);
      return false;
    }
}

// ============================================================
// 五、主流程
// ============================================================
print.log("\n【主流程】开始京东每日签到领京豆...\n");

var 签到成功次数 = 0;
var 签到跳过次数 = 0;
var 签到失败次数 = 0;

for (var 当前轮次 = 1; ; 当前轮次++) {
    print.log("\n========== 第 " + 当前轮次 + " 轮签到 ==========");

    var 连续错误 = 0;

    // ===== 步骤1:启动京东 =====
    print.log("\n【步骤1】启动京东APP...");
    if (!启动京东()) {
      print.log("✗ 启动失败,等待10秒后重试");
      sleep.second(秒 = 10);
      签到失败次数++;
      continue;
    }
    sleep.second(秒 = 2);

    // ===== 调试模式:只输出页面摘要不签到 =====
    if (config.调试模式 == 'debug') {
      print.log("\n【调试模式】当前为debug模式,只输出页面节点摘要,不执行签到");
      print.log(">>> 京东首页节点摘要 <<<");
      输出完整页面摘要("京东首页");

      // 同时进入"我的"页面输出摘要
      print.log("\n>>> 进入「我的」页面输出摘要 <<<");
      if (进入我的页面()) {
            输出完整页面摘要("我的页面");
      }

      print.log("\n【调试模式】请查看上方节点摘要,确认签到入口文字后:");
      print.log("1. 将关键词加入 config.首页签到入口 或 config.我的页签到入口");
      print.log("2. 将 config.调试模式 改为 'test' 验证完整签到流程");
      print.log("3. 验证通过后改为 'false' 进入正常模式");
      print.log("\n完成时间:");
      print.time();
      break;
    }

    // ===== 步骤2:进入签到页面(首页→我的→搜索三路径) =====
    print.log("\n【步骤2】进入签到页面...");
    if (!进入签到页面()) {
      print.log("⚠ 进入签到页面失败");
      连续错误++;

      if (连续错误 >= config.最大连续错误) {
            print.log("✗ 连续失败,重启京东...");
            重启京东();
            连续错误 = 0;
            continue;
      }
      continue;
    }

    // ===== 步骤3:执行签到 =====
    print.log("\n【步骤3】执行签到...");
    var 签到结果 = 执行签到();

    if (签到结果 == 0) {
      // 签到成功
      签到成功次数++;
      print.log("✓ 签到成功!累计成功:" + 签到成功次数 + " 次");

      // 步骤4:领取额外奖励
      print.log("\n【步骤4】领取额外奖励...");
      领取奖励();

    } else if (签到结果 == 1) {
      // 今日已签到
      签到跳过次数++;
      print.log("ℹ 今日已签到过,跳过");

    } else {
      // 签到失败
      签到失败次数++;
      print.log("✗ 签到失败");
      输出页面摘要();
    }

    // ===== 步骤5:回到首页 =====
    print.log("\n【步骤5】回到首页...");
    回到首页();

    // ===== 统计输出 =====
    print.log("\n【签到统计】");
    print.log("==========================================");
    print.log("签到成功:" + 签到成功次数 + " 次");
    print.log("已签跳过:" + 签到跳过次数 + " 次");
    print.log("签到失败:" + 签到失败次数 + " 次");
    print.log("==========================================");

    // 判断是否需要循环
    if (!config.循环签到) {
      print.log("\n✓ 单次签到模式,任务完成!");
      print.log("完成时间:");
      print.time();
      break;
    }

    // 循环模式:等待下一次签到
    var 等待秒 = config.循环间隔分钟 * 60;
    print.log("\n等待 " + config.循环间隔分钟 + " 分钟后进行下一次签到...");
    print.log("下次签到时间:约 " + new Date(Date.now() + 等待秒 * 1000).toLocaleString());

    for (var 倒计时 = 等待秒; 倒计时 > 0; 倒计时 -= 60) {
      var 显示分钟 = Math.floor(倒计时 / 60);
      print.log("剩余等待时间:约 " + 显示分钟 + " 分钟");
      sleep.second(秒 = Math.min(60, 倒计时));
    }
}

print.log("\n===== 京东每日签到领京豆完成 =====");


页: [1]
查看完整版本: 京东每日签到领京豆脚本