解析Kimi返回的拖动距离示例

380

主题

193

回帖

2397

积分

管理员

积分
2397
解析Kimi返回的拖动距离示例

2.png

1.png

3.png

  1. // AIWROK 自动化流程:截图B站验证码 → 启动Kimi助手 → 发送截图询问拖动距离 → 采集回答 → 切回B站拖动滑块
  2. // 包名:com.moonshot.kimichat(Kimi AI)
  3. //
  4. // 使用说明:
  5. //   1. 在B站验证码页面运行脚本
  6. //   2. 脚本自动截图并发送给Kimi识别
  7. //   3. 解析Kimi返回的拖动距离,切回B站拖动滑块
  8. //   4. Kimi使用WebView/Compose渲染,XML可能无可用节点,故采用OCR优先+坐标兜底策略

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

  11. // ========== 配置区 ==========
  12. var KIMI包名 = "com.moonshot.kimichat";
  13. var B站包名 = "tv.danmaku.bili";
  14. var 截图保存路径 = "/sdcard/验证码截图.png";

  15. var 等待回答超时秒 = 60;
  16. var 回答输出等待秒 = 60;
  17. var 滑块校准系数 = 1.0;

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

  19. // ========== 获取设备信息 ==========
  20. var 屏幕宽 = screen.getScreenWidth();
  21. var 屏幕高 = screen.getScreenHeight();
  22. var 验证码滑块距离 = null;
  23. print.log("屏幕尺寸:" + 屏幕宽 + " x " + 屏幕高);
  24. try {
  25.     var 代理状态 = agent.isOpen();
  26.     print.log("代理状态:" + (代理状态 ? "已开启" : "未开启"));
  27. } catch (e) {
  28.     print.log("代理状态检查失败:" + e.message);
  29. }

  30. // ========== 工具函数 ==========

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

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

  39. function 解码HTML实体(s) {
  40.     var str = "" + s;
  41.     str = str.replace(/
/g, "\n");
  42.     str = str.replace(/
/g, "\r");
  43.     str = str.replace(/	/g, "\t");
  44.     str = str.replace(/'/g, "'");
  45.     str = str.replace(/"/g, '"');
  46.     str = str.replace(/"/g, '"');
  47.     str = str.replace(/'/g, "'");
  48.     str = str.replace(/&/g, "&");
  49.     str = str.replace(/&lt;/g, "<");
  50.     str = str.replace(/&gt;/g, ">");
  51.     return str;
  52. }

  53. function 获取当前最后AI回答(xml) {
  54.     var 回答列表 = 采集AI回答列表(xml);
  55.     if (回答列表.length === 0) return "";
  56.    
  57.     print.log("调试:回答列表共" + 回答列表.length + "条:" + 回答列表.join(", "));
  58.    
  59.     var 候选回答 = [];
  60.     for (var i = 0; i < 回答列表.length; i++) {
  61.         var 回答 = 回答列表[i];
  62.         if (回答.indexOf("距离") !== -1 || 回答.indexOf("像素") !== -1) {
  63.             候选回答.push({ text: 回答, len: 回答.length });
  64.         }
  65.     }
  66.    
  67.     if (候选回答.length > 0) {
  68.         候选回答.sort(function(a, b) { return a.len - b.len; });
  69.         print.log("✓ 找到包含距离信息的回答,选择最短的:'" + 候选回答[0].text + "'");
  70.         return 候选回答[0].text;
  71.     }
  72.    
  73.     for (var j = 0; j < 回答列表.length; j++) {
  74.         var 回答2 = 回答列表[j];
  75.         var 数字匹配 = 回答2.match(/\d+/g);
  76.         if (数字匹配 && 数字匹配.length > 0) {
  77.             var 数字 = parseInt(数字匹配[0]);
  78.             if (数字 >= 10 && 数字 <= 500) {
  79.                 print.log("✓ 找到包含合理数字的回答:'" + 回答2 + "'");
  80.                 return 回答2;
  81.             }
  82.         }
  83.     }
  84.    
  85.     return 回答列表[回答列表.length - 1];
  86. }

  87. function OCR识别屏幕区域(xml) {
  88.     try {
  89.         var img = screen.screenShotFull();
  90.         
  91.         var ocrres = img.MLKitOcr('zhs');
  92.         var ocrText = "";
  93.         
  94.         if (ocrres && typeof ocrres.getAllString === 'function') {
  95.             ocrText = "" + ocrres.getAllString();
  96.         } else if (ocrres && typeof ocrres.getJsonString === 'function') {
  97.             var jsonStr = "" + ocrres.getJsonString();
  98.             var textPattern = /"text"\s*:\s*"([^"]+)"/g;
  99.             var match;
  100.             var allText = [];
  101.             while ((match = textPattern.exec(jsonStr)) !== null) {
  102.                 if (match[1] && match[1].length > 0) {
  103.                     allText.push(match[1]);
  104.                 }
  105.             }
  106.             ocrText = allText.join("\n");
  107.         } else {
  108.             var str = "" + ocrres;
  109.             var textPattern2 = /"text"\s*:\s*"([^"]+)"/g;
  110.             var match2;
  111.             var allText2 = [];
  112.             while ((match2 = textPattern2.exec(str)) !== null) {
  113.                 if (match2[1] && match2[1].length > 0) {
  114.                     allText2.push(match2[1]);
  115.                 }
  116.             }
  117.             ocrText = allText2.join("\n");
  118.         }
  119.         
  120.         img.recycle();
  121.         
  122.         if (ocrText && ocrText.length > 0) {
  123.             print.log("✓ MLKitOcr识别成功");
  124.             print.log("--- OCR结果开始 ---");
  125.             print.log(ocrText);
  126.             print.log("--- OCR结果结束 ---");
  127.             return "" + ocrText;
  128.         }
  129.         
  130.         print.log("⚠ MLKitOcr未识别到内容");
  131.         return "";
  132.     } catch (e) {
  133.         print.log("⚠ OCR识别异常:" + e.message);
  134.         return "";
  135.     }
  136. }

  137. function 提取数字(文本) {
  138.     var str = "" + 文本;
  139.     var 精确匹配 = str.match(/距离加(\d+)/);
  140.     if (精确匹配) {
  141.         var num = parseInt(精确匹配[1]);
  142.         if (num >= 30) {
  143.             return "" + num;
  144.         }
  145.     }
  146.     var 备选匹配 = str.match(/距离精[\u51c6\u79ef\u786e]加?(\d+)/);
  147.     if (备选匹配) {
  148.         var num2 = parseInt(备选匹配[1]);
  149.         if (num2 >= 30) {
  150.             return "" + num2;
  151.         }
  152.     }
  153.     var 距离匹配 = str.match(/距离[\uFF0C,\s]*(\d+)/);
  154.     if (距离匹配) {
  155.         var num3 = parseInt(距离匹配[1]);
  156.         if (num3 >= 30) {
  157.             return "" + num3;
  158.         }
  159.     }
  160.     var allMatches = str.match(/\d+/g);
  161.     if (allMatches) {
  162.         var 候选数字 = [];
  163.         for (var i = 0; i < allMatches.length; i++) {
  164.             var n = parseInt(allMatches[i]);
  165.             if (n >= 30 && n < 1000) {
  166.                 候选数字.push(n);
  167.             }
  168.         }
  169.         if (候选数字.length > 0) {
  170.             候选数字.sort(function(a, b) { return b - a; });
  171.             return "" + 候选数字[0];
  172.         }
  173.         var allNums = [];
  174.         for (var j = 0; j < allMatches.length; j++) {
  175.             allNums.push(parseInt(allMatches[j]));
  176.         }
  177.         allNums.sort(function(a, b) { return b - a; });
  178.         return "" + allNums[0];
  179.     }
  180.     return "";
  181. }

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

  185.     var textViewPattern = /<node[^>]*?text="([^"]+)"[^>]*?class="android\.widget\.TextView"[^>]*?\/?>/g;
  186.     var tvMatch;
  187.     while ((tvMatch = textViewPattern.exec(xmlStr)) !== null) {
  188.         if (tvMatch[1] && 字符串长度(tvMatch[1]) > 1) {
  189.             var 原始文本 = tvMatch[1];
  190.             var 解码文本 = 解码HTML实体(原始文本);
  191.             
  192.             if (解码文本.includes("给Kimi发消息") ||
  193.                 解码文本.includes("有问题尽管问我") ||
  194.                 解码文本.includes("拍照解题") ||
  195.                 解码文本.includes("深度研究") ||
  196.                 解码文本.includes("OK Computer") ||
  197.                 解码文本.includes("发送") ||
  198.                 解码文本.includes("消息")) {
  199.                 continue;
  200.             }
  201.             
  202.             if (!回答列表.includes(解码文本)) {
  203.                 回答列表.push(解码文本);
  204.                 print.log("✓ 在TextView中找到文本: '" + 解码文本 + "'");
  205.             }
  206.         }
  207.     }

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

  213.     return 回答列表;
  214. }

  215. function 全屏截图(保存路径) {
  216.     try {
  217.         var result = agent.shell("screencap -p " + 保存路径);
  218.         print.log("✓ 截图命令执行:" + result);
  219.         
  220.         var lsResult = agent.shell("ls -la " + 保存路径);
  221.         print.log("✓ 截图文件检查:" + lsResult);
  222.         
  223.         return true;
  224.     } catch (e) {
  225.         print.log("⚠ 截图失败:" + e.message);
  226.         return false;
  227.     }
  228. }

  229. function 判断KIMI打开形式(xml) {
  230.     var xmlStr = "" + xml;
  231.    
  232.     var inputKeywords = ["给Kimi发消息", "有问题尽管问我", "输入消息", "发消息"];
  233.     var hasInput = false;
  234.     for (var i = 0; i < inputKeywords.length; i++) {
  235.         if (xmlStr.indexOf('text="' + inputKeywords[i] + '"') !== -1) {
  236.             hasInput = true;
  237.             break;
  238.         }
  239.     }
  240.    
  241.     var plusKeywords = ["加号", "附件", "图片", "相册"];
  242.     var hasPlus = false;
  243.     for (var j = 0; j < plusKeywords.length; j++) {
  244.         if (xmlStr.indexOf('text="' + plusKeywords[j] + '"') !== -1 ||
  245.             xmlStr.indexOf('content-desc="' + plusKeywords[j] + '"') !== -1) {
  246.             hasPlus = true;
  247.             break;
  248.         }
  249.     }
  250.    
  251.     if (hasInput || hasPlus) {
  252.         print.log("✓ Kimi APP打开形式:对话页面");
  253.         return "对话页";
  254.     }
  255.    
  256.     print.log("⚠ Kimi APP打开形式:未知页面");
  257.     return "未知";
  258. }

  259. function 开启新对话(xml) {
  260.     var xmlStr = "" + xml;
  261.    
  262.     var newChatIdx = xmlStr.indexOf('content-desc="开启新会话"');
  263.     if (newChatIdx !== -1) {
  264.         var 段 = xmlStr.substring(newChatIdx);
  265.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  266.         if (m) {
  267.             var x = (parseInt(m[1]) + parseInt(m[3])) / 2;
  268.             var y = (parseInt(m[2]) + parseInt(m[4])) / 2;
  269.             agent.click(x, y);
  270.             print.log("✓ 已点击开启新对话按钮,content-desc=开启新会话,坐标(" + Math.round(x) + "," + Math.round(y) + ")");
  271.             sleep.second(秒 = 3);
  272.             return true;
  273.         }
  274.     }
  275.    
  276.     var rightButtonPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  277.     var rbMatch;
  278.     var 右上角按钮 = [];
  279.     while ((rbMatch = rightButtonPattern.exec(xmlStr)) !== null) {
  280.         var bx1 = parseInt(rbMatch[1]);
  281.         var by1 = parseInt(rbMatch[2]);
  282.         if (bx1 > 屏幕宽 * 0.85 && by1 > 100 && by1 < 400) {
  283.             右上角按钮.push({ x: (bx1 + parseInt(rbMatch[3])) / 2, y: (by1 + parseInt(rbMatch[4])) / 2 });
  284.         }
  285.     }
  286.    
  287.     if (右上角按钮.length > 0) {
  288.         右上角按钮.sort(function(a, b) { return a.x - b.x; });
  289.         var 最右边按钮 = 右上角按钮[右上角按钮.length - 1];
  290.         agent.click(最右边按钮.x, 最右边按钮.y);
  291.         print.log("✓ 已点击右上角按钮开启新对话,坐标(" + Math.round(最右边按钮.x) + "," + Math.round(最右边按钮.y) + ")");
  292.         sleep.second(秒 = 3);
  293.         return true;
  294.     }
  295.    
  296.     print.log("⚠ 未找到开启新对话按钮,使用坐标兜底(右上角)");
  297.     var 兜底X = 屏幕宽 * 0.96;
  298.     var 兜底Y = 屏幕高 * 0.06;
  299.     agent.click(兜底X, 兜底Y);
  300.     print.log("✓ 已点击右上角兜底位置开启新对话,坐标(" + Math.round(兜底X) + "," + Math.round(兜底Y) + ")");
  301.     sleep.second(秒 = 3);
  302.     return true;
  303. }

  304. function 查找输入框中心(xml) {
  305.     var xmlStr = "" + xml;
  306.    
  307.     var inputKeywords = ["有问题尽管问我...", "给Kimi发消息", "输入消息", "发消息", "接着问"];
  308.     for (var i = 0; i < inputKeywords.length; i++) {
  309.         var kw = inputKeywords[i];
  310.         var inputIdx = xmlStr.indexOf('text="' + kw + '"');
  311.         if (inputIdx !== -1) {
  312.             var 段 = xmlStr.substring(inputIdx);
  313.             var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  314.             if (m) {
  315.                 var x1 = parseInt(m[1]);
  316.                 var y1 = parseInt(m[2]);
  317.                 var x2 = parseInt(m[3]);
  318.                 var y2 = parseInt(m[4]);
  319.                 print.log("✓ 输入框定位:text=" + kw + ",bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
  320.                 return {
  321.                     x: (x1 + x2) / 2,
  322.                     y: (y1 + y2) / 2
  323.                 };
  324.             }
  325.         }
  326.     }

  327.     var editTextIdx = xmlStr.indexOf('class="android.widget.EditText"');
  328.     if (editTextIdx !== -1) {
  329.         var 段2 = xmlStr.substring(editTextIdx);
  330.         var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  331.         if (m2) {
  332.             var x1 = parseInt(m2[1]);
  333.             var y1 = parseInt(m2[2]);
  334.             var x2 = parseInt(m2[3]);
  335.             var y2 = parseInt(m2[4]);
  336.             print.log("✓ 输入框定位(EditText):bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
  337.             return {
  338.                 x: (x1 + x2) / 2,
  339.                 y: (y1 + y2) / 2
  340.             };
  341.         }
  342.     }

  343.     var viewFactoryHolderIdx = xmlStr.indexOf('class="androidx.compose.ui.viewinterop.ViewFactoryHolder"');
  344.     if (viewFactoryHolderIdx !== -1) {
  345.         var 段3 = xmlStr.substring(viewFactoryHolderIdx);
  346.         var m3 = 段3.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  347.         if (m3) {
  348.             var x1 = parseInt(m3[1]);
  349.             var y1 = parseInt(m3[2]);
  350.             var x2 = parseInt(m3[3]);
  351.             var y2 = parseInt(m3[4]);
  352.             print.log("✓ 输入框定位(ViewFactoryHolder):bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]");
  353.             return {
  354.                 x: (x1 + x2) / 2,
  355.                 y: (y1 + y2) / 2
  356.             };
  357.         }
  358.     }

  359.     print.log("⚠ 未找到输入框,使用坐标兜底(屏幕下方区域)");
  360.     return {
  361.         x: 屏幕宽 * 0.35,
  362.         y: 屏幕高 * 0.55
  363.     };
  364. }

  365. function 查找加号按钮(xml) {
  366.     var xmlStr = "" + xml;
  367.    
  368.     var plusDescIdx = xmlStr.indexOf('content-desc="更多输入方式"');
  369.     if (plusDescIdx !== -1) {
  370.         var 段 = xmlStr.substring(plusDescIdx);
  371.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  372.         if (m) {
  373.             var x1 = parseInt(m[1]);
  374.             var y1 = parseInt(m[2]);
  375.             var x2 = parseInt(m[3]);
  376.             var y2 = parseInt(m[4]);
  377.             var centerX = (x1 + x2) / 2;
  378.             var centerY = (y1 + y2) / 2;
  379.             print.log("✓ 加号按钮定位:content-desc=更多输入方式,坐标(" + Math.round(centerX) + "," + Math.round(centerY) + ")");
  380.             return { x: centerX, y: centerY };
  381.         }
  382.     }

  383.     var inputIdx = xmlStr.indexOf('text="有问题尽管问我..."');
  384.     if (inputIdx === -1) {
  385.         inputIdx = xmlStr.indexOf('text="给Kimi发消息"');
  386.     }
  387.     if (inputIdx === -1) {
  388.         inputIdx = xmlStr.indexOf('text="有问题尽管问我"');
  389.     }
  390.    
  391.     if (inputIdx !== -1) {
  392.         var 输入框段 = xmlStr.substring(inputIdx);
  393.         var 输入框边界 = 输入框段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  394.         if (输入框边界) {
  395.             var 输入框X2 = parseInt(输入框边界[3]);
  396.             var 输入框Y = parseInt(输入框边界[2]);
  397.             var 输入框底部Y = parseInt(输入框边界[4]);
  398.             var 输入框中心Y = (输入框Y + 输入框底部Y) / 2;
  399.             
  400.             var clickablePattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  401.             var clickMatch;
  402.             var 候选按钮 = [];
  403.             
  404.             while ((clickMatch = clickablePattern.exec(xmlStr)) !== null) {
  405.                 var bx1 = parseInt(clickMatch[1]);
  406.                 var by1 = parseInt(clickMatch[2]);
  407.                 var bx2 = parseInt(clickMatch[3]);
  408.                 var by2 = parseInt(clickMatch[4]);
  409.                
  410.                 if (by1 >= 输入框中心Y - 100 && by2 <= 输入框中心Y + 200 && bx1 > 输入框X2 - 50) {
  411.                     候选按钮.push({ x: (bx1 + bx2) / 2, y: (by1 + by2) / 2 });
  412.                 }
  413.             }
  414.             
  415.             if (候选按钮.length > 0) {
  416.                 候选按钮.sort(function(a, b) { return a.x - b.x; });
  417.                 print.log("✓ 加号按钮定位:输入框右侧clickable元素,坐标(" + Math.round(候选按钮[0].x) + "," + Math.round(候选按钮[0].y) + ")");
  418.                 return 候选按钮[0];
  419.             }
  420.         }
  421.     }

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

  429. function 查找相册按钮(xml) {
  430.     var xmlStr = "" + xml;
  431.    
  432.     var clickablePattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  433.     var clickMatch;
  434.     var 候选按钮 = [];
  435.    
  436.     while ((clickMatch = clickablePattern.exec(xmlStr)) !== null) {
  437.         var bx1 = parseInt(clickMatch[1]);
  438.         var by1 = parseInt(clickMatch[2]);
  439.         var bx2 = parseInt(clickMatch[3]);
  440.         var by2 = parseInt(clickMatch[4]);
  441.         
  442.         var 节点段 = xmlStr.substring(xmlStr.indexOf(clickMatch[0]));
  443.         var 节点结束 = 节点段.indexOf('</node>');
  444.         if (节点结束 !== -1) {
  445.             var 子节点内容 = 节点段.substring(0, 节点结束);
  446.             if (子节点内容.indexOf('text="相册"') !== -1 ||
  447.                 子节点内容.indexOf('content-desc="相册"') !== -1) {
  448.                 var centerX = (bx1 + bx2) / 2;
  449.                 var centerY = (by1 + by2) / 2;
  450.                 print.log("✓ 相册按钮定位:clickable父节点包含相册,bounds=[" + bx1 + "," + by1 + "][" + bx2 + "," + by2 + "],坐标(" + Math.round(centerX) + "," + Math.round(centerY) + ")");
  451.                 return { x: centerX, y: centerY };
  452.             }
  453.         }
  454.     }
  455.    
  456.     var albumDescIdx = xmlStr.indexOf('content-desc="相册"');
  457.     if (albumDescIdx !== -1) {
  458.         var 段 = xmlStr.substring(albumDescIdx);
  459.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  460.         if (m) {
  461.             var x1 = parseInt(m[1]);
  462.             var y1 = parseInt(m[2]);
  463.             var x2 = parseInt(m[3]);
  464.             var y2 = parseInt(m[4]);
  465.             var centerX = (x1 + x2) / 2;
  466.             var centerY = (y1 + y2) / 2;
  467.             print.log("✓ 相册按钮定位:content-desc=相册,bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "],坐标(" + Math.round(centerX) + "," + Math.round(centerY) + ")");
  468.             return { x: centerX, y: centerY };
  469.         }
  470.     }
  471.    
  472.     var albumKeywords = ["相册", "图片", "照片"];
  473.     for (var i = 0; i < albumKeywords.length; i++) {
  474.         var kw = albumKeywords[i];
  475.         var albumIdx = xmlStr.indexOf('text="' + kw + '"');
  476.         if (albumIdx !== -1) {
  477.             var 段 = xmlStr.substring(albumIdx);
  478.             var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  479.             if (m) {
  480.                 var x1 = parseInt(m[1]);
  481.                 var y1 = parseInt(m[2]);
  482.                 var x2 = parseInt(m[3]);
  483.                 var y2 = parseInt(m[4]);
  484.                 var centerX = (x1 + x2) / 2;
  485.                 var centerY = (y1 + y2) / 2;
  486.                 print.log("✓ 相册按钮定位:text=" + kw + ",bounds=[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "],坐标(" + Math.round(centerX) + "," + Math.round(centerY) + ")");
  487.                 return { x: centerX, y: centerY };
  488.             }
  489.         }
  490.     }

  491.     print.log("⚠ 相册按钮使用坐标兜底(底部中间)");
  492.     return {
  493.         x: 屏幕宽 * 0.5,
  494.         y: 屏幕高 * 0.88
  495.     };
  496. }

  497. function 查找相册选项(xml) {
  498.     var xmlStr = "" + xml;
  499.    
  500.     var text关键词 = ["相册", "图片", "照片", "所有照片", "从相册选择", "选择图片"];
  501.     for (var i = 0; i < text关键词.length; i++) {
  502.         var kw = text关键词[i];
  503.         var idx = xmlStr.indexOf('text="' + kw + '"');
  504.         if (idx !== -1) {
  505.             var 段 = xmlStr.substring(idx);
  506.             var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  507.             if (m) {
  508.                 print.log("✓ 相册选项定位:text=" + kw);
  509.                 return {
  510.                     x: (parseInt(m[1]) + parseInt(m[3])) / 2,
  511.                     y: (parseInt(m[2]) + parseInt(m[4])) / 2
  512.                 };
  513.             }
  514.         }
  515.     }

  516.     var desc关键词 = ["相册", "图片", "照片"];
  517.     for (var j = 0; j < desc关键词.length; j++) {
  518.         var kw2 = desc关键词[j];
  519.         var idx2 = xmlStr.indexOf('content-desc="' + kw2 + '"');
  520.         if (idx2 !== -1) {
  521.             var 段2 = xmlStr.substring(idx2);
  522.             var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  523.             if (m2) {
  524.                 print.log("✓ 相册选项定位:content-desc=" + kw2);
  525.                 return {
  526.                     x: (parseInt(m2[1]) + parseInt(m2[3])) / 2,
  527.                     y: (parseInt(m2[2]) + parseInt(m2[4])) / 2
  528.                 };
  529.             }
  530.         }
  531.     }
  532.    
  533.     return null;
  534. }

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

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

  548.     agent.shell("input keyevent 279");
  549.     sleep.millisecond(毫秒 = 500);
  550.    
  551.     try {
  552.         agent.shell("input text '" + 文本 + "'");
  553.         print.log("✓ 已通过input text直接输入文本");
  554.     } catch (e3) {
  555.         print.log("⚠ input text失败:" + e3.message);
  556.     }
  557.    
  558.     sleep.second(秒 = 1);
  559.     print.log("✓ 已粘贴文本:" + 文本);
  560. }

  561. function 是否在系统相册页面(xml) {
  562.     var xmlStr = "" + xml;
  563.    
  564.     var packageMatch = xmlStr.match(/package="([^"]+)"/);
  565.     var 当前包名 = packageMatch ? packageMatch[1] : "";
  566.    
  567.     if (当前包名 && 当前包名 !== KIMI包名) {
  568.         print.log("✓ 检测到系统相册页面,包名:" + 当前包名);
  569.         return true;
  570.     }
  571.    
  572.     return false;
  573. }

  574. function 是否在图片预览页面(xml) {
  575.     var xmlStr = "" + xml;
  576.     if (xmlStr.indexOf('text="完成"') !== -1 || xmlStr.indexOf('text="发送"') !== -1) {
  577.         return true;
  578.     }
  579.     if (xmlStr.indexOf('content-desc="完成"') !== -1 || xmlStr.indexOf('content-desc="发送"') !== -1) {
  580.         return true;
  581.     }
  582.     return false;
  583. }

  584. function 查找图片预览确认按钮(xml) {
  585.     var xmlStr = "" + xml;
  586.    
  587.     var 关键词 = ["完成", "发送", "确认"];
  588.     for (var i = 0; i < 关键词.length; i++) {
  589.         var kw = 关键词[i];
  590.         var textIdx = xmlStr.indexOf('text="' + kw + '"');
  591.         if (textIdx !== -1) {
  592.             var 段 = xmlStr.substring(textIdx);
  593.             var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  594.             if (m) {
  595.                 var x = (parseInt(m[1]) + parseInt(m[3])) / 2;
  596.                 var y = (parseInt(m[2]) + parseInt(m[4])) / 2;
  597.                 print.log("✓ 图片预览确认按钮定位:text=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
  598.                 return { x: x, y: y };
  599.             }
  600.         }
  601.     }
  602.    
  603.     for (var j = 0; j < 关键词.length; j++) {
  604.         var kw2 = 关键词[j];
  605.         var descIdx = xmlStr.indexOf('content-desc="' + kw2 + '"');
  606.         if (descIdx !== -1) {
  607.             var 段2 = xmlStr.substring(descIdx);
  608.             var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  609.             if (m2) {
  610.                 var x = (parseInt(m2[1]) + parseInt(m2[3])) / 2;
  611.                 var y = (parseInt(m2[2]) + parseInt(m2[4])) / 2;
  612.                 print.log("✓ 图片预览确认按钮定位:content-desc=" + kw2 + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
  613.                 return { x: x, y: y };
  614.             }
  615.         }
  616.     }
  617.    
  618.     print.log("⚠ 图片预览确认按钮使用坐标兜底(底部中间)");
  619.     return { x: 屏幕宽 * 0.5, y: 屏幕高 * 0.92 };
  620. }

  621. function 查找相册选择按钮(xml) {
  622.     var xmlStr = "" + xml;
  623.    
  624.     var 关键词列表 = ["选择", "完成", "确认", "发送", "确定"];
  625.     for (var i = 0; i < 关键词列表.length; i++) {
  626.         var kw = 关键词列表[i];
  627.         
  628.         var textPattern = new RegExp('text="' + kw + '[^"]*"[^>]*?bounds="\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]"', 'gi');
  629.         var textMatch = textPattern.exec(xmlStr);
  630.         if (textMatch) {
  631.             var x = (parseInt(textMatch[1]) + parseInt(textMatch[3])) / 2;
  632.             var y = (parseInt(textMatch[2]) + parseInt(textMatch[4])) / 2;
  633.             print.log("✓ 相册选择按钮定位:text=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
  634.             return { x: x, y: y };
  635.         }
  636.         
  637.         var descIdx = xmlStr.indexOf('content-desc="' + kw + '"');
  638.         if (descIdx !== -1) {
  639.             var 段2 = xmlStr.substring(descIdx);
  640.             var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  641.             if (m2) {
  642.                 var x = (parseInt(m2[1]) + parseInt(m2[3])) / 2;
  643.                 var y = (parseInt(m2[2]) + parseInt(m2[4])) / 2;
  644.                 print.log("✓ 相册选择按钮定位:content-desc=" + kw + ",坐标(" + Math.round(x) + "," + Math.round(y) + ")");
  645.                 return { x: x, y: y };
  646.             }
  647.         }
  648.     }
  649.    
  650.     var bottomButtonPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  651.     var bottomMatch;
  652.     var 底部按钮列表 = [];
  653.     while ((bottomMatch = bottomButtonPattern.exec(xmlStr)) !== null) {
  654.         var bx1 = parseInt(bottomMatch[1]);
  655.         var by1 = parseInt(bottomMatch[2]);
  656.         var bx2 = parseInt(bottomMatch[3]);
  657.         var by2 = parseInt(bottomMatch[4]);
  658.         if (by1 >= 屏幕高 * 0.85) {
  659.             底部按钮列表.push({ x: (bx1 + bx2) / 2, y: (by1 + by2) / 2 });
  660.         }
  661.     }
  662.    
  663.     if (底部按钮列表.length > 0) {
  664.         底部按钮列表.sort(function(a, b) { return b.x - a.x; });
  665.         var 最右边按钮 = 底部按钮列表[0];
  666.         print.log("✓ 相册选择按钮定位:底部最右边按钮,坐标(" + Math.round(最右边按钮.x) + "," + Math.round(最右边按钮.y) + ")");
  667.         return { x: 最右边按钮.x, y: 最右边按钮.y };
  668.     }
  669.    
  670.     print.log("⚠ 相册选择按钮使用坐标兜底(底部)");
  671.     return { x: 屏幕宽 * 0.78, y: 屏幕高 * 0.92 };
  672. }

  673. function 查找第一张图片(xml) {
  674.     var xmlStr = "" + xml;
  675.    
  676.     if (是否在系统相册页面(xmlStr)) {
  677.         agent.shell("echo '" + xmlStr.substring(0, 5000) + "' > /sdcard/相册UI_dump.xml");
  678.         print.log("✓ 已保存相册UI XML到/sdcard/相册UI_dump.xml");
  679.         
  680.         var nafPattern = /<node[^>]*?NAF="true"[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  681.         var nafMatch;
  682.         var NAF小圆点列表 = [];
  683.         
  684.         while ((nafMatch = nafPattern.exec(xmlStr)) !== null) {
  685.             var nx1 = parseInt(nafMatch[1]);
  686.             var ny1 = parseInt(nafMatch[2]);
  687.             var nx2 = parseInt(nafMatch[3]);
  688.             var ny2 = parseInt(nafMatch[4]);
  689.             var nw = nx2 - nx1;
  690.             var nh = ny2 - ny1;
  691.             
  692.             if (nw >= 50 && nw <= 300 && nh >= 50 && nh <= 300) {
  693.                 if (ny1 >= 400 && ny2 <= 屏幕高 - 400) {
  694.                     NAF小圆点列表.push({ x1: nx1, y1: ny1, x2: nx2, y2: ny2, x: (nx1 + nx2) / 2, y: (ny1 + ny2) / 2 });
  695.                 }
  696.             }
  697.         }
  698.         
  699.         if (NAF小圆点列表.length > 0) {
  700.             NAF小圆点列表.sort(function(a, b) {
  701.                 if (a.y1 !== b.y1) return a.y1 - b.y1;
  702.                 return a.x1 - b.x1;
  703.             });
  704.             var 选中小圆点 = NAF小圆点列表[0];
  705.             print.log("✓ ComposeView找到NAF小圆点选择框:坐标(" + Math.round(选中小圆点.x) + "," + Math.round(选中小圆点.y) + "),bounds=[" + 选中小圆点.x1 + "," + 选中小圆点.y1 + "][" + 选中小圆点.x2 + "," + 选中小圆点.y2 + "]");
  706.             return { x: 选中小圆点.x, y: 选中小圆点.y };
  707.         }
  708.         
  709.         var imageDescPattern = /<node[^>]*?content-desc="([^"]+\.(png|jpg|jpeg|gif))"[^>]*?class="android\.view\.View"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/gi;
  710.         var imgMatch;
  711.         var 图片节点列表 = [];
  712.         
  713.         while ((imgMatch = imageDescPattern.exec(xmlStr)) !== null) {
  714.             var 文件名 = imgMatch[1];
  715.             var ix1 = parseInt(imgMatch[3]);
  716.             var iy1 = parseInt(imgMatch[4]);
  717.             var ix2 = parseInt(imgMatch[5]);
  718.             var iy2 = parseInt(imgMatch[6]);
  719.             var iw = ix2 - ix1;
  720.             var ih = iy2 - iy1;
  721.             
  722.             if (iw >= 100 && ih >= 100) {
  723.                 图片节点列表.push({ 文件名: 文件名, x1: ix1, y1: iy1, x2: ix2, y2: iy2, w: iw, h: ih });
  724.             }
  725.         }
  726.         
  727.         if (图片节点列表.length > 0) {
  728.             图片节点列表.sort(function(a, b) {
  729.                 if (a.y1 !== b.y1) return a.y1 - b.y1;
  730.                 return a.x1 - b.x1;
  731.             });
  732.             var 第一张图片 = 图片节点列表[0];
  733.             print.log("✓ 找到图片:" + 第一张图片.文件名 + ",bounds=[" + 第一张图片.x1 + "," + 第一张图片.y1 + "][" + 第一张图片.x2 + "," + 第一张图片.y2 + "]");
  734.             
  735.             var 选择框X = 第一张图片.x1 + 第一张图片.w * 0.77;
  736.             var 选择框Y = 第一张图片.y1 + 第一张图片.h * 0.24;
  737.             print.log("✓ 计算小圆点选择框坐标:(" + Math.round(选择框X) + "," + Math.round(选择框Y) + ")");
  738.             return { x: 选择框X, y: 选择框Y };
  739.         }
  740.         
  741.         var clickableViewPattern = /<node[^>]*?clickable="true"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/g;
  742.         var clickMatch;
  743.         var 可点击区域 = [];
  744.         while ((clickMatch = clickableViewPattern.exec(xmlStr)) !== null) {
  745.             var cx1 = parseInt(clickMatch[1]);
  746.             var cy1 = parseInt(clickMatch[2]);
  747.             var cx2 = parseInt(clickMatch[3]);
  748.             var cy2 = parseInt(clickMatch[4]);
  749.             var cw = cx2 - cx1;
  750.             var ch = cy2 - cy1;
  751.             可点击区域.push({ x1: cx1, y1: cy1, x2: cx2, y2: cy2, w: cw, h: ch, x: (cx1 + cx2) / 2, y: (cy1 + cy2) / 2 });
  752.         }
  753.         
  754.         var 图片区域内可点击 = [];
  755.         for (var k = 0; k < 可点击区域.length; k++) {
  756.             var item = 可点击区域[k];
  757.             if (item.y >= 400 && item.y <= 屏幕高 - 400) {
  758.                 图片区域内可点击.push(item);
  759.             }
  760.         }
  761.         
  762.         if (图片区域内可点击.length > 0) {
  763.             图片区域内可点击.sort(function(a, b) {
  764.                 if (a.y !== b.y) return a.y - b.y;
  765.                 return a.x - b.x;
  766.             });
  767.             var 第一个可点击 = 图片区域内可点击[0];
  768.             print.log("✓ ComposeView图片区域内第一个可点击元素:坐标(" + Math.round(第一个可点击.x) + "," + Math.round(第一个可点击.y) + ")");
  769.             return { x: 第一个可点击.x, y: 第一个可点击.y };
  770.         }

  771.         print.log("⚠ 系统相册未找到图片,使用坐标兜底");
  772.         return { x: 屏幕宽 * 0.25, y: 屏幕高 * 0.3 };
  773.     }

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

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

  779.     try {
  780.         var detects = opencv.findImagesEx('图色168638.cv');
  781.         if (detects != null && detects.length > 0) {
  782.             print.log("✓ 发送按钮定位:图色匹配");
  783.             var dStr = "" + detects[0];
  784.             print.log("图色匹配结果:" + dStr);
  785.             var m = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  786.             if (m) {
  787.                 var left = parseFloat(m[1]);
  788.                 var top = parseFloat(m[2]);
  789.                 var right = parseFloat(m[3]);
  790.                 var bottom = parseFloat(m[4]);
  791.                 var centerX = (left + right) / 2;
  792.                 var centerY = (top + bottom) / 2;
  793.                 print.log("图色匹配百分比坐标:left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom);
  794.                 print.log("计算点击百分比:(" + centerX + "," + centerY + ")");
  795.                 agent.clickPercent(centerX, centerY);
  796.                 print.log("✓ 已点击发送按钮(图色)");
  797.                 return null;
  798.             }
  799.             print.log("⚠ 图色匹配结果格式解析失败");
  800.         }
  801.     } catch (e) {
  802.         print.log("⚠ 图色匹配失败:" + e.message);
  803.     }

  804.     var inputKeywords = ["给Kimi发消息", "有问题尽管问我"];
  805.     var inputIdx = -1;
  806.     for (var k = 0; k < inputKeywords.length; k++) {
  807.         inputIdx = xmlStr.indexOf('text="' + inputKeywords[k] + '"');
  808.         if (inputIdx !== -1) break;
  809.     }
  810.    
  811.     if (inputIdx !== -1) {
  812.         var 输入框段 = xmlStr.substring(inputIdx);
  813.         var 输入框边界 = 输入框段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  814.         
  815.         if (输入框边界) {
  816.             var 输入框信息 = {
  817.                 x1: parseInt(输入框边界[1]),
  818.                 y1: parseInt(输入框边界[2]),
  819.                 x2: parseInt(输入框边界[3]),
  820.                 y2: parseInt(输入框边界[4]),
  821.                 centerY: (parseInt(输入框边界[2]) + parseInt(输入框边界[4])) / 2
  822.             };

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

  826.             while ((clickMatch = clickablePattern.exec(xmlStr)) !== null) {
  827.                 var cx1 = parseInt(clickMatch[1]);
  828.                 var cy1 = parseInt(clickMatch[2]);
  829.                 var cx2 = parseInt(clickMatch[3]);
  830.                 var cy2 = parseInt(clickMatch[4]);
  831.                 var cw = cx2 - cx1;
  832.                 var ch = cy2 - cy1;

  833.                 var 是小按钮 = (cw < 300 && ch < 300);
  834.                 var 在输入框右侧 = (cx1 > 输入框信息.x2 - 100);
  835.                 var 在输入框水平行 = (cy1 >= 输入框信息.y1 - 100 && cy1 <= 输入框信息.y2 + 100);

  836.                 if (是小按钮 && 在输入框右侧 && 在输入框水平行) {
  837.                     candidates.push({
  838.                         x1: cx1, y1: cy1, x2: cx2, y2: cy2,
  839.                         x: (cx1 + cx2) / 2,
  840.                         y: (cy1 + cy2) / 2
  841.                     });
  842.                 }
  843.             }

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

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

  858.     var sendTextIdx = xmlStr.indexOf('text="发送"');
  859.     if (sendTextIdx !== -1) {
  860.         var 段 = xmlStr.substring(sendTextIdx);
  861.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  862.         if (m) {
  863.             print.log("✓ 发送按钮定位:文本匹配");
  864.             return {
  865.                 x: (parseInt(m[1]) + parseInt(m[3])) / 2,
  866.                 y: (parseInt(m[2]) + parseInt(m[4])) / 2
  867.             };
  868.         }
  869.     }

  870.     var sendDescIdx = xmlStr.indexOf('content-desc="发送"');
  871.     if (sendDescIdx !== -1) {
  872.         var 段2 = xmlStr.substring(sendDescIdx);
  873.         var m2 = 段2.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  874.         if (m2) {
  875.             print.log("✓ 发送按钮定位:content-desc匹配");
  876.             return {
  877.                 x: (parseInt(m2[1]) + parseInt(m2[3])) / 2,
  878.                 y: (parseInt(m2[2]) + parseInt(m2[4])) / 2
  879.             };
  880.         }
  881.     }

  882.     print.log("⚠ 发送按钮使用屏幕右侧坐标兜底");
  883.     agent.clickPercent(0.9, 0.92);
  884.     print.log("✓ 已点击发送按钮(硬编码兜底)");
  885.     return null;
  886. }

  887. function 等待AI回答完成(发送前的最后文本, 发送前回答数量) {
  888.     var 已耗时 = 0;
  889.     var 检查间隔秒 = 3;
  890.     var 连续相同次数 = 0;
  891.     var 上次规范化文本 = "";

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

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

  895.     while (已耗时 < 等待回答超时秒) {
  896.         var xml = 获取UI();
  897.         var 回答列表 = 采集AI回答列表(xml);
  898.         
  899.         if (回答列表.length > 0) {
  900.             var 当前最后回答 = 回答列表[回答列表.length - 1];
  901.             var 当前提取数字 = 提取数字(当前最后回答);
  902.             var 当前规范化文本 = 当前最后回答.trim().replace(/\s+/g, " ");
  903.             
  904.             var 有新回答 = false;
  905.             var 回答数量增加 = (回答列表.length > 发送前回答数量);
  906.             
  907.             var 发送前规范化 = 发送前的最后文本.trim().replace(/\s+/g, " ");
  908.             有新回答 = (当前规范化文本 !== 发送前规范化) || (当前提取数字 !== "");
  909.             
  910.             if (有新回答 || 回答数量增加) {
  911.                 print.log("✓ 检测到新回答(" + 已耗时 + "秒):'" + 当前最后回答 + "',提取数字:'" + 当前提取数字 + "'");
  912.                
  913.                 if (当前规范化文本 === 上次规范化文本) {
  914.                     连续相同次数++;
  915.                     if (连续相同次数 >= 2) {
  916.                         print.log("✓ 文本连续2次相同,回答完成");
  917.                         return 当前最后回答;
  918.                     }
  919.                 } else {
  920.                     连续相同次数 = 0;
  921.                     上次规范化文本 = 当前规范化文本;
  922.                 }
  923.             }
  924.         }
  925.         
  926.         sleep.second(秒 = 检查间隔秒);
  927.         已耗时 += 检查间隔秒;
  928.     }

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

  939. // ========== 主流程开始 ==========

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

  949.     // 步骤2:启动Kimi APP
  950.     print.log("\n========== 步骤2:启动Kimi APP ==========");
  951.     try {
  952.         app.openApp(KIMI包名);
  953.         print.log("✓ 已启动Kimi");
  954.     } catch (e) {
  955.         print.log("⚠ 启动失败:" + e.message);
  956.     }
  957.     sleep.second(秒 = 5);

  958.     // 步骤3:点击右上角新对话按钮
  959.     print.log("\n========== 步骤3:点击右上角新对话按钮 ==========");
  960.    
  961.     xml = 获取UI();
  962.    
  963.     var 新对话X = 屏幕宽 * 0.96;
  964.     var 新对话Y = 屏幕高 * 0.06;
  965.    
  966.     var newChatIdx = xml.indexOf('content-desc="开启新会话"');
  967.     if (newChatIdx !== -1) {
  968.         var 段 = xml.substring(newChatIdx);
  969.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  970.         if (m) {
  971.             新对话X = (parseInt(m[1]) + parseInt(m[3])) / 2;
  972.             新对话Y = (parseInt(m[2]) + parseInt(m[4])) / 2;
  973.             print.log("✓ 新对话按钮定位:content-desc=开启新会话,坐标(" + Math.round(新对话X) + "," + Math.round(新对话Y) + ")");
  974.         }
  975.     }
  976.    
  977.     agent.click(新对话X, 新对话Y);
  978.     print.log("✓ 已点击新对话按钮,坐标(" + Math.round(新对话X) + "," + Math.round(新对话Y) + ")");
  979.    
  980.     sleep.second(秒 = 5);
  981.     print.log("✓ 新对话操作完成,等待5秒确保界面加载");

  982.     // 步骤4:记录发送前的最后一条AI回答和数量
  983.     print.log("\n========== 步骤4:记录发送前状态 ==========");
  984.     var 发送前回答数量 = 0;
  985.     print.log("✓ 发送前回答数量:" + 发送前回答数量);

  986.     // 步骤5:点击加号按钮打开附件菜单(使用图色匹配,因为点击前XML可能不完整)
  987.     print.log("\n========== 步骤5:点击加号按钮打开附件菜单 ==========");
  988.    
  989.     var 加号点击成功 = false;
  990.     var detects = opencv.findImagesEx('图色414387.cv');
  991.     if (detects != null && detects.length > 0) {
  992.         print.log("✓ 图色匹配找到加号按钮");
  993.         print.log("图色匹配结果:" + detects[0]);
  994.         var dStr = "" + detects[0];
  995.         var m = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  996.         if (m) {
  997.             var left = parseFloat(m[1]);
  998.             var top = parseFloat(m[2]);
  999.             var right = parseFloat(m[3]);
  1000.             var bottom = parseFloat(m[4]);
  1001.             var centerX = (left + right) / 2;
  1002.             var centerY = (top + bottom) / 2;
  1003.             agent.clickPercent(centerX, centerY);
  1004.             print.log("✓ 已点击加号按钮(图色匹配),百分比坐标(" + centerX.toFixed(4) + "," + centerY.toFixed(4) + ")");
  1005.             加号点击成功 = true;
  1006.         }
  1007.     }
  1008.    
  1009.     if (!加号点击成功) {
  1010.         print.log("⚠ 图色匹配未找到加号按钮,使用坐标兜底");
  1011.         var 兜底X = 屏幕宽 * 0.92;
  1012.         var 兜底Y = 屏幕高 * 0.55;
  1013.         agent.click(兜底X, 兜底Y);
  1014.         print.log("✓ 已点击加号按钮(坐标兜底),坐标(" + Math.round(兜底X) + "," + Math.round(兜底Y) + ")");
  1015.         加号点击成功 = true;
  1016.     }
  1017.    
  1018.     sleep.second(秒 = 3);
  1019.     print.log("✓ 等待3秒让附件菜单完全弹出");

  1020.     // 步骤6:点击相册按钮选择图片(点击加号后XML已完整,使用XML定位)
  1021.     print.log("\n========== 步骤6:点击相册按钮选择图片 ==========");
  1022.    
  1023.     var 相册选择成功 = false;
  1024.     var 尝试次数 = 0;
  1025.    
  1026.     while (!相册选择成功 && 尝试次数 < 5) {
  1027.         尝试次数++;
  1028.         xml = 获取UI();
  1029.         
  1030.         if (是否在系统相册页面(xml)) {
  1031.             print.log("✓ 已进入系统相册页面");
  1032.             相册选择成功 = true;
  1033.             break;
  1034.         }
  1035.         
  1036.         var 相册位置 = 查找相册按钮(xml);
  1037.         agent.click(相册位置.x, 相册位置.y);
  1038.         print.log("✓ 已点击相册按钮,坐标(" + Math.round(相册位置.x) + "," + Math.round(相册位置.y) + ")");
  1039.         sleep.second(秒 = 2);
  1040.         
  1041.         xml = 获取UI();
  1042.         if (是否在系统相册页面(xml)) {
  1043.             print.log("✓ 已进入系统相册页面");
  1044.             相册选择成功 = true;
  1045.             break;
  1046.         }
  1047.         
  1048.         print.log("⚠ 第" + 尝试次数 + "次尝试:未进入相册页面,重新点击加号");
  1049.         
  1050.         var 加号Detects = opencv.findImagesEx('图色414387.cv');
  1051.         if (加号Detects != null && 加号Detects.length > 0) {
  1052.             var dStr2 = "" + 加号Detects[0];
  1053.             var m2 = dStr2.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  1054.             if (m2) {
  1055.                 var centerX2 = (parseFloat(m2[1]) + parseFloat(m2[3])) / 2;
  1056.                 var centerY2 = (parseFloat(m2[2]) + parseFloat(m2[4])) / 2;
  1057.                 agent.clickPercent(centerX2, centerY2);
  1058.                 print.log("✓ 重新点击加号按钮(图色匹配),百分比坐标(" + centerX2.toFixed(4) + "," + centerY2.toFixed(4) + ")");
  1059.             }
  1060.         } else {
  1061.             var 兜底X = 屏幕宽 * 0.92;
  1062.             var 兜底Y = 屏幕高 * 0.55;
  1063.             agent.click(兜底X, 兜底Y);
  1064.             print.log("✓ 重新点击加号按钮(坐标兜底),坐标(" + Math.round(兜底X) + "," + Math.round(兜底Y) + ")");
  1065.         }
  1066.         sleep.second(秒 = 3);
  1067.     }

  1068.     // 步骤7:选择第一张图片(系统相册用Canvas渲染,XML无节点,直接坐标点击)
  1069.     print.log("\n========== 步骤7:选择第一张图片 ==========");
  1070.    
  1071.     var 图片尝试次数 = 0;
  1072.     while (图片尝试次数 < 3) {
  1073.         图片尝试次数++;
  1074.         xml = 获取UI();
  1075.         
  1076.         if (是否在系统相册页面(xml)) {
  1077.             print.log("✓ 系统相册页面,点击图片位置");
  1078.             
  1079.             var 标题栏底部 = 432;
  1080.             var actionBarMatch = xml.match(/resource-id="android:id\/action_bar"[^>]*?bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  1081.             if (actionBarMatch) {
  1082.                 标题栏底部 = parseInt(actionBarMatch[4]);
  1083.             }
  1084.             
  1085.             var 图片区域顶部 = 标题栏底部;
  1086.             var 图片区域底部 = 屏幕高;
  1087.             var 图片区域高度 = 图片区域底部 - 图片区域顶部;
  1088.             
  1089.             var 图片X = 屏幕宽 * 0.5;
  1090.             var 图片Y = 图片区域顶部 + 图片区域高度 * 0.4;
  1091.             agent.click(图片X, 图片Y);
  1092.             print.log("✓ 已点击图片位置,坐标(" + Math.round(图片X) + "," + Math.round(图片Y) + ")");
  1093.             sleep.second(秒 = 3);
  1094.             
  1095.             xml = 获取UI();
  1096.             if (!是否在系统相册页面(xml)) {
  1097.                 print.log("✓ 已离开相册页面,图片可能已添加");
  1098.                 break;
  1099.             }
  1100.         } else {
  1101.             break;
  1102.         }
  1103.     }

  1104.     // 步骤8:等待图片添加到输入区域(等待回到Kimi对话页面)
  1105.     print.log("\n========== 步骤8:等待图片添加到输入区域 ==========");
  1106.     sleep.second(秒 = 3);
  1107.     xml = 获取UI();
  1108.    
  1109.     var packageMatch = xml.match(/package="([^"]+)"/);
  1110.     if (packageMatch && packageMatch[1] === KIMI包名) {
  1111.         print.log("✓ 已回到Kimi对话页面");
  1112.     }
  1113.    
  1114.     print.log("✓ 图片已添加到输入区域");

  1115.     // 步骤9:粘贴文字询问拖动距离
  1116.     print.log("\n========== 步骤9:粘贴文字询问拖动距离 ==========");
  1117.     xml = 获取UI();
  1118.    
  1119.     var 输入框位置 = 查找输入框中心(xml);
  1120.     agent.click(输入框位置.x, 输入框位置.y);
  1121.     print.log("✓ 已点击输入框,坐标(" + Math.round(输入框位置.x) + "," + Math.round(输入框位置.y) + ")");
  1122.     sleep.second(秒 = 2);
  1123.    
  1124.     try {
  1125.         Clipboard.copy("" + 提问话术);
  1126.         sleep.millisecond(毫秒 = 500);
  1127.         agent.shell("input keyevent 279");
  1128.         sleep.second(秒 = 2);
  1129.         print.log("✓ 已通过剪贴板粘贴文本");
  1130.     } catch (e) {
  1131.         print.log("⚠ 剪贴板粘贴失败:" + e.message);
  1132.         agent.shell("input text '" + 提问话术 + "'");
  1133.         sleep.second(秒 = 2);
  1134.         print.log("✓ 已通过input text直接输入文本");
  1135.     }
  1136.     sleep.second(秒 = 2);

  1137.     // 步骤10:发送图片和文字
  1138.     print.log("\n========== 步骤10:发送图片和文字 ==========");
  1139.    
  1140.     var detects = opencv.findImagesEx('图色769678.cv');
  1141.     if (detects != null) {
  1142.         print.log("✓ 图色匹配找到发送按钮");
  1143.         print.log("图色匹配结果:" + detects[0]);
  1144.         var dStr = "" + detects[0];
  1145.         var sendMatch = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  1146.         if (sendMatch) {
  1147.             var left = parseFloat(sendMatch[1]);
  1148.             var top = parseFloat(sendMatch[2]);
  1149.             var right = parseFloat(sendMatch[3]);
  1150.             var bottom = parseFloat(sendMatch[4]);
  1151.             var centerX = (left + right) / 2;
  1152.             var centerY = (top + bottom) / 2;
  1153.             agent.clickPercent(centerX, centerY);
  1154.             print.log("✓ 已点击发送按钮(图色匹配),百分比坐标(" + centerX.toFixed(4) + "," + centerY.toFixed(4) + ")");
  1155.         }
  1156.     } else {
  1157.         print.log("⚠ 图色匹配未找到发送按钮,尝试坐标兜底");
  1158.         var 发送按钮位置 = 查找发送按钮中心(xml);
  1159.         if (发送按钮位置) {
  1160.             agent.click(发送按钮位置.x, 发送按钮位置.y);
  1161.             print.log("✓ 已点击发送按钮(坐标兜底)");
  1162.         }
  1163.     }
  1164.    
  1165.     sleep.second(秒 = 10);

  1166.     // 步骤11:等待Kimi AI回答
  1167.     print.log("\n========== 步骤11:等待Kimi AI回答 ==========");
  1168.     xml = 获取UI();
  1169.     var AI回答 = 等待AI回答完成("", 0);
  1170.    
  1171.     if (!AI回答) {
  1172.         print.log("✗ 未获取到AI回答");
  1173.         print.log("===== KIMI识别验证码拖动B站流程结束 =====");
  1174.         return;
  1175.     }

  1176.     print.log("\n✓ 采集到AI回答");
  1177.    
  1178.     print.log("\n========== 步骤11.5:点击加号获取完整源码 ==========");
  1179.     var detects = opencv.findImagesEx('图色414387.cv');
  1180.     if (detects != null && detects.length > 0) {
  1181.         var dStr = "" + detects[0];
  1182.         var m = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  1183.         if (m) {
  1184.             var centerX = (parseFloat(m[1]) + parseFloat(m[3])) / 2;
  1185.             var centerY = (parseFloat(m[2]) + parseFloat(m[4])) / 2;
  1186.             agent.clickPercent(centerX, centerY);
  1187.             print.log("✓ 已点击加号按钮(图色匹配),百分比坐标(" + centerX.toFixed(4) + "," + centerY.toFixed(4) + ")");
  1188.         }
  1189.     } else {
  1190.         agent.click(屏幕宽 * 0.92, 屏幕高 * 0.55);
  1191.         print.log("✓ 已点击加号按钮(坐标兜底)");
  1192.     }
  1193.     sleep.second(秒 = 2);
  1194.     xml = 获取UI();
  1195.     print.log("✓ 点击加号后重新获取UI");
  1196.    
  1197.     AI回答 = 获取当前最后AI回答(xml);
  1198.     print.log("✓ 重新采集AI回答:'" + AI回答 + "'");
  1199.    
  1200.     print.log("--- 采集内容开始 ---");
  1201.     print.log(AI回答);
  1202.     print.log("--- 采集内容结束 ---");

  1203.     // 步骤12:解析拖动距离坐标
  1204.     print.log("\n========== 步骤12:解析拖动距离坐标 ==========");
  1205.     print.log("✓ AI原始回答:'" + AI回答 + "'");
  1206.    
  1207.     var 所有数字 = AI回答.match(/\d+/g);
  1208.     var 拖动距离像素 = null;
  1209.     var AI图像宽度 = null;
  1210.    
  1211.     if (AI回答.indexOf("距离") !== -1 || AI回答.indexOf("像素") !== -1) {
  1212.         var 距离匹配 = AI回答.match(/(\d+)\s*[像相]?素?/);
  1213.         if (距离匹配) {
  1214.             拖动距离像素 = parseInt(距离匹配[1]);
  1215.             print.log("✓ 识别到距离格式回答,提取数字:" + 拖动距离像素);
  1216.             AI图像宽度 = 屏幕宽;
  1217.             print.log("✓ 距离格式回答,使用屏幕宽度作为图像宽度,不额外缩放");
  1218.         } else {
  1219.             var 数字匹配 = AI回答.match(/(\d+)/);
  1220.             if (数字匹配) {
  1221.                 拖动距离像素 = parseInt(数字匹配[1]);
  1222.                 print.log("✓ 从距离回答中提取数字:" + 拖动距离像素);
  1223.                 AI图像宽度 = 屏幕宽;
  1224.             }
  1225.         }
  1226.     } else if (所有数字 && 所有数字.length >= 2) {
  1227.         var 数字列表 = [];
  1228.         for (var n = 0; n < 所有数字.length; n++) {
  1229.             数字列表.push(parseInt(所有数字[n]));
  1230.         }
  1231.         print.log("✓ 提取到所有数字:" + 数字列表.join(", "));
  1232.         
  1233.         if (数字列表.length >= 5) {
  1234.             var x1 = 数字列表[0];
  1235.             var y1 = 数字列表[1];
  1236.             var x2 = 数字列表[2];
  1237.             var y2 = 数字列表[3];
  1238.             AI图像宽度 = 数字列表[4];
  1239.             拖动距离像素 = Math.abs(x2 - x1);
  1240.             print.log("✓ 识别到两组坐标+图像宽度:(" + x1 + "," + y1 + ") 和 (" + x2 + "," + y2 + "),图像宽度=" + AI图像宽度);
  1241.             print.log("✓ 计算拖动距离:|x2 - x1| = |" + x2 + " - " + x1 + "| = " + 拖动距离像素);
  1242.         } else if (数字列表.length >= 4) {
  1243.             var x1 = 数字列表[0];
  1244.             var y1 = 数字列表[1];
  1245.             var x2 = 数字列表[2];
  1246.             var y2 = 数字列表[3];
  1247.             拖动距离像素 = Math.abs(x2 - x1);
  1248.             AI图像宽度 = Math.max(x1, x2) + 100;
  1249.             print.log("✓ 识别到两组坐标格式:(" + x1 + "," + y1 + ") 和 (" + x2 + "," + y2 + ")");
  1250.             print.log("✓ 计算拖动距离:|x2 - x1| = |" + x2 + " - " + x1 + "| = " + 拖动距离像素);
  1251.             print.log("✓ 估算图像宽度:max(x1,x2)+100 = " + AI图像宽度);
  1252.         } else if (数字列表.length >= 2) {
  1253.             var 坐标匹配 = AI回答.match(/(\d+)\s*[,,]\s*(\d+)/);
  1254.             if (坐标匹配) {
  1255.                 var cx1 = parseInt(坐标匹配[1]);
  1256.                 var cx2 = parseInt(坐标匹配[2]);
  1257.                 拖动距离像素 = Math.abs(cx2 - cx1);
  1258.                 AI图像宽度 = Math.max(cx1, cx2) + 100;
  1259.                 print.log("✓ 识别到坐标格式:(" + cx1 + "," + cx2 + "),计算距离:" + 拖动距离像素);
  1260.             } else {
  1261.                 拖动距离像素 = 数字列表[0];
  1262.                 print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
  1263.             }
  1264.         } else {
  1265.             拖动距离像素 = 数字列表[0];
  1266.             print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
  1267.         }
  1268.     } else if (所有数字 && 所有数字.length === 1) {
  1269.         拖动距离像素 = parseInt(所有数字[0]);
  1270.         print.log("✓ 提取到单个数字(拖动距离):" + 拖动距离像素);
  1271.     } else {
  1272.         print.log("✗ 未找到有效的拖动距离");
  1273.         print.log("===== KIMI识别验证码拖动B站流程结束 =====");
  1274.         return;
  1275.     }
  1276.    
  1277.     var 图像缩放系数 = 1.0;
  1278.     if (拖动距离像素 < 30) {
  1279.         print.log("⚠ 提取到的拖动距离过小(" + 拖动距离像素 + "),可能是OCR识别错误,跳过缩放补偿");
  1280.     } else if (AI图像宽度 && AI图像宽度 > 0 && AI图像宽度 < 屏幕宽) {
  1281.         图像缩放系数 = 屏幕宽 / AI图像宽度;
  1282.         if (图像缩放系数 > 5) {
  1283.             图像缩放系数 = 1.0;
  1284.         } else {
  1285.             拖动距离像素 = Math.round(拖动距离像素 * 图像缩放系数);
  1286.             print.log("✓ 图像缩放补偿:AI图像宽度=" + AI图像宽度 + ",屏幕宽=" + 屏幕宽);
  1287.             print.log("✓ 缩放系数=" + 图像缩放系数.toFixed(2) + ",补偿后拖动距离=" + 拖动距离像素);
  1288.         }
  1289.     } else {
  1290.         图像缩放系数 = 屏幕宽 / 750;
  1291.         拖动距离像素 = Math.round(拖动距离像素 * 图像缩放系数);
  1292.         print.log("✓ 单数字模式补偿:假设AI图像宽度≈750,缩放系数=" + 图像缩放系数.toFixed(2));
  1293.         print.log("✓ 补偿后拖动距离=" + 拖动距离像素);
  1294.     }
  1295.    
  1296.     验证码滑块距离 = {
  1297.         x: 拖动距离像素,
  1298.         y: 0
  1299.     };

  1300.     // 步骤13:切换回B站并拖动滑块
  1301.     print.log("\n========== 步骤13:切换回B站并拖动滑块 ==========");
  1302.    
  1303.     agent.shell("input keyevent 187");
  1304.     print.log("✓ 已按后台键打开任务列表");
  1305.     sleep.second(秒 = 2);
  1306.    
  1307.     xml = 获取UI();
  1308.     var biliIdx = xml.indexOf('哔哩哔哩');
  1309.     if (biliIdx === -1) {
  1310.         biliIdx = xml.indexOf('bilibili');
  1311.     }
  1312.     if (biliIdx === -1) {
  1313.         biliIdx = xml.indexOf('B站');
  1314.     }
  1315.    
  1316.     if (biliIdx !== -1) {
  1317.         var 段 = xml.substring(biliIdx);
  1318.         var m = 段.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  1319.         if (m) {
  1320.             var clickX = (parseInt(m[1]) + parseInt(m[3])) / 2;
  1321.             var clickY = (parseInt(m[2]) + parseInt(m[4])) / 2;
  1322.             agent.click(clickX, clickY);
  1323.             print.log("✓ 已点击B站任务卡片,坐标(" + Math.round(clickX) + "," + Math.round(clickY) + ")");
  1324.         }
  1325.     } else {
  1326.         print.log("⚠ 未找到B站任务卡片,尝试直接打开");
  1327.         try {
  1328.             app.openApp(B站包名);
  1329.             print.log("✓ 已打开B站");
  1330.         } catch (e) {
  1331.             print.log("⚠ 打开B站失败:" + e.message);
  1332.         }
  1333.     }
  1334.     sleep.second(秒 = 3);

  1335.     xml = 获取UI();
  1336.    
  1337.     var startX = 屏幕宽 * 0.15 + 50;
  1338.     var startY = 屏幕高 * 0.42;
  1339.    
  1340.     var detects = opencv.findImagesEx('图色975253.cv');
  1341.     if (detects != null && detects.length > 0) {
  1342.         print.log("✓ 图色匹配找到滑块初始位置");
  1343.         print.log("图色匹配结果:" + detects[0]);
  1344.         var dStr = "" + detects[0];
  1345.         var m = dStr.match(/矩形区域:([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  1346.         if (m) {
  1347.             var left = parseFloat(m[1]);
  1348.             var top = parseFloat(m[2]);
  1349.             var right = parseFloat(m[3]);
  1350.             var bottom = parseFloat(m[4]);
  1351.             startX = (left + right) / 2 * 屏幕宽;
  1352.             startY = (top + bottom) / 2 * 屏幕高;
  1353.             print.log("✓ 滑块初始位置:(" + Math.round(startX) + "," + Math.round(startY) + ")");
  1354.         }
  1355.     } else {
  1356.         print.log("⚠ 图色匹配未找到滑块,使用坐标兜底");
  1357.     }

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

  1359.     var endX = startX + 拖动距离;
  1360.     var endY = startY;

  1361.     print.log("✓ 滑块起点: (" + Math.round(startX) + "," + Math.round(startY) + "), 拖动距离: " + 拖动距离);
  1362.     agent.swip(startX, startY, endX, endY, 2000);
  1363.     print.log("✓ 已拖动滑块,起点(" + Math.round(startX) + "," + Math.round(startY) + "),终点(" + Math.round(endX) + "," + Math.round(endY) + ")");
  1364. }

  1365. 主流程();

  1366. print.log("\n===== KIMI识别验证码拖动B站流程结束 =====");
复制代码


举报 回复