YYPOST群发软件 发表于 5 天前

AIWROK软件测试文心识别识别验证码示例

AIWROK软件测试文心识别识别验证码示例


// AIWROK 自动化流程:截图B站验证码 → 启动文心助手 → 发送截图询问拖动距离 → 采集回答 → 切回B站拖动滑块
// 包名:com.baidu.newapp(百度文心助手)
//
// 使用说明:
//   1. 在B站验证码页面运行脚本
//   2. 脚本自动截图并发送给文心识别
//   3. 解析文心返回的拖动距离,切回B站拖动滑块

print.log("===== 文心识别验证码拖动B站流程启动 =====");
print.time();

// ========== 配置区 ==========
var 文心包名 = "com.baidu.newapp";
var B站包名 = "tv.danmaku.bili";
var 截图保存路径 = "/sdcard/验证码截图.png";

var 等待回答超时秒 = 20;
var 回答输出等待秒 = 20;
var 滑块校准系数 = 1.0;

var 提问话术 = "请帮我分析这张图片,告诉我左边的方块需要向右移动多少像素才能和右边的缺口对齐,只输出:距离加数字";

// ========== 获取设备信息 ==========
var 屏幕宽 = screen.getScreenWidth();
var 屏幕高 = screen.getScreenHeight();
var 验证码滑块距离 = null;
print.log("屏幕尺寸:" + 屏幕宽 + " x " + 屏幕高);
print.log("代理状态:" + (agent.isOpen() ? "已开启" : "未开启"));

// ========== 工具函数 ==========

function 获取UI() {
    var xml = agent.getXml();
    return "" + xml;
}

function 字符串长度(s) {
    var str = "" + s;
    return str.length;
}

function 解码HTML实体(s) {
    var str = "" + s;
    str = str.replace(/
/g, "\n");
    str = str.replace(/
/g, "\r");
    str = str.replace(/        /g, "\t");
    str = str.replace(/'/g, "'");
    str = str.replace(/"/g, '"');
    str = str.replace(/"/g, '"');
    str = str.replace(/'/g, "'");
    str = str.replace(/&/g, "&");
    str = str.replace(/</g, "<");
    str = str.replace(/>/g, ">");
    return str;
}

function 获取当前最后AI回答(xml) {
    var 回答列表 = 采集AI回答列表(xml);
    if (回答列表.length === 0) return "";
    return 回答列表[回答列表.length - 1];
}

function 获取WebView区域(xml) {
    var xmlStr = "" + xml;
    var webViewPattern = /<node[^>]*?class="com\.baidu\.webkit\.sdk\.WebView"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/;
    var wvMatch = webViewPattern.exec(xmlStr);
    if (wvMatch) {
      return {
            x1: parseInt(wvMatch),
            y1: parseInt(wvMatch),
            x2: parseInt(wvMatch),
            y2: parseInt(wvMatch)
      };
    }
    return { x1: 0, y1: 348, x2: 屏幕宽, y2: 屏幕高 * 0.5 };
}

function OCR识别WebView区域(xml) {
    try {
      var img = screen.screenShotFull();
      
      var ocrres = img.MLKitOcr('zhs');
      var ocrText = "";
      
      if (ocrres && typeof ocrres.getAllString === 'function') {
            ocrText = "" + ocrres.getAllString();
      } else if (ocrres && typeof ocrres.getJsonString === 'function') {
            var jsonStr = "" + ocrres.getJsonString();
            var textPattern = /"text"\s*:\s*"([^"]+)"/g;
            var match;
            var allText = [];
            while ((match = textPattern.exec(jsonStr)) !== null) {
                if (match && match.length > 0) {
                  allText.push(match);
                }
            }
            ocrText = allText.join("\n");
      } else {
            var str = "" + ocrres;
            var textPattern2 = /"text"\s*:\s*"([^"]+)"/g;
            var match2;
            var allText2 = [];
            while ((match2 = textPattern2.exec(str)) !== null) {
                if (match2 && match2.length > 0) {
                  allText2.push(match2);
                }
            }
            ocrText = allText2.join("\n");
      }
      
      img.recycle();
      
      if (ocrText && ocrText.length > 0) {
            print.log("✓ MLKitOcr识别成功");
            print.log("--- OCR结果开始 ---");
            print.log(ocrText);
            print.log("--- OCR结果结束 ---");
            return "" + ocrText;
      }
      
      print.log("⚠ MLKitOcr未识别到内容");
      return "";
    } catch (e) {
      print.log("⚠ OCR识别异常:" + e.message);
      return "";
    }
}

function 提取数字(文本) {
    var str = "" + 文本;
    var 精确匹配 = str.match(/距离加(\d+)/);
    if (精确匹配) {
      var num = parseInt(精确匹配);
      if (num >= 30) {
            return "" + num;
      }
    }
    var 备选匹配 = str.match(/距离精[\u51c6\u79ef\u786e]加?(\d+)/);
    if (备选匹配) {
      var num2 = parseInt(备选匹配);
      if (num2 >= 30) {
            return "" + num2;
      }
    }
    var allMatches = str.match(/\d+/g);
    if (allMatches) {
      var 候选数字 = [];
      for (var i = 0; i < allMatches.length; i++) {
            var n = parseInt(allMatches);
            if (n >= 30 && n < 1000) {
                候选数字.push(n);
            }
      }
      if (候选数字.length > 0) {
            候选数字.sort(function(a, b) { return b - a; });
            return "" + 候选数字;
      }
      var allNums = [];
      for (var j = 0; j < allMatches.length; j++) {
            allNums.push(parseInt(allMatches));
      }
      allNums.sort(function(a, b) { return b - a; });
      return "" + allNums;
    }
    return "";
}

function 采集AI回答列表(xml) {
    var xmlStr = "" + xml;
    var 回答列表 = [];

    var hasWebView = xmlStr.indexOf('com.baidu.webkit.sdk.WebView') !== -1;
   
    if (hasWebView) {
      var ocr文本 = OCR识别WebView区域(xml);
      if (ocr文本 && 字符串长度(ocr文本) > 1) {
            回答列表.push(ocr文本);
            print.log("✓ OCR识别到WebView文本");
            print.log("--- OCR结果开始 ---");
            print.log(ocr文本);
            print.log("--- OCR结果结束 ---");
            
            var ocr数字 = 提取数字(ocr文本);
            if (ocr数字) {
                print.log("✓ 从OCR结果中提取到数字:" + ocr数字);
                回答列表.push(ocr数字);
            }
      }
    }

    var textViewPattern = /<node[^>]*?text="([^"]+)"[^>]*?class="android\.widget\.TextView"[^>]*?\/?>/g;
    var tvMatch;
    var 跳过工具栏 = true;
    while ((tvMatch = textViewPattern.exec(xmlStr)) !== null) {
      if (tvMatch && 字符串长度(tvMatch) > 1) {
            var 原始文本 = tvMatch;
            var 解码文本 = 解码HTML实体(原始文本);
            
            if (解码文本.includes("任务") || 解码文本.includes("创意") ||
                解码文本.includes("放心") || 解码文本.includes("魔法")) {
                跳过工具栏 = false;
                continue;
            }
            
            if (!跳过工具栏 && !解码文本.includes("接着问") &&
                !解码文本.includes("内容由AI生成") &&
                !回答列表.includes(解码文本)) {
                回答列表.push(解码文本);
                print.log("✓ 在TextView中找到文本: '" + 解码文本 + "'");
            }
      }
    }

    var editTextPattern = /<node[^>]*?text="([^"]+)"[^>]*?class="android\.widget\.EditText"[^>]*?\/?>/g;
    var etMatch;
    while ((etMatch = editTextPattern.exec(xmlStr)) !== null) {
      if (etMatch && 字符串长度(etMatch) > 1 && !etMatch.includes("接着问")) {
            var 原始文本 = etMatch;
            var 解码文本 = 解码HTML实体(原始文本);
            if (!解码文本.includes("内容由AI生成") && !回答列表.includes(解码文本)) {
                回答列表.push(解码文本);
                print.log("✓ 在EditText中找到文本: '" + 解码文本 + "'");
            }
      }
    }

    if (回答列表.length === 0) {
      print.log("⚠ 未找到AI回答节点");
    } else {
      print.log("✓ 采集到" + 回答列表.length + "条AI回答");
    }

    return 回答列表;
}

