示例小红书发图文代理模式不用开发者

380

主题

193

回帖

2397

积分

管理员

积分
2397
示例小红书发图文代理模式不用开发者

1.png

2.png

3.png

4.png

  1. // AIWROK 完整自动化流程:启动小红书 → 点击加号 → 选择相册 → 选图 → 自动发图片笔记
  2. // 全程使用 agent 方式操作,不依赖无障碍权限
  3. // 包名:com.xingin.xhs(小红书)

  4. print.log("===== 小红书自动发图片流程启动 =====");
  5. print.time();

  6. // ========== 配置区 ==========
  7. var APP包名 = "com.xingin.xhs";

  8. // 循环配置
  9. var 循环次数 = 3; // 设置循环次数,0表示无限循环
  10. var 当前循环 = 0;

  11. // 发布文案数组(可以设置多条不同文案)
  12. var 发布文案列表 = [
  13.     "分享今日份的美好~",
  14.     "生活需要仪式感 📷",
  15.     "记录每一个值得纪念的瞬间 ✨"
  16. ];
  17. var 文案索引 = 0;

  18. // 标题数组(可以设置多条不同标题)
  19. var 标题列表 = [
  20.     "今日分享",
  21.     "生活日常",
  22.     "美好瞬间"
  23. ];
  24. var 标题索引 = 0;

  25. // 要选择的图片数量
  26. var 选择图片数量 = 3;

  27. // ========== 获取设备信息 ==========
  28. var 屏幕宽 = screen.getScreenWidth();
  29. var 屏幕高 = screen.getScreenHeight();
  30. print.log("屏幕尺寸:" + 屏幕宽 + " x " + 屏幕高);
  31. print.log("代理状态:" + (agent.isOpen() ? "已开启" : "未开启"));

  32. // ========== 工具函数区 ==========

  33. /**
  34. * 获取当前界面的UI XML
  35. * @returns {string} UI XML内容
  36. */
  37. function 获取UI() {
  38.     return agent.shell("uiautomator dump --compressed /proc/self/fd/1 2>/dev/null");
  39. }

  40. /**
  41. * 通过属性查找节点信息
  42. * @param {string} xml UI XML内容
  43. * @param {object} 条件 查找条件(text, resourceId, className, contentDesc)
  44. * @returns {object|null} 节点信息(left, top, right, bottom, centerX, centerY)
  45. */
  46. function 查找节点(xml, 条件) {
  47.     var 模式 = "";
  48.     var 查询值 = "";
  49.    
  50.     if (条件.text) {
  51.         模式 = "text";
  52.         查询值 = 条件.text;
  53.     } else if (条件.resourceId) {
  54.         模式 = "resourceId";
  55.         查询值 = 条件.resourceId;
  56.     } else if (条件.className) {
  57.         模式 = "className";
  58.         查询值 = 条件.className;
  59.     } else if (条件.contentDesc) {
  60.         模式 = "contentDesc";
  61.         查询值 = 条件.contentDesc;
  62.     } else {
  63.         print.log("⚠ 查找条件不能为空");
  64.         return null;
  65.     }
  66.    
  67.     var 属性名 = {
  68.         "text": "text="",
  69.         "resourceId": "resource-id="",
  70.         "className": "class="",
  71.         "contentDesc": "content-desc=""
  72.     }[模式];
  73.    
  74.     var 索引 = xml.indexOf(属性名 + 查询值);
  75.     if (索引 === -1) {
  76.         return null;
  77.     }
  78.    
  79.     var 后面 = xml.substring(索引);
  80.     var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  81.     if (!m) {
  82.         return null;
  83.     }
  84.    
  85.     var left = parseInt(m[1]);
  86.     var top = parseInt(m[2]);
  87.     var right = parseInt(m[3]);
  88.     var bottom = parseInt(m[4]);
  89.    
  90.     return {
  91.         left: left,
  92.         top: top,
  93.         right: right,
  94.         bottom: bottom,
  95.         centerX: (left + right) / 2,
  96.         centerY: (top + bottom) / 2,
  97.         width: right - left,
  98.         height: bottom - top
  99.     };
  100. }

  101. /**
  102. * 通过节点信息点击
  103. * @param {object} 节点 节点信息对象
  104. * @param {string} 描述 节点描述(用于日志)
  105. */
  106. function 点击节点信息(节点, 描述) {
  107.     agent.click(节点.centerX, 节点.centerY);
  108.     print.log("✓ 点击【" + 描述 + "】成功 (" + 节点.centerX + ", " + 节点.centerY + ")");
  109. }

  110. /**
  111. * 通过文本查找并点击节点
  112. * @param {string} 文本 要查找的节点文本
  113. * @param {number} 超时秒 超时时间(秒)
  114. * @returns {boolean} 是否找到并点击成功
  115. */
  116. function 点击节点(文本, 超时秒) {
  117.     var 开始时间 = Date.now();
  118.     var 超时毫秒 = 超时秒 * 1000;
  119.    
  120.     while (Date.now() - 开始时间 < 超时毫秒) {
  121.         try {
  122.             var xml = 获取UI();
  123.             var 节点 = 查找节点(xml, { text: 文本 });
  124.             if (节点) {
  125.                 点击节点信息(节点, 文本);
  126.                 return true;
  127.             }
  128.         } catch (e) {
  129.             print.log("⚠ 查找节点出错:" + e.message);
  130.         }
  131.         sleep.second(秒 = 1);
  132.     }
  133.     print.log("✗ 未找到节点【" + 文本 + "】,超时 " + 超时秒 + " 秒");
  134.     return false;
  135. }

  136. /**
  137. * 通过resourceId查找并点击节点
  138. * @param {string} resourceId 资源ID
  139. * @param {number} 超时秒 超时时间(秒)
  140. * @returns {boolean} 是否找到并点击成功
  141. */
  142. function 点击节点ById(resourceId, 超时秒) {
  143.     var 开始时间 = Date.now();
  144.     var 超时毫秒 = 超时秒 * 1000;
  145.    
  146.     while (Date.now() - 开始时间 < 超时毫秒) {
  147.         try {
  148.             var xml = 获取UI();
  149.             var 节点 = 查找节点(xml, { resourceId: resourceId });
  150.             if (节点) {
  151.                 点击节点信息(节点, "resourceId:" + resourceId);
  152.                 return true;
  153.             }
  154.         } catch (e) {
  155.             print.log("⚠ 通过ID查找节点出错:" + e.message);
  156.         }
  157.         sleep.second(秒 = 1);
  158.     }
  159.     print.log("✗ 未找到节点【resourceId:" + resourceId + "】,超时 " + 超时秒 + " 秒");
  160.     return false;
  161. }

  162. /**
  163. * 通过className查找并点击节点
  164. * @param {string} className 类名
  165. * @param {number} 超时秒 超时时间(秒)
  166. * @param {number} 索引 第几个匹配节点(默认0,第一个)
  167. * @returns {boolean} 是否找到并点击成功
  168. */
  169. function 点击节点ByClass(className, 超时秒, 索引) {
  170.     索引 = 索引 || 0;
  171.     var 开始时间 = Date.now();
  172.     var 超时毫秒 = 超时秒 * 1000;
  173.    
  174.     while (Date.now() - 开始时间 < 超时毫秒) {
  175.         try {
  176.             var xml = 获取UI();
  177.             var 节点列表 = 查找所有节点ByClass(xml, className);
  178.             if (节点列表.length > 索引) {
  179.                 var 节点 = 节点列表[索引];
  180.                 点击节点信息(节点, "className:" + className + "[" + 索引 + "]");
  181.                 return true;
  182.             }
  183.         } catch (e) {
  184.             print.log("⚠ 通过Class查找节点出错:" + e.message);
  185.         }
  186.         sleep.second(秒 = 1);
  187.     }
  188.     print.log("✗ 未找到节点【className:" + className + "[" + 索引 + "]】,超时 " + 超时秒 + " 秒");
  189.     return false;
  190. }

  191. /**
  192. * 通过className查找所有匹配节点
  193. * @param {string} xml UI XML内容
  194. * @param {string} className 类名
  195. * @returns {array} 节点信息数组
  196. */
  197. function 查找所有节点ByClass(xml, className) {
  198.     var 结果 = [];
  199.     var 属性名 = "class="";
  200.     var 查找值 = 属性名 + className;
  201.     var 位置 = 0;
  202.    
  203.     while (true) {
  204.         var 索引 = xml.indexOf(查找值, 位置);
  205.         if (索引 === -1) break;
  206.         
  207.         var 后面 = xml.substring(索引);
  208.         var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  209.         if (m) {
  210.             var left = parseInt(m[1]);
  211.             var top = parseInt(m[2]);
  212.             var right = parseInt(m[3]);
  213.             var bottom = parseInt(m[4]);
  214.             
  215.             结果.push({
  216.                 left: left,
  217.                 top: top,
  218.                 right: right,
  219.                 bottom: bottom,
  220.                 centerX: (left + right) / 2,
  221.                 centerY: (top + bottom) / 2
  222.             });
  223.         }
  224.         
  225.         位置 = 索引 + 查找值.length;
  226.     }
  227.    
  228.     return 结果;
  229. }

  230. /**
  231. * 通过hint文本查找输入框并点击
  232. * @param {string} hint 提示文本
  233. * @param {number} 超时秒 超时时间(秒)
  234. * @returns {boolean} 是否找到并点击成功
  235. */
  236. function 点击输入框(hint, 超时秒) {
  237.     var 开始时间 = Date.now();
  238.     var 超时毫秒 = 超时秒 * 1000;
  239.    
  240.     var 备选hint列表 = [hint];
  241.     if (hint === "标题") {
  242.         备选hint列表 = ["标题", "输入标题", "请输入标题", "写标题"];
  243.     } else if (hint === "正文") {
  244.         备选hint列表 = ["正文", "输入正文", "请输入正文", "写正文", "分享文字"];
  245.     }
  246.    
  247.     while (Date.now() - 开始时间 < 超时毫秒) {
  248.         try {
  249.             var xml = 获取UI();
  250.             
  251.             for (var i = 0; i < 备选hint列表.length; i++) {
  252.                 var 当前hint = 备选hint列表[i];
  253.                 var 索引 = xml.indexOf('hint="' + 当前hint);
  254.                 if (索引 !== -1) {
  255.                     var 后面 = xml.substring(索引);
  256.                     var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  257.                     if (m) {
  258.                         var cx = (parseInt(m[1]) + parseInt(m[3])) / 2;
  259.                         var cy = (parseInt(m[2]) + parseInt(m[4])) / 2;
  260.                         agent.click(cx, cy);
  261.                         print.log("✓ 点击输入框【" + 当前hint + "】成功 (" + cx + ", " + cy + ")");
  262.                         return true;
  263.                     }
  264.                 }
  265.             }
  266.             
  267.             var editTextIndex = xml.indexOf('class="android.widget.EditText"');
  268.             if (editTextIndex !== -1) {
  269.                 var 后面 = xml.substring(editTextIndex);
  270.                 var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  271.                 if (m) {
  272.                     var cx = (parseInt(m[1]) + parseInt(m[3])) / 2;
  273.                     var cy = (parseInt(m[2]) + parseInt(m[4])) / 2;
  274.                     agent.click(cx, cy);
  275.                     print.log("✓ 通过EditText类点击输入框成功 (" + cx + ", " + cy + ")");
  276.                     return true;
  277.                 }
  278.             }
  279.         } catch (e) {
  280.             print.log("⚠ 查找输入框出错:" + e.message);
  281.         }
  282.         sleep.second(秒 = 1);
  283.     }
  284.    
  285.     print.log("✗ 未找到输入框【" + hint + "】,超时 " + 超时秒 + " 秒");
  286.     print.log("当前UI片段(供调试):" + 获取UI().substring(0, 3000));
  287.     return false;
  288. }

  289. /**
  290. * 通过剪贴板 + 粘贴向当前输入框输入文字
  291. * @param {string} 文本 待输入内容
  292. */
  293. function 输入文字(文本) {
  294.     // 设置剪贴板(双保险)
  295.     agent.shell("service call clipboard 4 i32 1 s16 '" + 文本 + "' i32 0");
  296.     sleep.millisecond(毫秒 = 300);
  297.     agent.shell("service call clipboard 3 i32 1 s16 '" + 文本 + "' i32 0");
  298.     sleep.millisecond(毫秒 = 300);
  299.     Clipboard.copy(文本);
  300.     sleep.millisecond(毫秒 = 300);

  301.     var 粘贴成功 = false;

  302.     // 方式A:KEYCODE_PASTE (279)
  303.     agent.shell("input keyevent 279");
  304.     sleep.second(秒 = 1);
  305.     var xmlA = 获取UI();
  306.     if (xmlA.indexOf(文本.substring(0, 2)) !== -1) {
  307.         粘贴成功 = true;
  308.         print.log("✓ KEYCODE_PASTE 粘贴成功");
  309.     }

  310.     // 方式B:Ctrl+V
  311.     if (!粘贴成功) {
  312.         agent.shell("input keyevent 50 --meta 0x1000");
  313.         sleep.second(秒 = 1);
  314.         var xmlB = 获取UI();
  315.         if (xmlB.indexOf(文本.substring(0, 2)) !== -1) {
  316.             粘贴成功 = true;
  317.             print.log("✓ Ctrl+V 粘贴成功");
  318.         }
  319.     }

  320.     // 方式C:长按 + 查找粘贴按钮
  321.     if (!粘贴成功) {
  322.         var 输入框中心 = 获取输入框中心();
  323.         if (输入框中心) {
  324.             var 长按命令 = "input swipe " + 输入框中心.x + " " + 输入框中心.y + " " + (输入框中心.x + 1) + " " + (输入框中心.y + 1) + " 2000";
  325.             agent.shell(长按命令);
  326.             sleep.second(秒 = 2);

  327.             var xmlC = 获取UI();
  328.             var 关键词 = ["粘贴", "Paste", "paste"];
  329.             for (var i = 0; i < 关键词.length; i++) {
  330.                 if (xmlC.indexOf(关键词[i]) !== -1) {
  331.                     var 后面 = xmlC.substring(xmlC.indexOf(关键词[i]));
  332.                     var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  333.                     if (m) {
  334.                         var cx = (parseInt(m[1]) + parseInt(m[3])) / 2;
  335.                         var cy = (parseInt(m[2]) + parseInt(m[4])) / 2;
  336.                         agent.click(cx, cy);
  337.                         粘贴成功 = true;
  338.                         print.log("✓ 长按粘贴成功");
  339.                         break;
  340.                     }
  341.                 }
  342.             }
  343.         }
  344.     }

  345.     if (!粘贴成功) {
  346.         print.log("✗ 所有粘贴方式均未成功");
  347.     }
  348.     return 粘贴成功;
  349. }

  350. /**
  351. * 获取当前输入框的中心坐标
  352. * @returns {object|null} {x, y} 坐标对象
  353. */
  354. function 获取输入框中心() {
  355.     try {
  356.         var xml = 获取UI();
  357.         
  358.         var editTextIndex = xml.indexOf('class="android.widget.EditText"');
  359.         if (editTextIndex !== -1) {
  360.             var 后面 = xml.substring(editTextIndex);
  361.             var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  362.             if (m) {
  363.                 return {
  364.                     x: (parseInt(m[1]) + parseInt(m[3])) / 2,
  365.                     y: (parseInt(m[2]) + parseInt(m[4])) / 2
  366.                 };
  367.             }
  368.         }
  369.         
  370.         var inputIndex = xml.indexOf('class="android.widget.TextView"');
  371.         while (inputIndex !== -1) {
  372.             var 后面 = xml.substring(inputIndex);
  373.             var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  374.             if (m) {
  375.                 var left = parseInt(m[1]);
  376.                 var top = parseInt(m[2]);
  377.                 var right = parseInt(m[3]);
  378.                 var bottom = parseInt(m[4]);
  379.                
  380.                 if (right - left > 100 && bottom - top > 30) {
  381.                     return {
  382.                         x: (left + right) / 2,
  383.                         y: (top + bottom) / 2
  384.                     };
  385.                 }
  386.             }
  387.             inputIndex = xml.indexOf('class="android.widget.TextView"', inputIndex + 30);
  388.         }
  389.         
  390.         return { x: 屏幕宽 / 2, y: 屏幕高 / 2 };
  391.     } catch (e) {
  392.         return { x: 屏幕宽 / 2, y: 屏幕高 / 2 };
  393.     }
  394. }

  395. /**
  396. * 在相册网格中按顺序选择指定数量的图片(节点查找方式)
  397. * @param {number} 数量 要选择的图片数
  398. */
  399. function 选择相册图片(数量) {
  400.     print.log("准备从相册选择 " + 数量 + " 张图片");
  401.     var 已选 = 0;
  402.     var 开始时间 = Date.now();
  403.    
  404.     while (已选 < 数量 && Date.now() - 开始时间 < 30000) {
  405.         try {
  406.             var xml = 获取UI();
  407.             
  408.             var 选择框列表 = [];
  409.             var classIndex = xml.indexOf('class="android.widget.FrameLayout"');
  410.             while (classIndex !== -1) {
  411.                 var 后面 = xml.substring(classIndex);
  412.                 var boundsM = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  413.                 if (boundsM) {
  414.                     var left = parseInt(boundsM[1]);
  415.                     var top = parseInt(boundsM[2]);
  416.                     var right = parseInt(boundsM[3]);
  417.                     var bottom = parseInt(boundsM[4]);
  418.                     var width = right - left;
  419.                     var height = bottom - top;
  420.                     
  421.                     if (width > 50 && height > 50 && top > 600 && width < 400 && height < 400) {
  422.                         选择框列表.push({
  423.                             centerX: (left + right) / 2,
  424.                             centerY: (top + bottom) / 2,
  425.                             left: left,
  426.                             top: top
  427.                         });
  428.                     }
  429.                 }
  430.                 classIndex = xml.indexOf('class="android.widget.FrameLayout"', classIndex + 35);
  431.             }
  432.             
  433.             选择框列表.sort(function(a, b) {
  434.                 if (Math.abs(a.top - b.top) < 50) {
  435.                     return a.left - b.left;
  436.                 }
  437.                 return a.top - b.top;
  438.             });
  439.             
  440.             print.log("找到 " + 选择框列表.length + " 个图片选择框");
  441.             
  442.             if (选择框列表.length > 已选) {
  443.                 var 目标节点 = 选择框列表[已选];
  444.                 agent.click(目标节点.centerX, 目标节点.centerY);
  445.                 print.log("✓ 已选中第 " + (已选 + 1) + " 张图片 (" + 目标节点.centerX + ", " + 目标节点.centerY + ")");
  446.                 已选++;
  447.                 sleep.millisecond(毫秒 = 800);
  448.             } else {
  449.                 print.log("⚠ 选择框不足,等待加载...");
  450.                 sleep.second(秒 = 2);
  451.             }
  452.         } catch (e) {
  453.             print.log("⚠ 选择第 " + (已选 + 1) + " 张图片失败:" + e.message);
  454.             sleep.second(秒 = 1);
  455.         }
  456.     }
  457.    
  458.     print.log("共选中 " + 已选 + " 张图片");
  459.     return 已选;
  460. }

  461. // ========================================
  462. // 主循环开始
  463. // ========================================
  464. for (当前循环 = 1; 循环次数 == 0 || 当前循环 <= 循环次数; 当前循环++) {
  465.     if (循环次数 > 0) {
  466.         print.log("\n======================================");
  467.         print.log("===== 第 " + 当前循环 + " / " + 循环次数 + " 次循环 =====");
  468.         print.log("======================================");
  469.     } else {
  470.         print.log("\n======================================");
  471.         print.log("===== 第 " + 当前循环 + " 次循环(无限模式)=====");
  472.         print.log("======================================");
  473.     }

  474.     // 获取当前要发布的文案与标题
  475.     var 发布文案 = 发布文案列表[文案索引 % 发布文案列表.length];
  476.     var 发布标题 = 标题列表[标题索引 % 标题列表.length];
  477.     文案索引++;
  478.     标题索引++;

  479.     print.log("本次标题:" + 发布标题);
  480.     print.log("本文案:" + 发布文案);

  481.     // ========================================
  482.     // 步骤1:启动小红书
  483.     // ========================================
  484.     print.log("\n========== 步骤1:启动小红书 ==========");
  485.     try {
  486.         app.openApp(APP包名);
  487.         print.log("✓ 已启动小红书");
  488.     } catch (e) {
  489.         print.log("启动失败:" + e.message);
  490.     }
  491.     sleep.second(秒 = 5);

  492.     // ========================================
  493.     // 步骤2:点击底部加号按钮(节点查找)
  494.     // ========================================
  495.     print.log("\n========== 步骤2:点击加号按钮 ==========");
  496.     var 加号点击成功 = false;
  497.    
  498.     var 开始时间 = Date.now();
  499.     while (Date.now() - 开始时间 < 15000 && !加号点击成功) {
  500.         try {
  501.             var xml = 获取UI();
  502.             
  503.             var 加号节点 = null;
  504.             
  505.             // 方式1:通过content-desc查找加号按钮
  506.             var contentDesc索引 = xml.indexOf('content-desc="');
  507.             while (contentDesc索引 !== -1) {
  508.                 var 后面 = xml.substring(contentDesc索引);
  509.                 var m = 后面.match(/content-desc="([^"]*)"/);
  510.                 if (m && (m[1] === "发布" || m[1] === "加号" || m[1] === "+")) {
  511.                     var boundsM = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  512.                     if (boundsM) {
  513.                         加号节点 = {
  514.                             centerX: (parseInt(boundsM[1]) + parseInt(boundsM[3])) / 2,
  515.                             centerY: (parseInt(boundsM[2]) + parseInt(boundsM[4])) / 2
  516.                         };
  517.                         break;
  518.                     }
  519.                 }
  520.                 contentDesc索引 = xml.indexOf('content-desc="', contentDesc索引 + 14);
  521.             }
  522.             
  523.             // 方式2:查找底部导航栏中最中间的按钮
  524.             if (!加号节点) {
  525.                 var 导航按钮列表 = [];
  526.                 var classIndex = xml.indexOf('class="android.widget.ImageView"');
  527.                 while (classIndex !== -1) {
  528.                     var 后面 = xml.substring(classIndex);
  529.                     var boundsM = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  530.                     if (boundsM) {
  531.                         var top = parseInt(boundsM[2]);
  532.                         if (top > 屏幕高 * 0.85) {
  533.                             导航按钮列表.push({
  534.                                 centerX: (parseInt(boundsM[1]) + parseInt(boundsM[3])) / 2,
  535.                                 centerY: (parseInt(boundsM[2]) + parseInt(boundsM[4])) / 2
  536.                             });
  537.                         }
  538.                     }
  539.                     classIndex = xml.indexOf('class="android.widget.ImageView"', classIndex + 32);
  540.                 }
  541.                
  542.                 if (导航按钮列表.length >= 3) {
  543.                     加号节点 = 导航按钮列表[Math.floor(导航按钮列表.length / 2)];
  544.                 }
  545.             }
  546.             
  547.             if (加号节点) {
  548.                 agent.click(加号节点.centerX, 加号节点.centerY);
  549.                 print.log("✓ 通过节点查找点击加号按钮成功 (" + 加号节点.centerX + ", " + 加号节点.centerY + ")");
  550.                 sleep.second(秒 = 2);
  551.                
  552.                 var xml2 = 获取UI();
  553.                 if (xml2.indexOf("从相册选择") !== -1 || xml2.indexOf("相册") !== -1) {
  554.                     加号点击成功 = true;
  555.                     print.log("✓ 检测到发布弹窗,加号点击成功");
  556.                 } else {
  557.                     print.log("⚠ 未检测到弹窗,重试...");
  558.                 }
  559.             } else {
  560.                 print.log("⚠ 未找到加号按钮节点,等待重试...");
  561.                 sleep.second(秒 = 1);
  562.             }
  563.         } catch (e) {
  564.             print.log("⚠ 点击加号失败:" + e.message);
  565.             sleep.second(秒 = 1);
  566.         }
  567.     }
  568.    
  569.     if (!加号点击成功) {
  570.         print.log("✗ 加号按钮点击失败,dump当前UI供调试:");
  571.         var debugXml = 获取UI();
  572.         print.log("UI内容片段:" + debugXml.substring(0, 2000));
  573.     }

  574.     // ========================================
  575.     // 步骤3:选择"从相册选择"选项(节点查找)
  576.     // ========================================
  577.     print.log("\n========== 步骤3:选择相册选项 ==========");
  578.     var 相册成功 = 点击节点("从相册选择", 10);
  579.     if (!相册成功) {
  580.         点击节点("相册", 10);
  581.     }
  582.     sleep.second(秒 = 3);

  583.     // ========================================
  584.     // 步骤4:从相册选择图片(节点查找)
  585.     // ========================================
  586.     print.log("\n========== 步骤4:从相册选择图片 ==========");
  587.     try {
  588.         选择相册图片(选择图片数量);
  589.     } catch (e) {
  590.         print.log("⚠ 选择图片失败:" + e.message);
  591.     }
  592.     sleep.second(秒 = 1);

  593.     // ========================================
  594.     // 步骤5:点击"下一步"(节点查找)
  595.     // ========================================
  596.     print.log("\n========== 步骤5:点击下一步 ==========");
  597.     点击节点("下一步", 10);
  598.     sleep.second(秒 = 5);

  599.     // ========================================
  600.     // 步骤6:点击图片编辑页的"下一步"(节点查找)
  601.     // ========================================
  602.     print.log("\n========== 步骤6:点击编辑页下一步 ==========");
  603.     点击节点("下一步", 10);
  604.     sleep.second(秒 = 3);

  605.     // ========================================
  606.     // 步骤7:输入标题(节点查找输入框)
  607.     // ========================================
  608.     print.log("\n========== 步骤7:输入标题 ==========");
  609.     print.log("准备输入标题:" + 发布标题);
  610.    
  611.     点击输入框("标题", 15);
  612.     sleep.second(秒 = 1);
  613.     输入文字(发布标题);
  614.     sleep.second(秒 = 2);

  615.     // ========================================
  616.     // 步骤8:输入正文文案(节点查找输入框)
  617.     // ========================================
  618.     print.log("\n========== 步骤8:输入正文文案 ==========");
  619.     print.log("准备输入正文:" + 发布文案);
  620.    
  621.     var 正文输入成功 = false;
  622.    
  623.     var xml = 获取UI();
  624.     var editTextIndex = xml.indexOf('class="android.widget.EditText"');
  625.     var editTextCount = 0;
  626.    
  627.     while (editTextIndex !== -1) {
  628.         var 后面 = xml.substring(editTextIndex);
  629.         var m = 后面.match(/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/);
  630.         if (m) {
  631.             var left = parseInt(m[1]);
  632.             var top = parseInt(m[2]);
  633.             var right = parseInt(m[3]);
  634.             var bottom = parseInt(m[4]);
  635.             var width = right - left;
  636.             var height = bottom - top;
  637.             
  638.             if (editTextCount === 1) {
  639.                 var cx = (left + right) / 2;
  640.                 var cy = (parseInt(m[2]) + parseInt(m[4])) / 2;
  641.                 agent.click(cx, cy);
  642.                 print.log("✓ 点击第2个EditText作为正文输入框成功 (" + cx + ", " + cy + ")");
  643.                 正文输入成功 = true;
  644.                 break;
  645.             }
  646.             editTextCount++;
  647.         }
  648.         editTextIndex = xml.indexOf('class="android.widget.EditText"', editTextIndex + 32);
  649.     }
  650.    
  651.     if (!正文输入成功) {
  652.         print.log("⚠ 尝试通过hint查找正文输入框...");
  653.         正文输入成功 = 点击输入框("正文", 15);
  654.     }
  655.    
  656.     sleep.second(秒 = 1);
  657.     输入文字(发布文案);
  658.     sleep.second(秒 = 2);

  659.     // ========================================
  660.     // 步骤9:点击"发布笔记"(节点查找)
  661.     // ========================================
  662.     print.log("\n========== 步骤9:点击发布笔记 ==========");
  663.     点击节点("发布", 10);
  664.     sleep.second(秒 = 3);

  665.     // ========================================
  666.     // 循环间隔(最后一次不等待)
  667.     // ========================================
  668.     if (循环次数 == 0 || 当前循环 < 循环次数) {
  669.         print.log("\n========== 等待下一次循环 ==========");
  670.         sleep.second(秒 = 5);
  671.     }
  672. }

  673. // ========================================
  674. // 结果汇总
  675. // ========================================
  676. print.timeEnd();
  677. print.log("\n======================================");
  678. print.log("===== 所有循环执行完成 =====");
  679. if (循环次数 > 0) {
  680.     print.log("总计执行:" + 循环次数 + " 次");
  681. } else {
  682.     print.log("无限循环模式,已执行:" + (当前循环 - 1) + " 次");
  683. }
  684. print.log("======================================");
复制代码


举报 回复