引言
2021年的时候分享了一个自动化脚本的文章,介绍了使用Tasker+Autojs实现自动化操作。现在公司更换了新的考勤软件,脚本也做了许多期迭代,所以更新了一版脚本分享的文章,并记录一些遇到的问题
准备工作
同这篇文章:
- 目标App换成北sen
- 电脑+VSCode
- 安卓模拟器推荐个资源网站雷电模拟器 v4.0.83 / v5.0.60 绿色版 / 雷电模拟器9 v9.0.50.0 绿色版-六音
- AutoX.js
- Tasker,中文站,原网站
脚本开发
开发调试的过程可以参照这篇文章
这里直接贴出脚本
/* * @Author: Hunter * @Date: 2023-07-10 17:46:35 * @LastEditTime: 2023-07-18 14:56:28 * @LastEditors: Hunter * @Description: * @FilePath: \北sen\main.js * 可以输入预定的版权声明、个性签名、空行等 */ var appName = "北森iTalent", //app名 packageName = getPackageName(appName), //包名 roundTimer = 60 * 1000, //超时定时器间隔60秒 randomTimer = parseInt(Math.random() * 5 * 60 * 1000), //随机定时器0-5分钟(精确到毫秒) maxRetryCount = 3, //重试打卡次数 useEmail = true, // 是否发送邮件 useDate = true, // 是否检查节假日 cardMenuBtn = () => id("mIVBottomCenter").findOne().bounds(), //打卡界面菜单 cardViewBtn = () => text("签到").findOne().bounds(), //打卡界面按钮 positionBtn = () => id("tv_sign_company_status").text("办公地点"), //定位成功按钮 cardTakeBtn = () => id("rlt_sign_click").findOne(); //打卡按钮 let logs = ``; // 日志记录 const __log = function () { logs += ` ${new Date()}:${JSON.stringify(arguments)} `; toast(JSON.stringify(arguments)); console.trace.apply(null, arguments); }; const getLog = () => logs; const mailApi = "https://api.emailjs.com/api/v1.0/email/send", //邮箱请求地址 mailConfig = { user_id: "user_xxxxxxxxxxxxxxxxxxxxxxhj", service_id: "sexxxxxxxmk", template_id: "templxxxxxxxxxxmn", accessToken: "8xxxxxxxxxxxxxxxxxxxxxxxxxxx9", template_params: { title: "自动打卡通知", content: `打卡成功 日志:${getLog()} `, email: "xxxxxxxxx@qq.com",// 接收消息的邮箱 }, }, //邮箱配置,需要去emailjs官网申请api,每月免费200次 dateApi = "http://api.tianapi.com/jiejiari/index", //节假日接口 dateConfig = { key: "9dxxxxxxxxxxxxxxxxxx93", date: formatDate(new Date()), }; //在天行数据申请节假日api(每天免费查询100次):https://www.tianapi.com/ __log("随机延迟时间:", randomTimer); if (useDate) { // 检查是否开启节假日检测 checkDateIsWork(dateConfig, function (res) { if (res.newslist[0].isnotwork) { sendEmail(setNewMessage("今天是法定节假日,无需打卡")); exitApp(true); return; } exitApp(false); setTimeout(init, randomTimer); }); } else { exitApp(false); setTimeout(init, randomTimer); } function init() { if (!!maxRetryCount) { __log("剩余重试次数" + maxRetryCount); timeOutMsg(); maxRetryCount--; startProgram(); return; } exitApp(true); } //开启应用 function startProgram() { __log("launchApp:" + appName, launchApp(appName)); //打开app waitForPackage(packageName); //等待app打开 __log("launchAppSuccess", packageName); openCardView(); } //首页--->打卡页 function openCardView() { var cardMenuButton = cardMenuBtn(); __log( "打卡界面菜单", click(cardMenuButton.centerX(), cardMenuButton.centerY()) ); var cardButton = cardViewBtn(); __log("进入打卡界面", click(cardButton.centerX(), cardButton.centerY() - 10)); takeCard(); } //打卡 function takeCard() { __log("等待定位"); positionBtn().waitFor(); __log("定位成功"); __log("点击打卡按钮", cardTakeBtn().click()); sendSuccEmail(); exitApp(true); } // 打卡成功发邮件 function sendSuccEmail() { const _mailConfig = mailConfig; _mailConfig.template_params.content += getLog(); __log("发送邮件", sendEmail(_mailConfig)); } //退出程序 function exitApp(exitJs, fn) { shell("am force-stop " + packageName, true); fn && fn(); exitJs && exit(); } // 程序超时处理 function timeOutMsg() { threads.start(function () { //在新线程执行的代码 setTimeout(function () { sendEmail(setNewMessage("自动打卡超时,正在重试")); exitApp(false, init); }, roundTimer); }); } // 发送邮件api function sendEmail(params) { if (useEmail) { var res = http.post(mailApi, params || mailConfig, { contentType: "application/json", }); return res; } } // 节假日api请求 function checkDateIsWork(params, fn) { var res = http.post(dateApi, params || dateConfig).body.json(); if (res.code === 200) { fn(res); return; } __log(res); sendEmail(setNewMessage(res.msg)); } // 修改默认邮件提示信息 function setNewMessage(msg) { var _mailConfig = simpleCloneObj(mailConfig, { contentType: "application/json", }); _mailConfig.template_params.content = `${msg + new Date()} 日志:${getLog()} `; return _mailConfig; } //日期格式转换 YYYY-MM-DD function formatDate(date) { var y = date.getFullYear(); var m = date.getMonth() + 1; m = m < 10 ? "0" + m : m; var d = date.getDate(); d = d < 10 ? "0" + d : d; return y + "-" + m + "-" + d; } // 简单的深复制 function simpleCloneObj(target) { return typeof target === "object" && JSON.parse(JSON.stringify(target)); }
邮件提示(不使用邮件推送的可以跳过这步)
在代码中可以配置邮件通知的选项,或者使用useEmail来控制是否发送邮件,此外还可以参照这篇文章,使用自己的邮件推送服务
这里以emailjs为例,每个月可以调用200次。
首先绑定自己的邮件服务
接着同样参照这篇文章,配置一下邮箱的选项用于邮件推送
然后是邮件模板的配置,代码中的template_params请求参数与模板配置对应
最后是emailjs的一些id
- service_id
- template_id
- user_id和accessToken
将这些配置项放在代码中就可以使用了
节假日判断(不需要判断节假日的可以跳过)
为了计算当前日期是否是节假日,我调用了一个天行的公共api,当然也可以通过将代码中的useDate设置为false关闭该功能
注册并实名后搜索节假日
点击开通,每天免费使用100次
问题及技巧归总
在上一版本脚本迭代中遇到了以下问题以及autoxjs中的一些使用技巧,供参考
JS语法错误:软件更新
旧版本的autojs或AutoXJS可能会提示语法错误,有可能是使用了过于超前的JS语法,建议更新app版本比如字符串模板 ` ${} ` ,const 等
按钮或组件无法找到
按钮无法找到的问题出现在北sen软件中,在*人薪事中可以使用id或者text的方式找到并点击组件,但是升级安卓高版本的系统后,组件的clickable为false,可能会出现找不到组件的问题,那就只能通过例如:text("签到").findOne().bounds() 的方式来获取组件的范围,然后通过类似:click(cardButton.centerX(), cardButton.centerY()) 的方式对屏幕进行动态定位点击事件,具体可以参考上面代码中的openCardView函数的两个点击事件
使用定时器等待组件出现
使用setinterval来轮询查询页面组件的clickable是否为true,由于有时使用官方的waitfor失效,所以想到了这个方式,这种方式虽然可以解决问题,不到万不得已不推荐使用,会导致性能差
root环境下才能用shell的root模式
模拟器中需要开启root,手机也需要root才能使用root模式执行sh
主线程堵塞问题
我在脚本后续迭代中加入了主线程超时处理,超过一分钟我就会重启脚本和软件,具体参考timeOutMsg函数
全局日志记录
好的程序必定离不开日志监控及问题定位排查,在__log函数中我封装了全局的日志处理,每步操作都会记录日志信息
巧用id或text
有许多组件没有id选项,所以就只能使用text或者parent等方式取获取组件
Tasker和AutoXjs自启问题
自启问题比较棘手,我使用tasker每天定时启动autojs防止脚本执行,那么如何保证tasker自启呢?使用autojs实现的,说起来很怪,有时会偶发autojs启动了但是却无法接受tasker发的系统广播,此时重启一下autojs就可以解决,具体脚本如下
// 自启tesker,防止开机被kill var appName = "Tasker", //app名 packageName = getPackageName(appName); //包名 startProgram(); //开启应用 function startProgram() { toast("launchApp:" + appName); console.log("launchApp:" + appName, launchApp(appName)); //打开app waitForPackage(packageName); //等待app打开 console.log("launchAppSuccess", packageName); toast("launchAppSuccess", packageName); exit(); }
效果展示
讲完了这么多,我们参考这个将脚本放在AutoXJS中演示一下
写在最后
本篇文章对以前的自动化脚本的迭代更新做了个梳理,有许多步骤在之前的文章中有,建议先过一遍,除此之外,文章总结了一些在脚本迭代过程中遇到的问题和解决技巧。其中涉及到的问题包括JS语法错误、按钮或组件无法找到、使用定时器等待组件出现、root环境下才能用shell的root模式以及主线程堵塞问题等。同时,文章提供了一些技巧,如巧用id或text获取组件、全局日志记录和Tasker与AutoX.js自启问题的解决方案。
注意:该脚本请勿用于商用,侵删
以上就是文章全部内容了,如果觉得文章不错的话,还请三连支持一下,谢谢!