function 全屏截图(保存路径) {
    try {
      var result = agent.shell("screencap -p " + 保存路径);
      print.log("✓ 截图命令执行:" + result);
      
      var lsResult = agent.shell("ls -la " + 保存路径);
      print.log("✓ 截图文件检查:" + lsResult);
      
      return true;
    } catch (e) {
      print.log("⚠ 截图失败:" + e.message);
      return false;
    }
}

function 判断文心打开形式(xml) {
    var xmlStr = "" + xml;
   
    var hasWebView = xmlStr.indexOf('com.baidu.webkit.sdk.WebView') !== -1;
    var hasInput = xmlStr.indexOf('text="发消息或按住说话"') !== -1 ||
                   xmlStr.indexOf('text="在这里提问"') !== -1 ||
                   xmlStr.indexOf('text="接着问"') !== -1;
   
    if (hasWebView || hasInput) {
      print.log("✓ 文心APP打开形式:直接对话页面");
      return "对话页";
    }
   
    var hasDialogTab = xmlStr.indexOf('text="对话"') !== -1;
    var hasWenxinText = xmlStr.indexOf('text="文心"') !== -1;
    var hasAssistantText = xmlStr.indexOf('text="助手"') !== -1;
    var hasRecyclerView = xmlStr.indexOf('class="androidx.recyclerview.widget.RecyclerView"') !== -1;
    var hasBottomNav = xmlStr.indexOf('text="工具"') !== -1 && xmlStr.indexOf('text="发现"') !== -1 && xmlStr.indexOf('text="我的"') !== -1;
   
    if ((hasDialogTab && hasRecyclerView) || (hasWenxinText && hasAssistantText) || hasBottomNav) {
      print.log("✓ 文心APP打开形式:对话列表页面");
      return "列表页";
    }
   
    print.log("⚠ 文心APP打开形式:未知页面");
    return "未知";
}

function 查找输入框中心(xml) {
    var xmlStr = "" + xml;
   
    var inputKeywords = ["发消息或按住说话", "在这里提问", "接着问"];
    for (var k = 0; k < inputKeywords.length; k++) {
      var inputIdx = xmlStr.indexOf('text="' + inputKeywords + '"');
      if (inputIdx !== -1) {
            var 段 = xmlStr.substring(inputIdx);
            var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m) {
                var x1 = parseInt(m);
                var y1 = parseInt(m);
                var x2 = parseInt(m);
                var y2 = parseInt(m);
                print.log("✓ 输入框定位:text=" + inputKeywords + ",bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
                return {
                  x: (x1 + x2) / 2,
                  y: (y1 + y2) / 2
                };
            }
      }
    }

    var editTextIdx = xmlStr.indexOf('class="android.widget.EditText"');
    if (editTextIdx !== -1) {
      var 段2 = xmlStr.substring(editTextIdx);
      var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      if (m2) {
            var x1 = parseInt(m2);
            var y1 = parseInt(m2);
            var x2 = parseInt(m2);
            var y2 = parseInt(m2);
            print.log("✓ 输入框定位(EditText):bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
            return {
                x: (x1 + x2) / 2,
                y: (y1 + y2) / 2
            };
      }
    }

    print.log("⚠ 未找到输入框,使用兜底坐标");
    return {
      x: 屏幕宽 * 0.4,
      y: 屏幕高 * 0.85
    };
}

function 查找加号按钮(xml) {
    var xmlStr = "" + xml;
   
    var inputKeywords = ["发消息或按住说话", "在这里提问", "接着问"];
    var 输入框边界 = null;
   
    for (var k = 0; k < inputKeywords.length; k++) {
      var inputBoxIdx = xmlStr.indexOf('text="' + inputKeywords + '"');
      if (inputBoxIdx !== -1) {
            var 输入框段 = xmlStr.substring(inputBoxIdx);
            输入框边界 = 输入框段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (输入框边界) break;
      }
    }
   
    if (!输入框边界) {
      var editTextIdx = xmlStr.indexOf('class="android.widget.EditText"');
      if (editTextIdx !== -1) {
            var 段 = xmlStr.substring(editTextIdx);
            输入框边界 = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      }
    }
   
    if (输入框边界) {
      var 输入框X2 = parseInt(输入框边界);
      var 输入框Y = parseInt(输入框边界);
      var 输入框底部Y = parseInt(输入框边界);
      var 输入框中心Y = (输入框Y + 输入框底部Y) / 2;
      
      var plusPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
      var plusMatch;
      var 候选按钮 = [];
      
      while ((plusMatch = plusPattern.exec(xmlStr)) !== null) {
            var bx1 = parseInt(plusMatch);
            var by1 = parseInt(plusMatch);
            var bx2 = parseInt(plusMatch);
            var by2 = parseInt(plusMatch);
            
            if (by1 >= 输入框中心Y - 100 && by2 <= 输入框中心Y + 200 && bx1 > 输入框X2 - 50) {
                候选按钮.push({ x: (bx1 + bx2) / 2, y: (by1 + by2) / 2 });
            }
      }
      
      if (候选按钮.length > 0) {
            候选按钮.sort(function(a, b) { return a.x - b.x; });
            print.log("✓ 加号按钮定位:输入框右侧clickable元素,坐标(" + Math.round(候选按钮.x) + "," + Math.round(候选按钮.y) + ")");
            return 候选按钮;
      }
    }

    print.log("⚠ 加号按钮使用兜底坐标(输入框右侧)");
    var 输入框 = 查找输入框中心(xml);
    return {
      x: 屏幕宽 * 0.9,
      y: 输入框.y
    };
}

function 粘贴文本(文本) {
    try {
      Clipboard.copy("" + 文本);
    } catch (e) {
      print.log("⚠ Clipboard.copy 失败:" + e.message);
    }
    sleep.millisecond(毫秒 = 300);

    try {
      agent.shell("service call clipboard 3 i32 1 s16 '" + 文本 + "' i32 0");
    } catch (e2) {
      // 忽略错误
    }
    sleep.millisecond(毫秒 = 300);

    agent.shell("input keyevent 279");
    sleep.second(秒 = 1);
    print.log("✓ 已粘贴文本:" + 文本);
}

function 查找相册选项(xml) {
    var xmlStr = "" + xml;
   
    var text关键词 = ["相册", "图片", "照片", "所有照片", "从相册选择", "选择图片"];
    for (var i = 0; i < text关键词.length; i++) {
      var kw = text关键词;
      var idx = xmlStr.indexOf('text="' + kw + '"');
      if (idx !== -1) {
            var 段 = xmlStr.substring(idx);
            var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m) {
                print.log("✓ 相册选项定位:text=" + kw);
                return {
                  x: (parseInt(m) + parseInt(m)) / 2,
                  y: (parseInt(m) + parseInt(m)) / 2
                };
            }
      }
    }

    var desc关键词 = ["相册", "图片", "照片"];
    for (var j = 0; j < desc关键词.length; j++) {
      var kw2 = desc关键词;
      var idx2 = xmlStr.indexOf('content-desc="' + kw2 + '"');
      if (idx2 !== -1) {
            var 段2 = xmlStr.substring(idx2);
            var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m2) {
                print.log("✓ 相册选项定位:content-desc=" + kw2);
                return {
                  x: (parseInt(m2) + parseInt(m2)) / 2,
                  y: (parseInt(m2) + parseInt(m2)) / 2
                };
            }
      }
    }

    return null;
}

