哔哩哔哩:https://www.bilibili.com/video/BV1te411Z78u
1、RPA脚本开发简介
第一部分:私人助理RPA (脚本部分)
第二部分:业务RPA (脚本部分)
Autojs实现
流程化、标准化 => 系统
2、私人助理RPA的原理
- 什么是私人助理RPA
- 使用场景:定时执行、指令执行
- 简单演示
开发步骤:
- 把每天要做任务流程化
- RPA唤醒
- 下发指令(发朋友圈)
- RPA返回结果
- 把流程标准化
- 开发私人助理RPA的框架
- 增加功能扩展的方式
- 实现7*24小时运行
- 调试优化
3、私人助理RPA的框架开发
准备工作:两个微信,登录到不同或者相同的手机上
1、实现收到消息自动唤醒
2、程序结构分析(长时间运行、能够扩展、方便对接云控和群控)
- main.js (控制器角色)
- 功能名.js (实现具体功能)
- common.js (公共函数)
4、实现指令识别功能
1、判断监听的消息是否是 sender 发过来的
let title = notification.getTitle();
log(title);
if(title == sender){
toast("收到指令");
}
2、获取收到的指令内容
// 获取指令
let instruct = notification.getText();
// 判断指令
if(instruct.indexOf("条]") !== -1){
instruct = instruct.split(":")[1].replace(/^\s*|\s*$/g,"");
}
3、判断获取的指令是否是任务指令
if(jumpModel.indexOf(instruct) !== -1){
// 进行跳转
notification.click();
engines.execScriptFile("/sdcard/脚本/"+ instruct +".js");
}
5、相对路径和绝对路径的使用
相对路径(相对于运行文件):path.js、./path.js、../path.js、../../path.js
绝对路径:/path.js 、/sdcard/脚本/path.js
获取脚本运行路径:
files.cwd()
engines.myEngine().cwd()
6、实现对话的功能
1、判断是否来到聊天界面
// 等待进入聊天界面聊天界面
if(!text(sender).findOne(5000)){
toastLog("===========未进入聊天界面");
exit();
}
if(!className("android.widget.EditText").findOne(2000)){
toastLog("===========未找到输入框");
exit();
}
2、回复消息在呢
setText("在呢");
sleep(1000);
click("发送")
sleep(1000);
3、等待新消息出现
let msgNode = text(flag).find();
let msgY = msgNode.get(msgNode.size() -1).bounds().centerY();
for(let i = 0;i<30;i++){
msgNode = text(flag).find();
let msgY2 = msgNode.get(msgNode.size() -1).bounds().centerY();
// 判断新信息是否出现
if(msgY2 !== msgY){
toast("新消息出现");
break;
}else{
sleep(1000);
}
}
toast("等待超时");
4、获取新消息并进行相应回复
// 判断新消息内容
let chatCollection = id("com.tencent.mm:id/b4b").visibleToUser().find();
let lastChat = chatCollection.get(chatCollection.size() - 1);
let lastChatText = lastChat.text();
switch(lastChatText){
case "测试":
setText("已准备好");
sleep(1000);
click("发送")
sleep(1000);
break;
case "退出":
setText("好的");
sleep(1000);
click("发送")
sleep(1000);
break;
default:
setText("抱歉,未找到相应回复");
sleep(1000);
click("发送")
sleep(1000);
}
5、实现循环对话
let loop = true;
while(loop){
}
7、完善优化对话功能
1、封装等待进入聊天界面的函数
// 等待进入聊天界面
function waitChat(sender){
if(!text(sender).findOne(5000)){
toastLog("===========未进入聊天界面");
exit();
}
if(!className("android.widget.EditText").findOne(2000)){
toastLog("===========未找到输入框");
exit();
}
}
2、封装发送消息的函数
// 发送消息
function sendText(msg){
setText(msg);
sleep(1000);
let sendBtn = text("发送").className("android.widget.Button").findOnce();
randClick(sendBtn);
sleep(1000);
}
function randClick(node){
let x = node.bounds().centerX();
let y = node.bounds().centerY();
let randX = x + random(-10,10);
let randY = y + random(-10,10);
click(randX,randY);
}
3、封装等待消息出现的函数
// 等待消息出现
function waitMsg(sendMsg,n){
// 3、等待新消息出现
let msgNode = text(sendMsg).find();
let msgY = msgNode.get(msgNode.size() -1).bounds().centerY();
// 等待N秒
for(let i = 0;i<n;i++){
msgNode = text(sendMsg).find();
let msgY2 = msgNode.get(msgNode.size() -1).bounds().centerY();
// 判断新信息是否出现
if(msgY2 !== msgY){
toast("新消息出现");
return true;
}else{
sleep(1000);
}
}
return false;
}
4、封装获取对话中的最后一条消息的函数
// 获取对话中的最后一条消息
function getLastChat(idSelector){
// 4、获取新消息并进行相应回复 "com.tencent.mm:id/b4b"
let chatCollection = id(idSelector).visibleToUser().find();
let lastChat = chatCollection.get(chatCollection.size() - 1);
let lastChatText = lastChat.text();
return lastChatText;
}
8、集成对话功能到RPA框架
1、将功能放到rpa文件夹
2、使用本地存储传递数据
3、只允许一个脚本功能运行
let engineList = engines.all();
log(engineList)
if(engineList.length > 1){
return;
}
4、防止主脚本二次重启 preventRestart(‘main’);
function preventRestart(){
let engineList = engines.all();
log(engineList)
if(engineList.length > 1){
let engineId = storage.get("engineId");
log("正在运行的引擎:"+engineId);
for (let i = 0; i < engineList.length - 1; i++){
if (engineId !== engineList[i].id){
engineList[i].forceStop();
log("停止脚本引擎"+ engineList[i].id)
sleep(5000);
}
}
}else{
storage.put("engineId",engineList[0].id);
}
}
9、实现自动发朋友圈功能
1、设计触发方式:关键词、对话中
2、获取图片和文案
let sendMsg = '请发图片';
sendText(sendMsg);
let loop = true;
while(loop){
let res = waitContent(sendMsg);
if(res){
// 判断新消息内容
let chatCollection = id("com.tencent.mm:id/b4b").visibleToUser().find();
let lastChat = chatCollection.get(chatCollection.size() - 1);
let lastChatText = lastChat.text();
if(lastChatText == "请发图片"){
downloadImg();
sendText(sendMsg);
}else{
log(lastChatText);
switch(lastChatText){
case "好了":
sendMsg = "请发送文案";
sendText(sendMsg);
loop = 1;
break;
case "退出":
sendText("好的");
loop = false;
break;
default:
if(loop == 1){
PYQText = lastChatText;
sendText("文案已获取,开始发圈");
loop = false;
}else{
sendMsg = "抱歉,未找到相应回复\n如果图片已发送完毕,请回复 好了";
sendText(sendMsg);
sendMsg = '请发图片';
sendText(sendMsg);
}
}
}
}else{
sendText("等待超时,已退出");
break;
}
}
3、执行发朋友圈功能
10、完善优化发朋友圈功能
1、返回桌面函数的封装
deskTop("com.miui.home:id/icon_title");
// 回到桌面
function deskTop(idSelector){
for(let i = 0;i<10;i++){
// 判断是否再桌面
if(id(idSelector).findOnce()){
break;
}else{
back();
sleep(1000);
}
}
if(!id(idSelector).text("微信").visibleToUser().findOnce()){
home();
sleep(1000)
}
toastLog('已回到桌面');
}
2、选择图片的解决方案
clearDir("/storage/emulated/999/Pictures/WeiXin/");
clearDir("/storage/emulated/0/Pictures/WeiXin/");
function clearDir(dir){
var dirPath = dir;
var arr = files.listDir(dirPath);
arr.forEach(element => {
var filePath = files.join(dirPath, element);
log(files.remove(filePath));
sleep(100);
});
toast('图片清理成功');
}
3、运行结果反馈到发指令者
function sendBack(backText){
let loop = true;
while(loop){
deskTop();
// 点击微信
click(395,1399);
// 查看来到的是微信
for(let i = 0;i<10;i++){
if(text('通讯录').findOne(3000)){
toast("来到首页");
break;
}else{
back();
sleep(3000);
}
}
// 判断是否来到微信聊天列表界面
for(let i = 0;i<10;i++){
if(text("微信").boundsInside(0, 0, device.width, device.height / 2).visibleToUser().findOnce()){
toast('来到聊天界面');
break;
}else{
click(144,2119);
sleep(3000);
}
}
if(!text(sender).visibleToUser().findOnce()){
click(310,165);
sleep(10);
click(310,165);
sleep(2000);
}
findAndClick("text('"+sender+"')");
if(text(sender).visibleToUser().exists() && descStartsWith("切换到").exists()){
sendText(backText);
deskTop();
loop = false;
}
}
}
11、集成发朋友圈功能到RPA框架
1、使用main函数触发功能
2、将重复函数集中到common文件
3、引入common模块
12、实现远程更新功能
1、对话式功能
2、自动保存文件到脚本文件夹
let jsPath = "/storage/emulated/0/Download/WeiXin/";
// 点击最后一个.js文件
let jsCollection = textEndsWith(".js").visibleToUser().find();
let lastJs = jsCollection.get(jsCollection.size() - 1);
let fileName = lastJs.text();
randClick(lastJs);
sleep(3000);
// 关闭出现的对话框
for(let i= 0 ;i<2;i++){
let confirmBtn = text("确定").findOnce();
if(confirmBtn){
randClick(confirmBtn);
sleep(1500);
}
}
// 点击右上角三个点
let moreNode = desc("更多信息").findOne(2000);
if(moreNode){
randClick(moreNode);
sleep(1500);
}
// 点击保存
let saveNode = text("保存").findOne(2000);
if(saveNode){
upClick(saveNode);
sleep(3000);
// 移动文件到脚本文件夹
files.move(jsPath+fileName, "/sdcard/脚本/" + fileName);
}
back();
function upClick(node) {
let x = node.bounds().centerX();
let y = node.bounds().centerY();
let randX = x + random(-10,10);
let randY = y - 50;
click(randX,randY);
}
function randClick(node){
let x = node.bounds().centerX();
let y = node.bounds().centerY();
let randX = x + random(-10,10);
let randY = y + random(-10,10);
click(randX,randY);
}
13、完善优化远程更新功能
1、回到桌面
2、循环返回
function loopBack(str){
for(let i = 0; i < 5;i++){
if(text(str).visibleToUser().findOnce()){
return true;
}else{
back();
sleep(2500);
}
}
return false;
}
3、清空js文件
14、集成远程更新功能到RPA框架
1、使用main文件触发功能
2、将重复函数集中到common文件
3、引入common模块
15、实现动态获取指令功能
1、遍历运行目录的js文件
let dir = engines.myEngine().cwd();
let jsFiles = files.listDir(dir, function(name){
return name.endsWith(".js") && files.isFile(files.join(dir, name));
});
log(jsFiles);
2、将文件名加入数组
jsFiles.forEach(item => {
if(item !== "main.js" && item !== "common.js"){
item = item.split(".j")[0];
arr.push(item);
}
})
3、封装动态获取指令的函数
function getJs(dir){
let jsFiles = files.listDir(dir, function(name){
return name.endsWith(".js") && files.isFile(files.join(dir, name));
});
return jsFiles;
}
function getInstruct(jsFiles){
let arr = [];
jsFiles.forEach(item => {
if(item !== "main.js" && item !== "common.js"){
item = item.split(".j")[0];
arr.push(item);
}
})
return arr;
}
4、目录更改后则调用此函数
let jsFiles = getJs(engines.myEngine().cwd());
log(getInstruct(jsFiles));
16、实现卸载功能
1、进入对话后的代码结构
2、根据功能名删除文件
17、完善优化卸载功能
1、返回到桌面
18、集成卸载功能到RPA框架
1、使用main文件触发功能
2、将重复函数集中到common文件
3、引入common模块