autox.js 使用开发案例:抖音私信截图上传自动化脚本开发


最近有个需求,为了提高工作效率,借助autox.js 来进行一个私信截图上传脚本小工具的开发。

首先先大概了解一下autox.js ,AutoX.js 是在 Auto.js 基础上进行了扩展和改进的自动化测试框架。它采用了 V8 引擎,提供了更高的性能和更多的功能。AutoX.js 支持使用 JavaScript 或 Kotlin 编写脚本,并提供了更多的 API 和工具,用于进行更复杂的自动化测试和任务自动化。它还提供了图像识别和模拟点击等功能,使得在 Android 平台上进行 UI 自动化更加方便和灵活。autox.js 免root 且可以用javaScript 来写,那真的是十分契合作为前端的我。

中文文档是:http://doc.autoxjs.com/#/

需求是:截图->上传云cos服务器->拿到返回地址->获取用户抖音号,昵称,检测提取谈话内容手机号->一起把数据发送到后台。

大概就是这么个流程。

autox.js 需要打开无障碍,悬浮框权限。

1. 首先需要渲染一个“截图”侧边按钮,同时需要获取截图权限

/**
 * 点击初始菜单页私信截图
 */
function showFloatControl3() {
  //获取截图权限
  if (!requestScreenCapture()) {
    toast("请求截图授权失败");
    return false;
  }
  closeFloatyWindow();
  threads.start(function () {
    window = floaty.window(
      <vertical>
        <button
          id="screenshot"
          margin="0"
          w="60"
          style="Widget.AppCompat.Button.Colored"
        >
          截图
        </button>
      </vertical>
    );
    let wx = window.getX();
    let wy = window.getY() + 200;
    window.setPosition(wx, wy);
    let x = 0;
    let y = 0;
    let X = 0;
    let Y = 0;
    let startX, startY;
    // 设置按钮的触摸事件
    window.screenshot.setOnTouchListener(function (view, event) {
      switch (event.getAction()) {
        case event.ACTION_DOWN:
          X = event.getRawX();
          Y = event.getRawY();
          startX = event.getX();
          startY = event.getY();
          return true;
        case event.ACTION_MOVE:
          x = event.getRawX() - X;
          y = event.getRawY() - Y;
          window.setPosition(wx + x, wy + y);

          return true;
        case event.ACTION_UP:
          let endX = event.getX();
          let endY = event.getY();
          let distance = Math.abs(endX - startX) + Math.abs(endY - startY);

          wx += x;
          wy += y;
          // 如果移动的距离小于 10, 则认为点击事件
          if (distance < 10) {
            renderScreenshotClick();
          }
          return false;
      }
      return true;
    });

    //当脚本结束时关闭悬浮按钮
    events.on("exit", function () {
      if (window) {
        window.close();
      }
    });
  });
}


2. 给侧边“截图”按钮绑定 点击时间,进行截图。

/**
 * 进行私信截图操作
 */
function renderScreenshotClick() {
  if (!isChatPage()) {
    alert("请转到私信页面截图");
    return;
  }
  if (!id("an=").exists()) {
    alert("客户没有说话");
    return;
  }
  var screenshot = captureScreen(); // 截取当前屏幕截图
  var saveDir = files.join(files.getSdcardPath(), "Pictures", "Screenshots"); // 构建保存目录
  files.createWithDirs(saveDir); // 创建保存目录(如果不存在)
  var timestamp = new Date().getTime(); // 获取当前时间戳
  var savePath = files.join(saveDir, "screenshot_" + timestamp + ".png");
  if (screenshot) {
    images.save(screenshot, savePath); // 保存截图到指定路径
    console.log("截图保存路径:", savePath);
    toast("截图保存路径:", savePath);
    uploadScreenshot(savePath);
  } else {
    alert("出现错误", "截屏失败,请检查是否开启权限", function () {
      global.robotStop();
      return;
    });
  }
}


3. 判断是否处于私信页

/**
 * 判断是否在私信页
 * @returns {Boolean} 是否在私信页
 */
function isChatPage() {
  var idList = ["xxx"]; //私信页标志性节点id集合
  var existsInIdList = false;
  for (var i = 0; i < idList.length; i++) {
    if (id(idList[i]).exists()) {
      existsInIdList = true;
      break;
    }
  }
  return existsInIdList;
}


4. 将截图上传到云cos服务器

/**
 * 上传截图到云 COS 服务器
 * @param path {String} 图片路径
 */