function 是否在系统相册页面(xml) {
    var xmlStr = "" + xml;
   
    var packageMatch = xmlStr.match(/package="([^"]+)"/);
    var 当前包名 = packageMatch ? packageMatch : "";
   
    if (当前包名 && 当前包名 !== 文心包名) {
      print.log("✓ 检测到系统相册页面,包名:" + 当前包名);
      return true;
    }
   
    if (当前包名 === 文心包名) {
      var 有拍照关键词 = xmlStr.indexOf('text="拍照"') !== -1;
      var 有本地文件关键词 = xmlStr.indexOf('text="本地文件"') !== -1;
      var 有微信文件关键词 = xmlStr.indexOf('text="微信文件"') !== -1;
      
      if (有拍照关键词 || 有本地文件关键词 || 有微信文件关键词) {
            var 有图片网格 = xmlStr.indexOf('ugc_item_gridview') !== -1;
            var 有所有图片 = xmlStr.indexOf('text="所有图片"') !== -1;
            var 有取消按钮 = xmlStr.indexOf('text="取消"') !== -1;
            
            if (有图片网格 || 有所有图片 || 有取消按钮) {
                print.log("✓ 检测到文心助手内部图片选择页面");
                return true;
            }
            
            print.log("⚠ 文心助手内部附件菜单,不是系统相册");
            return false;
      }
    }
   
    var gallery关键词 = ["最近", "图库", "Camera", "所有图片", "照片", "相册", "All photos", "Photos"];
    for (var i = 0; i < gallery关键词.length; i++) {
      if (xmlStr.indexOf('text="' + gallery关键词 + '"') !== -1 ||
            xmlStr.indexOf('content-desc="' + gallery关键词 + '"') !== -1) {
            var 有聊天输入 = xmlStr.indexOf("接着问") !== -1 || xmlStr.indexOf("在这里提问") !== -1 || xmlStr.indexOf("发消息或按住说话") !== -1;
            if (!有聊天输入) {
                print.log("✓ 检测到相册页面(关键词:" + gallery关键词 + ")");
                return true;
            }
      }
    }
   
    var 有图片文件 = xmlStr.indexOf('.png"') !== -1 || xmlStr.indexOf('.jpg"') !== -1 ||
                     xmlStr.indexOf('.jpeg"') !== -1 || xmlStr.indexOf('.gif"') !== -1;
    var 有选择按钮 = xmlStr.indexOf('text="选择"') !== -1;
    var 有聊天输入2 = xmlStr.indexOf("接着问") !== -1 || xmlStr.indexOf("在这里提问") !== -1 || xmlStr.indexOf("发消息或按住说话") !== -1;
   
    if (有图片文件 && 有选择按钮 && !有聊天输入2) {
      print.log("✓ 检测到相册页面(有图片文件和选择按钮)");
      return true;
    }
   
    return false;
}

function 是否在图片预览页面(xml) {
    var xmlStr = "" + xml;
    if (xmlStr.indexOf('text="完成"') !== -1 || xmlStr.indexOf('text="发送"') !== -1) {
      return true;
    }
    if (xmlStr.indexOf('content-desc="完成"') !== -1 || xmlStr.indexOf('content-desc="发送"') !== -1) {
      return true;
    }
    return false;
}

function 查找图片预览确认按钮(xml) {
    var xmlStr = "" + xml;
   
    var 关键词 = ["完成", "发送", "确认"];
    for (var i = 0; i < 关键词.length; i++) {
      var kw = 关键词;
      var textIdx = xmlStr.indexOf('text="' + kw + '"');
      if (textIdx !== -1) {
            var 段 = xmlStr.substring(textIdx);
            var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m) {
                var x = (parseInt(m) + parseInt(m)) / 2;
                var y = (parseInt(m) + parseInt(m)) / 2;
                print.log("✓ 图片预览确认按钮定位:text=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
                return { x: x, y: y };
            }
      }
    }
   
    for (var j = 0; j < 关键词.length; j++) {
      var kw2 = 关键词;
      var descIdx = xmlStr.indexOf('content-desc="' + kw2 + '"');
      if (descIdx !== -1) {
            var 段2 = xmlStr.substring(descIdx);
            var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m2) {
                var x = (parseInt(m2) + parseInt(m2)) / 2;
                var y = (parseInt(m2) + parseInt(m2)) / 2;
                print.log("✓ 图片预览确认按钮定位:content-desc=" + kw2 + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
                return { x: x, y: y };
            }
      }
    }
   
    print.log("⚠ 图片预览确认按钮使用兜底坐标(底部中间)");
    return { x: 屏幕宽 * 0.5, y: 屏幕高 * 0.92 };
}

