Autojs实战案例
一、概述:
记录封装常用autojs语法及案例,方便日后急速上手使用,高手请绕路!
二、基础案例
1.上传图片剪切并返回图片
- 由于我的autojs的images.clip方法在使用是报错,所以换种方式剪切图片;
- 方式:通过接口处理图片,后端接口服务为python+flask
服务端:
#!/usr/bin/env python # encoding: utf-8 import flask from PIL import Image from io import BytesIO from werkzeug.utils import secure_filename from flask import request, send_from_directory,jsonify server = flask.Flask(__name__)#创建Flask对象server # post方法:上传文件的 @server.route('/upload', methods=['post']) def upload(): imgData = request.files.get('file')#文件流 cc=request.values.get('chen') dd=eval(cc) if imgData and dd: # chen = secure_filename(imgData.filename)#获取文件名 # imgData.save(chen)#保存图片 # imgData.seek(0)#重新定义指针到文件开头 ###读取文件流bytes chen = request.files["file"].stream.read() #把bytes数组还原成图片 img = Image.open(BytesIO(chen)) print('图片尺寸:',img.size) #指定区域剪切图片 cropped = img.crop((dd)) # (x1, y1, x2, y2) cropped.save(r"./test.png")#保存图片 return send_from_directory(r'./','test.png', as_attachment=True) #返回要下载的文件内容给客户端 else: return jsonify({"msg": "false", "ret": 2,"code": "fill","text":"图片处理异常或没有传坐标"}) if __name__ == '__main__': server.run(port=8000, debug=True, host='0.0.0.0')
autojs端:
device = device.serial; //________________________________________截图__________________________________________ if (!requestScreenCapture()) { console.show() toastLog("请求截图失败"); exit(); } else { toastLog("请求截图成功"); } //截图 captureScreen("/sdcard/"+device+'.png'); log('开始请求接口-区域处理图片'); sleep(2000); // ////____________________上传截图并保存文件__________________________________________________ var url="192.168.10.32:8000/upload" //打开图片并请求 var res = http.postMultipart(url, { file: open("/sdcard/"+device+".png"),//图片 chen: "10,920,1050,1500"//坐标x1,y1,x2,y2 }); //判断状态码 if(res.statusCode >= 200 && res.statusCode < 300){ //保存文件 files.writeBytes('/sdcard/'+device+'.png',res.body.bytes()); //打开文件 // app.viewFile('/sdcard/'+device+'.png'); } else{ log('图片区域处理错误') }
2.关闭app应用
////______________________________________________关闭app应用______________________________________________________ auto(); /***** app应用关闭 *****/ function killapk(androidname) { var packageName = getPackageName(androidname);//通过app名称获取包名 app.openAppSetting(packageName);//通过包名打开应用的详情页(设置页) sleep(random(1000, 2000)); text(app.getAppName(packageName)).waitFor(); //通过包名获取已安装的应用名称,判断是否已经跳转至该app的应用设置界面 let is_sure = textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/).findOne();//正则方式匹配包含“强”,“停”,“结”,“行”的控件 if (is_sure.enabled()) { textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/).findOne().click(); sleep(1000); console.log("进入设置页面成功,准备kill应用!"); text("强行停止").findOne().click(); log(app.getAppName(packageName) + "应用已被关闭"); sleep(random(1000, 2000)); back(); } else { log(app.getAppName(packageName) + "应用不能被正常关闭可能不在后台运行或没有运行"); back(); sleep(random(1000, 2000)); } } killapk("抖音"); ////____________________________________________关闭应用进程__________________________________________________ auto(); function kill_app(packageName) { var name = getPackageName(packageName); if(!name){ if(getAppName(packageName)){ name = packageName; }else{ return false; } } app.openAppSetting(name); text(app.getAppName(name)).waitFor(); let is_sure = text("强行停止").findOne(); if (is_sure.enabled()) { click('强行停止'); sleep(700); //确定强行停止 text("强行停止").findOne().click(); log(app.getAppName(name) + "应用已被关闭"); sleep(1000); back(); } else { log(app.getAppName(name) + "应用不能被正常关闭或不在后台运行"); back(); } } kill_app('com.tencent.mobileqq') ////_________________________________________通过最近任务关闭进程(不同设备需要微调)__________________________________________________ "auto"; // 封装请求截图权限并自动点击“允许”按钮的函数 function requestScreenCaptureWithAutoClick() { // 创建线程,用于自动点击“允许”按钮 threads.start(function() { sleep(1000); // 等待1秒,确保权限框已经弹出 var allowButton = classNameContains("Button").textContains("允许").findOne(2000); // 查找“允许”按钮 if (allowButton) { allowButton.click(); // 点击“允许”按钮 log("已自动点击允许"); } else { toast("未找到允许按钮,请手动点击"); } }); // 请求截图权限 if (!requestScreenCapture()) { toast("请求截图失败"); exit(); } // 继续执行脚本 log(">>>>截图权限已开启,继续执行>>>>"); sleep(1000); } // 封装查找图片并点击的函数 function findAndClickImage(imagePath,threshold) { // 加载目标图片 var targetImage = images.read(imagePath); if (!targetImage) { toast("目标图片加载失败,请检查路径"); exit(); } // 截取当前屏幕 var screenshot = captureScreen(); //当阀值没传时、默认取0.8 if (threshold === undefined) { threshold = 0.8; // 默认值 } // 查找目标图片 log("图片阀值:",threshold); var point = images.findImage(screenshot, targetImage, { threshold: threshold // 设置匹配精度,根据实际情况调整 }); if (point) { // 获取目标图片的中心坐标 var x = point.x + targetImage.width / 2; var y = point.y + targetImage.height / 2; // 点击目标图片的中心 swipe(x, y, x,y*0.1,200); toastLog("滑动坐标为"+x+","+y+","+x+","+(y*0.1).toFixed(2)); //toast("滑动坐标为"+x+","+y+","+x+","+(y*0.1).toFixed(2)); // 释放图片资源 targetImage.recycle(); screenshot.recycle(); return true; } else { targetImage.recycle(); screenshot.recycle(); toast("未找到目标图片"); return false; } } /** * 通过最近任务关闭应用(无需root) * @param {string} packageName 目标应用的包名(如 "com.tencent.mm") * @param {string} package_img 目标应用对应的图片 * @param {int} similarity 目标应用对应的图片阈值 * @returns {boolean} 是否成功关闭 */ function killAppWithoutRoot(packageName,package_img,similarity) { if (!packageName) { console.error("请提供目标应用的包名"); return false; } try { // 1. 打开指定的应用(用于进程排列) launch(packageName); //通过包名启动应用 sleep(500); // 2. 打开“最近任务”界面 home(); // 返回桌面 sleep(500); recents(); // 打开最近任务 sleep(1000); // 检查是否在“最近任务”界面(不同设备可能不同) if (currentPackage() === "com.huawei.android.launcher" || currentPackage() === "com.android.launcher3") { console.log("当前在[最近任务]管理页面!"); // 查找目标应用(通过包名或应用名) let appName = getAppName(packageName); let targetWidget = null; // 方式1:通过应用名查找(推荐) if (appName) { console.log("通过应用名查找(推荐):",appName); targetWidget = text(appName).findOne(800); } // 方式2:通过包名查找(可能需要OCR或图像识别) if (!targetWidget) { console.log("通过应用名称找不到元素、走图片识别处理!"); // 通过匹配目标图片 requestScreenCaptureWithAutoClick(); //// 查找并点击目标图片(图片太小好像不行) var img = findAndClickImage(package_img,similarity); if (img==false){ console.log("图片识别为false!"); sleep(800); return false; }else{ console.log("图片识别为true, 已关闭应用:",packageName); sleep(1000); return true; } } else if (targetWidget) { console.log("通过应用名称找到元素,获取元素坐标并操作!"); let bounds = targetWidget.bounds(); let centerX = bounds.centerX(); let centerY = bounds.centerY(); console.log(centerX, (device.height * 0.78).toFixed(2), centerX, centerY , 500); // 滑动关闭(从应用位置向上滑动) swipe(centerX, (device.height * 0.78).toFixed(2), centerX, centerY , 500); console.log("已关闭应用:", packageName); sleep(1000); return true; } } else { console.log("当前不在[最近任务]管理页面,请仔细排查!"); recents(); sleep(1000); return false; } } catch (e) { console.error("关闭应用时出错:", e); return false; } } //关闭指定应用的进程(目前只对华为mate9做过测试、不同手机需要微调),包名、图片、阈值都是必传 var result = killAppWithoutRoot("com.tencent.mtt","/sdcard/Pictures/Screenshots/QQ_process.jpg",0.6); console.error("返回结果:", result); ////_________________________________________通过最近任务关闭进程(不带图片识别)________________________________________________ "auto"; /***** 关闭指定任务进程 *****/ function close_recent_process(packageName){ //1、//通过包名启动应用(前置应用) launch(packageName); sleep(500); // 2. 打开“最近任务”界面 home(); // 返回桌面 sleep(500); recents(); // 打开最近任务 sleep(1000); //当前正在运行的应用的包名【currentPackage()表示获取当前正在运行的应用的包名】 if (currentPackage() === "com.huawei.android.launcher" || currentPackage() === "com.android.launcher3") { console.log("当前在[最近任务]管理页面!"); let appName = getAppName(packageName); if (appName) { console.log("通过应用名查找(推荐):",appName); targetWidget = text(appName).findOne(800); if (targetWidget){ console.log("通过应用名称找到元素,获取元素坐标并操作!"); let bounds = targetWidget.bounds(); let centerX = bounds.centerX(); let centerY = bounds.centerY(); console.log(centerX, (device.height * 0.78).toFixed(2), centerX, centerY , 500); // 滑动关闭(从应用位置向上滑动) swipe(centerX, (device.height * 0.78).toFixed(2), centerX, centerY , 500); console.log("已关闭应用:", packageName); sleep(1000); return true; }else{ console.log("通过应用名称找不到元素,请重新调试元素信息逻辑!"); return false; } }else{ console.log("通过包名找应用名失败:"+packageName); return false; } } else{ console.log("当前不在[最近任务]管理页面,请仔细排查!"); return false; } } //关闭指定应用的进程(不同手机可能需要微调) close_recent_process("com.tencent.mtt");
3.获取函数执行时间
auto(); var start = new Date().getTime() //开始时间-时间戳毫秒 test() function test(){ log('执行函数test') sleep(2000) sleep(2000) sleep(2000) } var end = new Date().getTime()//结束时间-时间戳毫秒 //程序运行的时间-单位秒 var time_zhong = Math.floor((end - start) / 1000); toast("执行耗时"+time_zhong+"秒")
4.在一个js文件调用不同模块的函数
//main.js auto(); toastLog("启动脚本") //调用ceshi1.js var m2 = require("ceshi1.js") toast(m2.kuaishou("com.kuaishou.cn"))
//调用ceshi2.js var m3 = require("ceshi2.js") toast(m3.douyin("com.douyin.cn"))
//ceshi1.js module.exports={ //变量 a:1, //带两个形参的函数 kuaishou:(name)=>{ toast("执行快手函数-ceshi1.js") log("执行快手函数-ceshi1.js") return true } } //ceshi2.js module.exports={ //变量 a:2, //带两个形参的函数 douyin:(name)=>{ toast("执行douyin函数-ceshi2.js") log("执行douyin函数-ceshi2.js") return true } }
5.获取手机推送通知并上传到后端
采用autojs+python+flask
前端:
if (auto.service != null){ events.observeToast(); events.onToast(function(toast){ //log(toast.texts) //log("Toast内容:" + toast.getText() +'\n'+"包名:" + toast.getPackageName()) var text=toast.getText() log(text); var name=toast.getPackageName() var start = parseInt(new Date().getTime()/1000) //当前时间 log(start); if (text != null){ var url = "http://hv8g6k.natappfree.cc/smg_upload"; r = http.post(url, { "text": text, "name": name, "time": start }); var response=r.body.string();//获取响应 //同时弹起toast和记录日志 toastLog(response); } }); } else{ app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" }); }
后端:
#!/usr/bin/env python # encoding: utf-8 import flask from flask import request, send_from_directory,jsonify server = flask.Flask(__name__)#创建Flask对象server @server.route('/smg_upload', methods=['post']) def smg_upload(): '''接收客户端上报的推送''' text=(request.form).to_dict() print(text) if text: #这里可以使用mysql入库操作 return jsonify({"msg": "True", "ret": 1,"code": "200","text":"请求成功"}) else: return jsonify({"msg": "False", "ret": 2,"code": "500","text":"请求失败,请重试"}) if __name__ == '__main__': server.run(port=8000, debug=True, host='0.0.0.0')#可以绑定域名
6.微信扫一扫
"auto"; launch("com.tencent.mm"); sleep(1500); //循环点击元素直到成功(空语句缩写) while(!click("发现")); while(!click("扫一扫")); sleep(1000); //判断指定元素 if(text("相册").exists()){ toast("找到相册"); //点击相册坐标(也可使用元素点击) click(950,1930); sleep(1500); //点击相册中图片 click(400,350); }
7.解锁手机
// 输入密码(也可以用滑动方式gesture()函数滑动解锁,需要根据手机设置方式确定) function password_input() { var password = "123456" for(var i = 0; i < password.length; i++) { var p = text(password[i].toString()).findOne().bounds(); click(p.centerX(), p.centerY()); sleep(100); } }
// 解锁屏幕 function unlock() { if(!device.isScreenOn()) { device.wakeUp(); //唤醒设备 sleep(500); //必须加等待时间,不然可能滑动不生效
//滑动设备解锁 swipe(500,2000,500,1000,210);
//调用输入密码函数 password_input(); } }
//调用函数 unlock();
三、实战案例
1.实现判断、启动、进服操作
auto(); //判断启动包名并启动 function start_app(packageName,AppName){ if (AppName != null) { //停止app shell("rm -rf ./sdcard/.dalan"); toastLog('开始启动:'+AppName); launch(page); sleep(15000); click(20,30); //单点击坐标 ////_________________截图操作____________________________________ //1.请求截图权限 if(!requestScreenCapture()){ toast("请求截图失败"); exit(); } //2、进行截图 captureScreen("/sdcard/test/shywl_96/登录前开始游戏.png"); sleep(2000) ////______________________________________判断图片相似度(可以两张一样的图片)_____________________________________ //大图 let max = images.read("/sdcard/test/shywl_96/登录前开始游戏.png"); //小图 let min = images.read("/sdcard/test/shywl_96/min登录前开始游戏.png"); //对比相似对(threshold是阈值) let p = findImage(max, min,{threshold:0.6});//// 找到返回true, 失败返回false if (p) { print('找到开始游戏按钮'); click(550,1570); sleep(8000); } //如果没有匹配到图片,就判断是否有账号登录 else if (text("帐号登录").findOne(20000)) { toastLog('打开游戏成功-有账号登录') click(800, 800);//点击帐号登录 if (text("登录").findOne(3000)) { log('当前已在账号输入页面') setText(id("dlhm_et_account").find(), '18520103625');//输入账号 var pass = className("android.widget.EditText").text("请输入密码").findOne(1000); pass.setText(123456);//输入密码 click('登录'); //点击text的元素 } else { log('有账号登录、没有到账号密码页面') } } else{ log('图片匹配失败并没有登录元素') } } else { log('手机没有安装:'+packageName) } } //杀掉app应用 function kill_app(packageName) { var name = getPackageName(packageName); if (!name) { if (getAppName(packageName)) { name = packageName; } else { return false; } } app.openAppSetting(name); text(app.getAppName(name)).waitFor(); let is_sure = text("强行停止").findOne(); if (is_sure.enabled()) { click('强行停止'); sleep(700); //确定强行停止 text("强行停止").findOne().click(); log(app.getAppName(name) + "应用已被关闭"); sleep(1000); back(); } else { log(app.getAppName(name) + "应用不能被正常关闭或不在后台运行"); back(); } } device.wakeUp();//判断是否黑屏 var page = 'com.dl.ctmd.gdt'; var AppName = getAppName(page); start_app(page,AppName);//启动游戏 kill_app(page);//关闭app
2.抖音评论区截流
说明:含调用扣子生成关键词(调用扣子服务放在腾讯云-私有服务里)
toast('>>>>>开始测试>>>>');
for(var globaldata=0;globaldata<50;globaldata++){
console.log("全局便利次数:%d",globaldata);
"auto";
var apkname = "抖音";
var search_keywords = kouzigenerate(); //调用生成关键字
console.log("search_keywords="+search_keywords);
launchApp(apkname);//先把应用前置
killapk(apkname);//调用关闭应用(自定义函数实现)
launchApp("抖音");//初始化应用
sleep(5000);
//*业务场景的编排*//
search_method(search_keywords);
comment_method(40,100);
}
/***** 评论区截流操作(count_videos需要爬取视频总数,featured_videos过滤评论数较少的视频) *****/
function comment_method(count_videos,featured_videos){
var comment_rees = 0;
//控制刷多少视频
for(var i=1;i<count_videos;i++){
sleep(1000);
swipe(500,1500,500,300,200);
var randoama = getRandomInt(3000,5000);
sleep(randoama);
var comments = className("android.widget.TextView").findOne().text();
//当视频的评论数大于xx时,才会截流操作
if (comments >featured_videos){
console.log("当前遍历第%d视频的评论数为:%d,需要截流处理!",i,comments);
descStartsWith("评论").find().click();
//随机生成滑动次数
var random_conut = getRandomInt(4,10);
//控制视频下的 评论滑动次数
for(var comment =1;comment<random_conut;comment++){
//生成随机间隔时间
var randomNumber = getRandomInt(3000,7000);
console.log("随机执行滑动:%d次,当前需要等待:%d毫秒",random_conut,randomNumber);
sleep(randomNumber);
swipe(500,1500,500,500,200); //滑动评论区
}
//评论操作
var editText = id("dr=").findOne();
if (editText) {
editText.click();
sleep(500);
setText("需要资料,可以看主页说明!");
sleep(1000);
swipe(500,200,500,100,500); //滑动弹框
sleep(1000);
click(950,2250);//点击发送评论
sleep(1000);
id("back_btn").findOne().click();
sleep(1300);
}
}
else if (comments <= featured_videos){
console.log("当前第%d个视频的评论数太少:%d个,跳过截流操作!",i,comments);
}
else if (isNaN(comments)) {
console.log("当前第%d个视频的评论数:%d个,可能原因(没有获取到作品的评论数|页面没有到达指定页!",i,comments);
comment_rees++;
if (comment_rees > 3){
console.log("跳出执行,重新启动任务!");
break;
}
}else{
console.log("当前第%d个视频出现未知情况!",i);
}
}
}
/***** 搜索功能函数 *****/
function search_method(keywordname) {
let search = "搜索";
if(desc(search).findOne(8000)){
log("找到了主页元素:"+search);
desc(search).find().click();
sleep(1000);
setText(keywordname);//输入搜索关键词
sleep(1000);
click(900,150);//点击搜索
sleep(6000);
if(className("android.widget.Button").text("视频").exists()){
sleep(1000);
className("android.widget.Button").text("视频").findOne().parent().click();
log("切换到视频tab页!");
sleep(2000);
id("pb4").findOne().click();
sleep(1000);
}else{
log('未知错误,请持续留意!');
}
}else{
log('没找到:'+search);
}
}
/***** 生成一个在[min, max]范围内的随机整数 *****/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/***** 调用AI大模型生成关键词 *****/
function kouzigenerate() {
var concat = "元素:一年级数学|一年级语文|二年级数学|二年级语文|一年级数学期末复习|二年级语文期末复习,需求:根据上面关键词随机生成一条,期望结果:15个字左右,结果不要重复,生成一条即可";
var url="http://101.42.38.92:8000/user/kouziapi?problem="+concat;
var res = http.get(url);
//判断状态码
if(res.statusCode >= 200 && res.statusCode < 300){
toast("页面获取成功!");
}else if(res.statusCode == 404){
toast("页面没找到哦...");
}else{
toast("错误: " + res.statusCode + " " + res.statusMessage);
}
var data = res.body.string();
log('扣子AI大模型生成的关键词:',data);//以字符串形式返回响应内容
sleep(10000);
return data;
}
/***** app应用关闭 *****/
function killapk(androidname) {
var packageName = getPackageName(androidname);//通过app名称获取包名
app.openAppSetting(packageName);//通过包名打开应用的详情页(设置页)
sleep(random(1000, 2000));
text(app.getAppName(packageName)).waitFor(); //通过包名获取已安装的应用名称,判断是否已经跳转至该app的应用设置界面
let is_sure = textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/).findOne();//正则方式匹配包含“强”,“停”,“结”,“行”的控件
if (is_sure.enabled()) {
textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/).findOne().click();
sleep(1000);
console.log("进入设置页面成功,准备kill应用!");
text("强行停止").findOne().click();
log(app.getAppName(packageName) + "应用已被关闭");
sleep(random(1000, 2000));
back();
} else {
log(app.getAppName(packageName) + "应用不能被正常关闭或不在后台运行");
back();
sleep(random(1000, 2000));
}
}
相关连接:
https://blog.csdn.net/feiyunjs/article/details/94722766.....................................常用的代码和公共函数搜集整理
https://blog.csdn.net/qq_30856231/article/details/110118735 ........................autojs常用案例
https://www.jianshu.com/p/3b24656b22c9 .....................................................autojs常用函数,autojs最全的案例仓库
https://blog.csdn.net/weixin_33690963/article/details/93515638 ....................爬取支付水滴
https://blog.csdn.net/tfnmdmx/article/details/118544342 ................................autojs多线程-复杂版
https://www.cnblogs.com/Mr-lin66/p/11969098.html .......................................手机同步到pc并获取元素
https://www.codeleading.com/article/98912319617/ .......................................惠头条项目 ,,auto.js实现攒攒 ,,抖音检测弹窗 ,,京东2020双十一脚本-非UI
https://pro.autojs.org/docs/#/zh-cn/project?id=launchconfig .........................项目打包配置
浙公网安备 33010602011771号