function uploadScreenshot(path) {
  threads.start(function () {
    try {
      var res = postFile("/upload/imagesToCos", {
        file: open(path),
      });
      var rbody = res.body.string();
      if (!isJSON(rbody)) {
        throw new Error("上传截图失败");
      }
      var uploadResJsonData = JSON.parse(rbody);
      var clientInfo = getProfileInfo(1); //获取客户的抖音号和昵称信息
      sleep(1000);
      var mineInfo = getProfileInfo(2); //获取自己的抖音号和昵称信息
      sleep(1000);
      var phone = phoneExtract(); //提取聊天内容手机号
      var params = {
        nickname: clientInfo.nickname,
        douyinId: clientInfo.douyinId,
        url: uploadResJsonData.url,
        orginName: mineInfo.nickname,
        mobile: phone,
      };
      if (
        !clientInfo.douyinId ||
        !mineInfo.nickname ||
        !uploadResJsonData.url
      ) {
        throw new Error("数据缺失无法上传");
      }
      setTimeout(function () {
        saveGuest(params);
      }, 0);
    } catch (error) {
      alert("出现错误", error.message, function () {
        global.robotStop();
        return;
      });
    }
  });
}


5. 根据节点标识,在个人主页,获取昵称+抖音号

/**
 * 获取个人主页昵称+抖音号
 * @param role {Number} 角色1 是客户; 角色2 是我
 * @return {Object} 返回对象 {昵称,抖音号}
 */
function getProfileInfo(role) {
  // 根据角色选择不同的控件ID
  var targetId = role === 1 ? "an=" : "an-";
  var targetElement = id(targetId).findOne();
  if (targetElement) {
    var isClickable = targetElement.clickable();
    console.log("控件的 clickable 属性为:" + isClickable);
    if (isClickable) {
      targetElement.click();
    } else {
      var bounds = targetElement.bounds(); // 获取目标控件的边界信息
      var centerX = bounds.centerX();
      var centerY = bounds.centerY();
      press(centerX, centerY, 10); // 模拟长按操作,持续时间为 1000ms 抖音只能用press,估计有点击时间控制现在,click()函数模拟连续点击时可能有点击速度过慢的问题
    }
  } else {
    alert("出现错误", "未找到目标控件", function () {
      global.robotStop();
      return;
    });
  }
  sleep(3000); // 等待个人主页加载完成
  // 查找私信用户的昵称和抖音号所在的元素text("");
  var nicknameElement = id("og0").findOnce();
  var douyinIdElement = id("yzh").findOnce();

  var nickname = nicknameElement ? nicknameElement.text() : "";
  var douyinId = douyinIdElement
    ? douyinIdElement.text().replace("抖音号:", "")
    : "";
  if (douyinId && nickname) {
    id("back_btn").click(); //返回私信
  } else {
    alert("出现错误", "获取抖音号,昵称数据异常,请手动上传数据", function () {
      global.robotStop();
      return;
    });
  }
  return { nickname, douyinId };
}


6.  提取他人谈话内容中的手机号,这里主要是考虑如何在内容节点集合中,过滤掉自己说话的内容,再从内容中提取手机号。采用了中线分割内容框,根据左右侧长度长短来判断,左侧长度比右侧长,则说明是对方的内容。

/**
 * 提取客户谈话内容手机号
 */
function phoneExtract() {
  var nodeList = id("content").find(); // content列表
  var phoneRegex = /(+d{1,2}s?)?(d{3,4}s?){2}d{4}/g; // 定义匹配手机号码的正则表达式

  for (var i = 0; i < nodeList.length; i++) {
    var node = nodeList[i];
    var content = node.desc(); // 获取节点的内容
    var bounds = node.bounds(); // 聊天节点的边界信息
    var deviceInfo = getDeviceInfo();
    var centerWidth = deviceInfo.width / 2; //屏幕中线

    if (centerWidth - bounds.left > bounds.right - centerWidth) {
      //屏幕中线,如果左侧长度 大于右侧长度,则说明是他人说话的内容
      var matches = content.match(phoneRegex); // 使用正则表达式匹配手机号码
      if (matches) {
        for (var j = 0; j < matches.length; j++) {
          var phoneNumber = matches[j].replace(/s/g, ""); // 去除空格
          console.log("提取到的手机号码:", phoneNumber);
          return phoneNumber; // 找到手机号码后立即返回
        }
      }
    }
  }

  return ""; // 找不到手机号码时返回空字符串
}


7. 最后进行私信信息入库

/**
 * 私信资料入库
 * @param params {Object} 需要提交入库的数据
 */
function saveGuest(params) {
  threads.start(function () {
    try {
      var param = {
        imgUrls: JSON.stringify([
          {
            url: params.url,
          },
        ]), // 截图
        appNickname: params.nickname, //抖音昵称
        appAccount: params.douyinId, //抖音号
        orginName: params.orginName, //本号昵称
        mobile: params.mobile,
      };
      console.log("入库参数", param);
      var res = post("/save", param);
      var rbody = res.body.string();
      console.log(rbody);
      if (!isJSON(rbody)) {
        throw new Error("入库失败");
      }
      var resJsonData = JSON.parse(rbody);
      toast(resJsonData.message);
    } catch (error) {
      alert("出现错误:" + error.message, function () {
        global.robotStop();
        return;
      });
    }
  });
}


以上就是主要代码及思想。

声明:BenBonBen博客|版权所有,违者必究|如未注明,均为原创

转载:转载请注明原文链接 - autox.js 使用开发案例:抖音私信截图上传自动化脚本开发


过去太迟,未来太远,当下最好