function 查找相册选择按钮(xml) {
    var xmlStr = "" + xml;
   
    var 关键词列表 = ["选择", "完成", "确认", "发送", "确定"];
    for (var i = 0; i < 关键词列表.length; i++) {
      var kw = 关键词列表;
      
      var textPattern = new RegExp('text="' + kw + '[^"]*"[^>]*?bounds="\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]"', 'gi');
      var textMatch = textPattern.exec(xmlStr);
      if (textMatch) {
            var x = (parseInt(textMatch) + parseInt(textMatch)) / 2;
            var y = (parseInt(textMatch) + parseInt(textMatch)) / 2;
            print.log("✓ 相册选择按钮定位:text=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
            return { x: x, y: y };
      }
      
      var descIdx = xmlStr.indexOf('content-desc="' + kw + '"');
      if (descIdx !== -1) {
            var 段2 = xmlStr.substring(descIdx);
            var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (m2) {
                var x = (parseInt(m2) + parseInt(m2)) / 2;
                var y = (parseInt(m2) + parseInt(m2)) / 2;
                print.log("✓ 相册选择按钮定位:content-desc=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
                return { x: x, y: y };
            }
      }
    }
   
    var bottomButtonPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
    var bottomMatch;
    var 底部按钮列表 = [];
    while ((bottomMatch = bottomButtonPattern.exec(xmlStr)) !== null) {
      var bx1 = parseInt(bottomMatch);
      var by1 = parseInt(bottomMatch);
      var bx2 = parseInt(bottomMatch);
      var by2 = parseInt(bottomMatch);
      if (by1 >= 屏幕高 * 0.85) {
            底部按钮列表.push({ x: (bx1 + bx2) / 2, y: (by1 + by2) / 2 });
      }
    }
   
    if (底部按钮列表.length > 0) {
      底部按钮列表.sort(function(a, b) { return b.x - a.x; });
      var 最右边按钮 = 底部按钮列表;
      print.log("✓ 相册选择按钮定位:底部最右边按钮,坐标(" + Math.round(最右边按钮.x) + "," + Math.round(最右边按钮.y) + ")");
      return { x: 最右边按钮.x, y: 最右边按钮.y };
    }
   
    print.log("⚠ 相册选择按钮使用兜底坐标(底部)");
    return { x: 屏幕宽 * 0.78, y: 屏幕高 * 0.92 };
}

function 查找第一张图片(xml) {
    var xmlStr = "" + xml;
   
    if (是否在系统相册页面(xmlStr)) {
      agent.shell("echo '" + xmlStr.substring(0, 5000) + "' > /sdcard/相册UI_dump.xml");
      print.log("✓ 已保存相册UI XML到/sdcard/相册UI_dump.xml");
      
      var packageMatch = xmlStr.match(/package="([^"]+)"/);
      var 当前包名 = packageMatch ? packageMatch : "";
      
      if (当前包名 === 文心包名) {
            var ugcPattern = /<node[^>]*?resource-id="[^"]*?ugc_img"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
            var ugcMatch;
            var ugc图片列表 = [];
            
            while ((ugcMatch = ugcPattern.exec(xmlStr)) !== null) {
                var ix1 = parseInt(ugcMatch);
                var iy1 = parseInt(ugcMatch);
                var ix2 = parseInt(ugcMatch);
                var iy2 = parseInt(ugcMatch);
                var iw = ix2 - ix1;
                var ih = iy2 - iy1;
               
                if (iw >= 100 && ih >= 100) {
                  ugc图片列表.push({ x1: ix1, y1: iy1, x2: ix2, y2: iy2, x: (ix1 + ix2) / 2, y: (iy1 + iy2) / 2 });
                }
            }
            
            if (ugc图片列表.length > 0) {
                ugc图片列表.sort(function(a, b) {
                  if (a.y1 !== b.y1) return a.y1 - b.y1;
                  return a.x1 - b.x1;
                });
                var 第一张图片 = ugc图片列表;
                print.log("✓ 文心助手内部相册找到第一张图片:坐标(" + Math.round(第一张图片.x) + "," + Math.round(第一张图片.y) + "),bounds=[" + 第一张图片.x1 + "," + 第一张图片.y1 + "][" + 第一张图片.x2 + "," + 第一张图片.y2 + "]");
                return { x: 第一张图片.x, y: 第一张图片.y };
            }
      }
      
      var nafPattern = /<node[^>]*?NAF="true"[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
      var nafMatch;
      var NAF小圆点列表 = [];
      
      while ((nafMatch = nafPattern.exec(xmlStr)) !== null) {
            var nx1 = parseInt(nafMatch);
            var ny1 = parseInt(nafMatch);
            var nx2 = parseInt(nafMatch);
            var ny2 = parseInt(nafMatch);
            var nw = nx2 - nx1;
            var nh = ny2 - ny1;
            
            if (nw >= 50 && nw <= 300 && nh >= 50 && nh <= 300) {
                if (ny1 >= 400 && ny2 <= 屏幕高 - 400) {
                  NAF小圆点列表.push({ x1: nx1, y1: ny1, x2: nx2, y2: ny2, x: (nx1 + nx2) / 2, y: (ny1 + ny2) / 2 });
                }
            }
      }
      
      if (NAF小圆点列表.length > 0) {
            NAF小圆点列表.sort(function(a, b) {
                if (a.y1 !== b.y1) return a.y1 - b.y1;
                return a.x1 - b.x1;
            });
            var 选中小圆点 = NAF小圆点列表;
            print.log("✓ ComposeView找到NAF小圆点选择框:坐标(" + Math.round(选中小圆点.x) + "," + Math.round(选中小圆点.y) + "),bounds=[" + 选中小圆点.x1 + "," + 选中小圆点.y1 + "][" + 选中小圆点.x2 + "," + 选中小圆点.y2 + "]");
            return { x: 选中小圆点.x, y: 选中小圆点.y };
      }
      
      var imageDescPattern = /<node[^>]*?content-desc="([^"]+\.(png|jpg|jpeg|gif))"[^>]*?class="android\.view\.View"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/gi;
      var imgMatch;
      var 图片节点列表 = [];
      
      while ((imgMatch = imageDescPattern.exec(xmlStr)) !== null) {
            var 文件名 = imgMatch;
            var ix1 = parseInt(imgMatch);
            var iy1 = parseInt(imgMatch);
            var ix2 = parseInt(imgMatch);
            var iy2 = parseInt(imgMatch);
            var iw = ix2 - ix1;
            var ih = iy2 - iy1;
            
            if (iw >= 100 && ih >= 100) {
                图片节点列表.push({ 文件名: 文件名, x1: ix1, y1: iy1, x2: ix2, y2: iy2, w: iw, h: ih });
            }
      }
      
      if (图片节点列表.length > 0) {
            图片节点列表.sort(function(a, b) {
                if (a.y1 !== b.y1) return a.y1 - b.y1;
                return a.x1 - b.x1;
            });
            var 第一张图片 = 图片节点列表;
            print.log("✓ 找到图片:" + 第一张图片.文件名 + ",bounds=[" + 第一张图片.x1 + "," + 第一张图片.y1 + "][" + 第一张图片.x2 + "," + 第一张图片.y2 + "]");
            
            var 选择框X = 第一张图片.x1 + 第一张图片.w * 0.77;
            var 选择框Y = 第一张图片.y1 + 第一张图片.h * 0.24;
            print.log("✓ 计算小圆点选择框坐标:(" + Math.round(选择框X) + "," + Math.round(选择框Y) + ")");
            return { x: 选择框X, y: 选择框Y };
      }
      
      var clickableViewPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
      var clickMatch;
      var 可点击区域 = [];
      while ((clickMatch = clickableViewPattern.exec(xmlStr)) !== null) {
            var cx1 = parseInt(clickMatch);
            var cy1 = parseInt(clickMatch);
            var cx2 = parseInt(clickMatch);
            var cy2 = parseInt(clickMatch);
            var cw = cx2 - cx1;
            var ch = cy2 - cy1;
            可点击区域.push({ x1: cx1, y1: cy1, x2: cx2, y2: cy2, w: cw, h: ch, x: (cx1 + cx2) / 2, y: (cy1 + cy2) / 2 });
      }
      
      var 图片区域内可点击 = [];
      for (var k = 0; k < 可点击区域.length; k++) {
            var item = 可点击区域;
            if (item.y >= 400 && item.y <= 屏幕高 - 400) {
                图片区域内可点击.push(item);
            }
      }
      
      if (图片区域内可点击.length > 0) {
            图片区域内可点击.sort(function(a, b) {
                if (a.y !== b.y) return a.y - b.y;
                return a.x - b.x;
            });
            var 第一个可点击 = 图片区域内可点击;
            print.log("✓ ComposeView图片区域内第一个可点击元素:坐标(" + Math.round(第一个可点击.x) + "," + Math.round(第一个可点击.y) + ")");
            return { x: 第一个可点击.x, y: 第一个可点击.y };
      }

      print.log("⚠ 系统相册未找到图片,输出调试信息:");
      print.log("当前包名:" + xmlStr.match(/package="([^"]+)"/));
      print.log("XML前1000字符:" + xmlStr.substring(0, 1000));
      print.log("NAF小圆点数量:" + NAF小圆点列表.length);
      print.log("图片节点数量:" + 图片节点列表.length);
      
      var allImgViews = xmlStr.match(/class="android\.widget\.ImageView"/g);
      print.log("全部ImageView数量:" + (allImgViews ? allImgViews.length : 0));
      
      return { x: 屏幕宽 * 0.25, y: 屏幕高 * 0.3 };
    }

    var rvPattern = /<node[^>]*?class="androidx\.recyclerview\.widget\.RecyclerView"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
    var rvMatch;
    var 图片列表区域 = null;

    while ((rvMatch = rvPattern.exec(xmlStr)) !== null) {
      var rx1 = parseInt(rvMatch);
      var ry1 = parseInt(rvMatch);
      var rx2 = parseInt(rvMatch);
      var ry2 = parseInt(rvMatch);
      var 高度 = ry2 - ry1;

      if (ry1 > 屏幕高 * 0.7 && 高度 > 300) {
            图片列表区域 = {x1: rx1, y1: ry1, x2: rx2, y2: ry2};
            print.log("✓ 找到底部图片列表RecyclerView:[" + rx1 + "," + ry1 + "][" + rx2 + "," + ry2 + "]");
            break;
      }
    }

    if (图片列表区域) {
      var imgPattern = /<node[^>]*?class="android\.widget\.ImageView"[^>]*?\/?>/g;
      var imgMatch;
      var 候选 = [];
      while ((imgMatch = imgPattern.exec(xmlStr)) !== null) {
            var imgNode = imgMatch;
            var boundsMatch = imgNode.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (!boundsMatch) continue;
            var ix1 = parseInt(boundsMatch);
            var iy1 = parseInt(boundsMatch);
            var ix2 = parseInt(boundsMatch);
            var iy2 = parseInt(boundsMatch);
            var iw = ix2 - ix1;
            var ih = iy2 - iy1;

            var 在区域内 = (ix1 >= 图片列表区域.x1 && ix2 <= 图片列表区域.x2 &&
                            iy1 >= 图片列表区域.y1 && iy2 <= 图片列表区域.y2);
            var 是大图 = (iw > 100 && ih > 100);

            if (在区域内 && 是大图) {
                候选.push({
                  x: (ix1 + ix2) / 2,
                  y: (iy1 + iy2) / 2,
                  y1: iy1,
                  x1: ix1
                });
            }
      }

      if (候选.length > 0) {
            候选.sort(function(a, b) {
                if (a.y1 !== b.y1) return a.y1 - b.y1;
                return a.x1 - b.x1;
            });
            var 选中 = 候选;
            print.log("✓ 第一张图片定位:底部图片列表内的ImageView,坐标(" + Math.round(选中.x) + "," + Math.round(选中.y) + ")");
            return { x: 选中.x, y: 选中.y };
      }
    }

    var imgPattern2 = /<node[^>]*?class="android\.widget\.ImageView"[^>]*?\/?>/g;
    var imgMatch2;
    var 候选2 = [];
    while ((imgMatch2 = imgPattern2.exec(xmlStr)) !== null) {
      var imgNode = imgMatch2;
      var boundsMatch = imgNode.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      if (!boundsMatch) continue;
      var ix1 = parseInt(boundsMatch);
      var iy1 = parseInt(boundsMatch);
      var ix2 = parseInt(boundsMatch);
      var iy2 = parseInt(boundsMatch);
      var iw = ix2 - ix1;
      var ih = iy2 - iy1;

      if (iy1 > 屏幕高 * 0.7 && iw > 100 && ih > 100) {
            候选2.push({
                x: (ix1 + ix2) / 2,
                y: (iy1 + iy2) / 2,
                y1: iy1,
                x1: ix1
            });
      }
    }

    if (候选2.length > 0) {
      候选2.sort(function(a, b) {
            if (a.y1 !== b.y1) return a.y1 - b.y1;
            return a.x1 - b.x1;
      });
      var 选中2 = 候选2;
      print.log("✓ 第一张图片定位:底部区域ImageView,坐标(" + Math.round(选中2.x) + "," + Math.round(选中2.y) + ")");
      return { x: 选中2.x, y: 选中2.y };
    }

    print.log("⚠ 未找到图片,使用兜底坐标(底部第一行第一列)");
    return { x: 屏幕宽 * 0.15, y: 屏幕高 * 0.85 };
}

