最近有个需求,为了提高工作效率,借助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;
});
}
});
}以上就是主要代码及思想。