function 查找发送按钮中心(xml) {
    var xmlStr = "" + xml;

    try {
      var detects = opencv.findImagesEx('图色168638.cv');
      if (detects != null && detects.length > 0) {
            print.log("✓ 发送按钮定位:图色匹配");
            var dStr = "" + detects;
            print.log("图色匹配结果:" + dStr);
            var m = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
            if (m) {
                var left = parseFloat(m);
                var top = parseFloat(m);
                var right = parseFloat(m);
                var bottom = parseFloat(m);
                var centerX = (left + right) / 2;
                var centerY = (top + bottom) / 2;
                print.log("图色匹配百分比坐标:left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom);
                print.log("计算点击百分比:(" + centerX + "," + centerY + ")");
                agent.clickPercent(centerX, centerY);
                print.log("✓ 已点击发送按钮(图色)");
                return null;
            }
            print.log("⚠ 图色匹配结果格式解析失败");
      }
    } catch (e) {
      print.log("⚠ 图色匹配失败:" + e.message);
    }

    var sendImgPattern = /class="android\.widget\.ImageView"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
    var imgMatches = [];
    var imgMatch;
   
    while ((imgMatch = sendImgPattern.exec(xmlStr)) !== null) {
      var ix1 = parseInt(imgMatch);
      var iy1 = parseInt(imgMatch);
      var ix2 = parseInt(imgMatch);
      var iy2 = parseInt(imgMatch);
      var iw = ix2 - ix1;
      var ih = iy2 - iy1;
      
      if (iw > 50 && ih > 50 && ix1 > 屏幕宽 * 0.7 && iy1 > 屏幕高 * 0.7) {
            imgMatches.push({
                x1: ix1, y1: iy1, x2: ix2, y2: iy2,
                x: (ix1 + ix2) / 2,
                y: (iy1 + iy2) / 2
            });
      }
    }
   
    if (imgMatches.length > 0) {
      imgMatches.sort(function(a, b) {
            return b.y1 - a.y1;
      });
      var 发送按钮 = imgMatches;
      print.log("✓ 发送按钮定位:输入框下方ImageView,坐标(" + Math.round(发送按钮.x) + "," + Math.round(发送按钮.y) + ")");
      return 发送按钮;
    }

    var sendTextIdx = xmlStr.indexOf('text="发送"');
    if (sendTextIdx !== -1) {
      var 段 = xmlStr.substring(sendTextIdx);
      var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      if (m) {
            print.log("✓ 发送按钮定位:文本匹配");
            return {
                x: (parseInt(m) + parseInt(m)) / 2,
                y: (parseInt(m) + parseInt(m)) / 2
            };
      }
    }

    var sendDescIdx = xmlStr.indexOf('content-desc="发送"');
    if (sendDescIdx !== -1) {
      var 段2 = xmlStr.substring(sendDescIdx);
      var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      if (m2) {
            print.log("✓ 发送按钮定位:content-desc匹配");
            return {
                x: (parseInt(m2) + parseInt(m2)) / 2,
                y: (parseInt(m2) + parseInt(m2)) / 2
            };
      }
    }

    var inputKeywords = ["发消息或按住说话", "在这里提问", "接着问", "分析图片内容"];
    var 输入框边界 = null;
   
    for (var k = 0; k < inputKeywords.length; k++) {
      var inputIdx = xmlStr.indexOf('text="' + inputKeywords + '"');
      if (inputIdx !== -1) {
            var 输入框段 = xmlStr.substring(inputIdx);
            输入框边界 = 输入框段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (输入框边界) break;
      }
    }
   
    if (!输入框边界) {
      var editTextIdx = xmlStr.indexOf('class="android.widget.EditText"');
      if (editTextIdx !== -1) {
            var 段 = xmlStr.substring(editTextIdx);
            输入框边界 = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      }
    }
   
    if (输入框边界) {
      var 输入框信息 = {
            x1: parseInt(输入框边界),
            y1: parseInt(输入框边界),
            x2: parseInt(输入框边界),
            y2: parseInt(输入框边界),
            centerY: (parseInt(输入框边界) + parseInt(输入框边界)) / 2
      };

      var toolbarKeywords = ["魔法修图", "视频通话", "放心写", "魔法漫画"];
      var 工具栏Y范围 = null;
      for (var t = 0; t < toolbarKeywords.length; t++) {
            var tbIdx = xmlStr.indexOf('text="' + toolbarKeywords + '"');
            if (tbIdx !== -1) {
                var tb段 = xmlStr.substring(tbIdx);
                var tbMatch = tb段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
                if (tbMatch) {
                  工具栏Y范围 = {
                        y1: parseInt(tbMatch),
                        y2: parseInt(tbMatch)
                  };
                  break;
                }
            }
      }

      var clickablePattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"[^>]*?\/?>/g;
      var clickMatch;
      var candidates = [];

      while ((clickMatch = clickablePattern.exec(xmlStr)) !== null) {
            var cx1 = parseInt(clickMatch);
            var cy1 = parseInt(clickMatch);
            var cx2 = parseInt(clickMatch);
            var cy2 = parseInt(clickMatch);
            var cw = cx2 - cx1;
            var ch = cy2 - cy1;

            var 是小按钮 = (cw < 300 && ch < 300);
            var 在输入框右侧 = (cx1 > 输入框信息.x2 - 100);
            var 在输入框水平行 = (cy1 >= 输入框信息.y1 - 100 && cy1 <= 输入框信息.y2 + 100);
            
            var 在工具栏区域 = false;
            if (工具栏Y范围 && cy1 >= 工具栏Y范围.y1 && cy2 <= 工具栏Y范围.y2 + 100) {
                在工具栏区域 = true;
            }

            if (是小按钮 && 在输入框右侧 && 在输入框水平行 && !在工具栏区域) {
                candidates.push({
                  x1: cx1, y1: cy1, x2: cx2, y2: cy2,
                  x: (cx1 + cx2) / 2,
                  y: (cy1 + cy2) / 2
                });
            }
      }

      if (candidates.length > 0) {
            candidates.sort(function(a, b) {
                return b.x1 - a.x1;
            });
            var 选中 = candidates;
            print.log("✓ 发送按钮定位:输入框右侧clickable元素,坐标(" + Math.round(选中.x) + "," + Math.round(选中.y) + ")");
            return { x: 选中.x, y: 选中.y };
      }

      var 预估X = 输入框信息.x2 + 100;
      var 预估Y = 输入框信息.centerY;
      print.log("⚠ 发送按钮使用输入框右侧兜底坐标(" + Math.round(预估X) + "," + Math.round(预估Y) + ")");
      return { x: 预估X, y: 预估Y };
    }

    print.log("⚠ 发送按钮使用屏幕右侧兜底策略");
    agent.clickPercent(0.9, 0.91);
    print.log("✓ 已点击发送按钮(硬编码兜底)");
    return null;
}

function 等待AI回答完成(发送前的最后文本, 发送前回答数量) {
    var 已耗时 = 0;
    var 检查间隔秒 = 1;

    print.log("发送前最后AI回答:'" + 发送前的最后文本 + "',发送前回答数量:" + 发送前回答数量);

    var 工具栏关键词 = ["魔法修图", "视频通话", "放心写", "魔法漫画"];
    var 发送前是工具栏 = 工具栏关键词.indexOf(发送前的最后文本) !== -1;

    var 检查间隔秒 = 3;
    var 连续相同次数 = 0;
    var 上次规范化文本 = "";

    sleep.second(秒 = 5);
    已耗时 += 5;

    while (已耗时 < 等待回答超时秒) {
      var xml = 获取UI();
      var 回答列表 = 采集AI回答列表(xml);
      
      if (回答列表.length > 0) {
            var 当前最后回答 = 回答列表[回答列表.length - 1];
            var 当前提取数字 = 提取数字(当前最后回答);
            var 当前规范化文本 = 当前最后回答.trim().replace(/\s+/g, " ");
            
            var 有新回答 = false;
            var 回答数量增加 = (回答列表.length > 发送前回答数量);
            
            if (发送前是工具栏) {
                var 当前不是工具栏 = 工具栏关键词.indexOf(当前最后回答) === -1;
                var 有OCR内容 = 当前最后回答.length > 10;
                有新回答 = 当前不是工具栏 || 有OCR内容 || 当前提取数字 !== "";
            } else {
                var 发送前规范化 = 发送前的最后文本.trim().replace(/\s+/g, " ");
                有新回答 = (当前规范化文本 !== 发送前规范化) || (当前提取数字 !== "");
            }
            
            if (有新回答 || 回答数量增加) {
                print.log("✓ 检测到新回答(" + 已耗时 + "秒):'" + 当前最后回答 + "',提取数字:'" + 当前提取数字 + "'");
               
                if (当前提取数字 !== "") {
                  print.log("✓ 提取到数字,直接返回:" + 当前提取数字);
                  return 当前提取数字;
                }
               
                if (当前规范化文本 === 上次规范化文本) {
                  连续相同次数++;
                  if (连续相同次数 >= 2) {
                        print.log("✓ 文本连续2次相同,回答完成");
                        var 最终数字 = 提取数字(当前最后回答);
                        if (最终数字 !== "") {
                            return 最终数字;
                        }
                        return 当前最后回答;
                  }
                } else {
                  连续相同次数 = 0;
                  上次规范化文本 = 当前规范化文本;
                }
            }
      }
      
      sleep.second(秒 = 检查间隔秒);
      已耗时 += 检查间隔秒;
    }

    print.log("⚠ 等待AI回答超时(" + 等待回答超时秒 + "秒),尝试返回当前内容");
    var xml = 获取UI();
    var 兜底列表 = 采集AI回答列表(xml);
    if (兜底列表.length > 0) {
      var 兜底文本 = 兜底列表[兜底列表.length - 1];
      print.log("✓ 超时兜底:返回当前采集到的回答:" + 兜底文本);
      return 兜底文本;
    }
    return null;
}

// ========== 主流程开始 ==========

function 主流程() {
    // 步骤1:全屏截图(B站验证码页面)
    print.log("\n========== 步骤1:全屏截图B站验证码 ==========");
    var 截图成功 = 全屏截图(截图保存路径);
    if (!截图成功) {
      print.log("✗ 截图失败,退出流程");
      print.log("===== 文心识别验证码拖动B站流程结束 =====");
      return;
    }

    // 步骤2:启动文心助手APP
    print.log("\n========== 步骤2:启动文心助手APP ==========");
    try {
      app.openApp(文心包名);
      print.log("✓ 已启动文心助手");
    } catch (e) {
      print.log("⚠ 启动失败:" + e.message);
    }
    sleep.second(秒 = 5);

    // 步骤3:确认进入对话页面(支持两种打开形式)
    print.log("\n========== 步骤3:确认进入对话页面 ==========");
    var xml = 获取UI();
    var 打开形式 = 判断文心打开形式(xml);
    var 进入对话成功 = false;
   
    if (打开形式 === "对话页") {
      print.log("✓ 文心APP直接打开到对话页面,无需额外操作");
      进入对话成功 = true;
    } else if (打开形式 === "列表页") {
      print.log("✓ 文心APP打开到对话列表页面,需要点击进入对话");
      var wenxinIdx = xml.indexOf('text="文心"');
      if (wenxinIdx !== -1) {
            var 文心段 = xml.substring(wenxinIdx);
            var wxBounds = 文心段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
            if (wxBounds) {
                var wxX = (parseInt(wxBounds) + parseInt(wxBounds)) / 2;
                var wxY = (parseInt(wxBounds) + parseInt(wxBounds)) / 2;
                agent.click(wxX, wxY);
                print.log("✓ 已点击文心对话,坐标(" + Math.round(wxX) + "," + Math.round(wxY) + ")");
                sleep.second(秒 = 3);
            } else {
                agent.clickPercent(0.3, 0.15);
                print.log("✓ 已点击文心对话(兜底坐标)");
                sleep.second(秒 = 3);
            }
      } else {
            agent.clickPercent(0.3, 0.15);
            print.log("✓ 已点击文心对话(兜底坐标)");
            sleep.second(秒 = 3);
      }
      
      xml = 获取UI();
      打开形式 = 判断文心打开形式(xml);
      if (打开形式 === "对话页") {
            print.log("✓ 成功进入对话页面");
            进入对话成功 = true;
      } else {
            print.log("⚠ 点击后仍未进入对话页面,再次尝试");
            agent.clickPercent(0.3, 0.15);
            sleep.second(秒 = 3);
            xml = 获取UI();
            打开形式 = 判断文心打开形式(xml);
            if (打开形式 === "对话页") {
                print.log("✓ 第二次尝试成功进入对话页面");
                进入对话成功 = true;
            }
      }
    } else {
      print.log("⚠ 文心APP打开到未知页面,等待3秒后重试");
      sleep.second(秒 = 3);
      xml = 获取UI();
      打开形式 = 判断文心打开形式(xml);
      
      if (打开形式 === "对话页") {
            print.log("✓ 等待后自动进入对话页面");
            进入对话成功 = true;
      } else if (打开形式 === "列表页") {
            print.log("✓ 等待后进入对话列表页面,点击进入对话");
            agent.clickPercent(0.3, 0.15);
            print.log("✓ 已点击文心对话(兜底坐标)");
            sleep.second(秒 = 3);
            xml = 获取UI();
            if (判断文心打开形式(xml) === "对话页") {
                print.log("✓ 点击后成功进入对话页面");
                进入对话成功 = true;
            }
      }
    }
   
    if (进入对话成功) {
      print.log("✓ 已成功进入文心对话页面");
    } else {
      print.log("⚠ 多次尝试仍未进入对话页面,继续后续步骤");
    }

    // 步骤4:记录发送前的最后一条AI回答和数量
    var 发送前回答列表 = 采集AI回答列表(xml);
    var 发送前回答数量 = 发送前回答列表.length;
    var 发送前最后文本 = "";
    if (发送前回答列表.length > 0) {
      发送前最后文本 = 发送前回答列表[发送前回答列表.length - 1];
    }
    print.log("✓ 发送前回答数量:" + 发送前回答数量 + ",最后文本:'" + 发送前最后文本 + "'");

    // 步骤5:点击加号按钮打开附件菜单
    print.log("\n========== 步骤5:点击加号按钮打开附件菜单 ==========");
    var 加号按钮 = 查找加号按钮(xml);
    agent.click(加号按钮.x, 加号按钮.y);
    print.log("✓ 已点击加号按钮,坐标(" + Math.round(加号按钮.x) + "," + Math.round(加号按钮.y) + ")");
    sleep.second(秒 = 2);

    // 步骤6:选择相册或图片
    print.log("\n========== 步骤6:选择相册或图片 ==========");
    var 相册选择成功 = false;
    var 尝试次数 = 0;
    while (!相册选择成功 && 尝试次数 < 5) {
      尝试次数++;
      xml = 获取UI();
      
      if (是否在系统相册页面(xml)) {
            print.log("✓ 已进入系统相册页面");
            相册选择成功 = true;
            break;
      }
      
      var 相册选项 = 查找相册选项(xml);
      if (相册选项) {
            agent.click(相册选项.x, 相册选项.y);
            print.log("✓ 已点击相册选项,坐标(" + Math.round(相册选项.x) + "," + Math.round(相册选项.y) + ")");
            sleep.second(秒 = 2);
            相册选择成功 = true;
            break;
      }
      
      var imgCount = (xml.match(/class="android\.widget\.ImageView"/g) || []).length;
      if (imgCount >= 4) {
            print.log("✓ 加号菜单底部已显示图片列表(检测到" + imgCount + "个图片)");
            相册选择成功 = true;
            break;
      }
      
      print.log("⚠ 第" + 尝试次数 + "次尝试:未找到相册选项,等待重试");
      sleep.second(秒 = 1);
    }

    // 步骤7:选择第一张图片(处理系统相册和图片预览页面)
    print.log("\n========== 步骤7:选择第一张图片 ==========");
    var 图片选择成功 = false;
    var 图片尝试次数 = 0;
    while (!图片选择成功 && 图片尝试次数 < 10) {
      图片尝试次数++;
      xml = 获取UI();
      
      if (图片尝试次数 === 1) {
            var pkgMatch = xml.match(/package="([^"]+)"/);
            print.log("调试:当前包名=" + (pkgMatch ? pkgMatch : "未知"));
            print.log("调试:XML前300字符=" + xml.substring(0, 300));
            print.log("调试:是否有接着问=" + (xml.indexOf("接着问") !== -1));
            var dbgImgCount = (xml.match(/class="android\.widget\.ImageView"/g) || []).length;
            print.log("调试:ImageView数量=" + dbgImgCount);
      }
      
      if (是否在图片预览页面(xml)) {
            var 确认按钮 = 查找图片预览确认按钮(xml);
            agent.click(确认按钮.x, 确认按钮.y);
            print.log("✓ 已点击图片预览确认按钮,坐标(" + Math.round(确认按钮.x) + "," + Math.round(确认按钮.y) + ")");
            图片选择成功 = true;
            sleep.second(秒 = 3);
            break;
      }
      
      if (是否在系统相册页面(xml)) {
            var 第一张图片 = 查找第一张图片(xml);
            if (第一张图片) {
                agent.click(第一张图片.x, 第一张图片.y);
                print.log("✓ 已点击第一张图片,坐标(" + Math.round(第一张图片.x) + "," + Math.round(第一张图片.y) + ")");
                sleep.second(秒 = 2);
               
                var pkgMatch = xml.match(/package="([^"]+)"/);
                var 当前包名 = pkgMatch ? pkgMatch : "";
               
                if (当前包名 === 文心包名) {
                  xml = 获取UI();
                  
                  var 确认按钮 = 查找图片预览确认按钮(xml);
                  if (确认按钮) {
                        agent.click(确认按钮.x, 确认按钮.y);
                        print.log("✓ 文心助手内部相册:点击确认按钮,坐标(" + Math.round(确认按钮.x) + "," + Math.round(确认按钮.y) + ")");
                  } else {
                        var 完成按钮Idx = xml.indexOf('text="完成"');
                        if (完成按钮Idx !== -1) {
                            var 完成段 = xml.substring(完成按钮Idx);
                            var 完成匹配 = 完成段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
                            if (完成匹配) {
                              var 完成X = (parseInt(完成匹配) + parseInt(完成匹配)) / 2;
                              var 完成Y = (parseInt(完成匹配) + parseInt(完成匹配)) / 2;
                              agent.click(完成X, 完成Y);
                              print.log("✓ 文心助手内部相册:点击完成按钮,坐标(" + Math.round(完成X) + "," + Math.round(完成Y) + ")");
                            }
                        }
                  }
                  
                  图片选择成功 = true;
                  sleep.second(秒 = 3);
                  break;
                }
               
                xml = 获取UI();
                var 选择按钮 = 查找相册选择按钮(xml);
                if (选择按钮) {
                  agent.click(选择按钮.x, 选择按钮.y);
                  print.log("✓ 已点击选择按钮,坐标(" + Math.round(选择按钮.x) + "," + Math.round(选择按钮.y) + ")");
                  图片选择成功 = true;
                  sleep.second(秒 = 3);
                  break;
                } else {
                  print.log("⚠ 第" + 图片尝试次数 + "次:未找到选择按钮,重新获取XML重试");
                  sleep.second(秒 = 1);
                  continue;
                }
            } else {
                print.log("⚠ 系统相册未找到图片");
                sleep.second(秒 = 1);
                continue;
            }
      }
      
      print.log("⚠ 第" + 图片尝试次数 + "次尝试:未检测到相册页面,等待重试");
      sleep.second(秒 = 1);
    }

    // 步骤8:等待图片添加到输入区域(等待回到文心对话页面)
    print.log("\n========== 步骤8:等待图片添加到输入区域 ==========");
    var 图片添加成功 = false;
    var 等待尝试次数 = 0;
    while (!图片添加成功 && 等待尝试次数 < 10) {
      等待尝试次数++;
      xml = 获取UI();
      
      var packageMatch = xml.match(/package="([^"]+)"/);
      if (packageMatch && packageMatch === 文心包名) {
            print.log("✓ 已回到文心对话页面");
            
            var hasInput = xml.indexOf('text="在这里提问"') !== -1 ||
                        xml.indexOf('text="接着问"') !== -1 ||
                        xml.indexOf('text="分析图片内容"') !== -1;
            
            var hasImagePreview = xml.indexOf('class="android.widget.HorizontalScrollView"') !== -1 &&
                              xml.indexOf('class="android.widget.FrameLayout"') !== -1;
            
            if (hasInput || hasImagePreview) {
                print.log("✓ 图片已添加到输入区域");
                图片添加成功 = true;
                break;
            }
      }
      
      print.log("⚠ 第" + 等待尝试次数 + "次尝试:图片尚未添加,等待重试");
      sleep.second(秒 = 1);
    }
   
    if (!图片添加成功) {
      print.log("⚠ 多次等待图片未添加,继续后续步骤");
    }

    // 步骤9:粘贴文字询问拖动距离
    print.log("\n========== 步骤9:粘贴文字询问拖动距离 ==========");
    xml = 获取UI();
    var 输入框 = 查找输入框中心(xml);
    var AI回答 = null;
    if (输入框) {
      agent.click(输入框.x, 输入框.y);
      sleep.second(秒 = 1);
      
      var hasDefaultText = xml.indexOf('text="分析图片内容"') !== -1;
      if (hasDefaultText) {
            agent.shell("input keyevent 112");
            sleep.millisecond(毫秒 = 500);
            agent.shell("input keyevent 112");
            sleep.millisecond(毫秒 = 500);
            agent.shell("input keyevent 112");
            sleep.millisecond(毫秒 = 500);
            agent.shell("input keyevent 67");
            sleep.millisecond(毫秒 = 300);
            agent.shell("input keyevent 67");
            sleep.millisecond(毫秒 = 300);
            agent.shell("input keyevent 67");
            sleep.millisecond(毫秒 = 300);
            agent.shell("input keyevent 67");
            sleep.millisecond(毫秒 = 300);
            agent.shell("input keyevent 67");
            sleep.millisecond(毫秒 = 300);
            print.log("✓ 已清除默认文本'分析图片内容'");
      }
      
      粘贴文本(提问话术);
      print.log("✓ 已粘贴文本:" + 提问话术);
      sleep.second(秒 = 2);

      // 步骤10:发送图片和文字
      print.log("\n========== 步骤10:发送图片和文字 ==========");
      xml = 获取UI();
      var 发送按钮 = 查找发送按钮中心(xml);
      if (发送按钮) {
            agent.click(发送按钮.x, 发送按钮.y);
            print.log("✓ 已点击发送按钮,坐标(" + Math.round(发送按钮.x) + "," + Math.round(发送按钮.y) + ")");
            agent.shell("input keyevent 66");
            print.log("✓ 已按回车键发送");
      }
      if (!发送按钮) {
            agent.shell("input keyevent 66");
            print.log("✓ 已按回车键发送");
      }
      sleep.second(秒 = 25);

      // 步骤11:等待文心AI回答
      print.log("\n========== 步骤11:等待文心AI回答 ==========");
      xml = 获取UI();
      var 发送前实际回答 = 获取当前最后AI回答(xml);
      var 发送前实际数量 = 采集AI回答列表(xml).length;
      print.log("发送前重新检测:最后回答='" + 发送前实际回答 + "',数量=" + 发送前实际数量);
      AI回答 = 等待AI回答完成(发送前实际回答, 发送前实际数量);
    } else {
      print.log("⚠ 未找到输入框");
    }
   
    if (!AI回答) {
      print.log("✗ 未获取到AI回答");
      print.log("===== 文心识别验证码拖动B站流程结束 =====");
      return;
    }

    print.log("\n✓ 采集到AI回答");
    print.log("--- 采集内容开始 ---");
    print.log(AI回答);
    print.log("--- 采集内容结束 ---");

    // 步骤12:解析拖动距离坐标
    print.log("\n========== 步骤12:解析拖动距离坐标 ==========");
    print.log("✓ AI原始回答:'" + AI回答 + "'");
   
    var 所有数字 = AI回答.match(/\d+/g);
    var 拖动距离像素 = null;
    var AI图像宽度 = null;
   
    if (所有数字 && 所有数字.length >= 2) {
      var 数字列表 = [];
      for (var i = 0; i < 所有数字.length; i++) {
            数字列表.push(parseInt(所有数字));
      }
      print.log("✓ 提取到所有数字:" + 数字列表.join(", "));
      
      if (数字列表.length >= 5) {
            var x1 = 数字列表;
            var y1 = 数字列表;
            var x2 = 数字列表;
            var y2 = 数字列表;
            AI图像宽度 = 数字列表;
            拖动距离像素 = Math.abs(x2 - x1);
            print.log("✓ 识别到两组坐标+图像宽度:(" + x1 + "," + y1 + ") 和 (" + x2 + "," + y2 + "),图像宽度=" + AI图像宽度);
            print.log("✓ 计算拖动距离:|x2 - x1| = |" + x2 + " - " + x1 + "| = " + 拖动距离像素);
      } else if (数字列表.length >= 4) {
            var x1 = 数字列表;
            var y1 = 数字列表;
            var x2 = 数字列表;
            var y2 = 数字列表;
            拖动距离像素 = Math.abs(x2 - x1);
            AI图像宽度 = Math.max(x1, x2) + 100;
            print.log("✓ 识别到两组坐标格式:(" + x1 + "," + y1 + ") 和 (" + x2 + "," + y2 + ")");
            print.log("✓ 计算拖动距离:|x2 - x1| = |" + x2 + " - " + x1 + "| = " + 拖动距离像素);
            print.log("✓ 估算图像宽度:max(x1,x2)+100 = " + AI图像宽度);
      } else if (数字列表.length >= 2) {
            var 坐标匹配 = AI回答.match(/(\d+)\s*[,,]\s*(\d+)/);
            if (坐标匹配) {
                var cx1 = parseInt(坐标匹配);
                var cx2 = parseInt(坐标匹配);
                拖动距离像素 = Math.abs(cx2 - cx1);
                AI图像宽度 = Math.max(cx1, cx2) + 100;
                print.log("✓ 识别到坐标格式:(" + cx1 + "," + cx2 + "),计算距离:" + 拖动距离像素);
            } else {
                拖动距离像素 = 数字列表;
                print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
            }
      } else {
            拖动距离像素 = 数字列表;
            print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
      }
    } else if (所有数字 && 所有数字.length === 1) {
      拖动距离像素 = parseInt(所有数字);
      print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
    } else {
      print.log("✗ 未找到有效的拖动距离");
      print.log("===== 文心识别验证码拖动B站流程结束 =====");
      return;
    }
   
    var 图像缩放系数 = 1.0;
    if (拖动距离像素 < 30) {
      print.log("⚠ 提取到的拖动距离过小(" + 拖动距离像素 + "),可能是OCR识别错误,跳过缩放补偿");
    } else if (AI图像宽度 && AI图像宽度 > 0 && AI图像宽度 < 屏幕宽) {
      图像缩放系数 = 屏幕宽 / AI图像宽度;
      if (图像缩放系数 > 5) {
            图像缩放系数 = 1.0;
      } else {
            拖动距离像素 = Math.round(拖动距离像素 * 图像缩放系数);
            print.log("✓ 图像缩放补偿:AI图像宽度=" + AI图像宽度 + ",屏幕宽=" + 屏幕宽);
            print.log("✓ 缩放系数=" + 图像缩放系数.toFixed(2) + ",补偿后拖动距离=" + 拖动距离像素);
      }
    } else {
      图像缩放系数 = 屏幕宽 / 750;
      拖动距离像素 = Math.round(拖动距离像素 * 图像缩放系数);
      print.log("✓ 单数字模式补偿:假设AI图像宽度≈750,缩放系数=" + 图像缩放系数.toFixed(2));
      print.log("✓ 补偿后拖动距离=" + 拖动距离像素);
    }
   
    验证码滑块距离 = {
      x: 拖动距离像素,
      y: 0
    };

    // 步骤13:切换回B站并拖动滑块
    print.log("\n========== 步骤13:切换回B站并拖动滑块 ==========");
   
    agent.shell("input keyevent 187");
    print.log("✓ 已按后台键打开任务列表");
    sleep.second(秒 = 2);
   
    xml = 获取UI();
    var biliIdx = xml.indexOf('哔哩哔哩');
    if (biliIdx === -1) {
      biliIdx = xml.indexOf('bilibili');
    }
    if (biliIdx === -1) {
      biliIdx = xml.indexOf('B站');
    }
   
    if (biliIdx !== -1) {
      var 段 = xml.substring(biliIdx);
      var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
      if (m) {
            var clickX = (parseInt(m) + parseInt(m)) / 2;
            var clickY = (parseInt(m) + parseInt(m)) / 2;
            agent.click(clickX, clickY);
            print.log("✓ 已点击B站任务卡片,坐标(" + Math.round(clickX) + "," + Math.round(clickY) + ")");
      }
    } else {
      print.log("⚠ 未找到B站任务卡片,尝试直接打开");
      try {
            app.openApp(B站包名);
            print.log("✓ 已打开B站");
      } catch (e) {
            print.log("⚠ 打开B站失败:" + e.message);
      }
    }
    sleep.second(秒 = 3);

    xml = 获取UI();
    var 验证码区域左边界 = 屏幕宽 * 0.15;
   
    var 滑块轨道匹配 = xml.match(/<node[^>]*?text="拖动滑块完成拼图"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
    if (滑块轨道匹配) {
      验证码区域左边界 = parseInt(滑块轨道匹配);
    }

    var 拖动距离 = 验证码滑块距离.x * 滑块校准系数;

    var startX = 验证码区域左边界 + 50;
    var startY = 屏幕高 * 0.42;
    var endX = startX + 拖动距离;
    var endY = startY;

    print.log("✓ 验证码区域左边界: " + 验证码区域左边界 + ", 拖动距离: " + 拖动距离);
    agent.swip(startX, startY, endX, endY, 2000);
    print.log("✓ 已拖动滑块,起点(" + Math.round(startX) + "," + Math.round(startY) + "),终点(" + Math.round(endX) + "," + Math.round(endY) + ")");
}

主流程();

print.log("\n===== 文心识别验证码拖动B站流程结束 =====");

页: [1]
查看完整版本: AIWROK软件测试文心识别识别验证码示例