手机模拟脚本思路总结
1.以UI界面为分步判断条件,建立脚本整体框架
2.优先以节点进行条件判断,以图色为辅助进行条件判断
3.推荐以上2中判断模式同步使用,稳定性更好
4.图色模式优先使用FindColor区域找色,不要使用GetPointColor
5.图色模式RGB,色差050505,相似度0.956.模拟延迟1000-1500-20007.雷电模拟器 手机推荐配置 Android 9:720*1280 320dpi
8.雷电模拟器4.0.83(64)https://www.ldmnq.com/other/version-history-and-release-notes.html?log=4&n=60009.Android懒人精灵代理模式(推荐)http://www.lrappsoft.com/10.Android节点精灵无障碍模式(无障碍模式开启麻烦,不稳定)http://www.nspirit.cn/11.ios推荐EasyClick开发(IDLE繁重,签名麻烦)https://ieasyclick.com/12.云手机(中控方便)红手指:https://www.gc.com.cn/ 稳定性好,推荐
合鑫云:http://www.hexiny.com/ 测试连接方便
13.YOLOv5
节点精灵
懒人精灵也有调用示例
其他可以做服务器接口调用
14.数据库
节点精灵自带数据库
懒人精灵自带数据库
```javascript
```javascript
--研修宝.lua
--Android 9:720*1280 320dpi 云手机通用
--雷电模拟器4.0.83(64)--懒人精灵开发
--默认密码abc123
username="16620122910"
password=""
卡密=""
version="2000-00-00 00:00:00"--require("form")require("common")require("dm")require("DDMC")function判断_初始界面_是否完成()log("判断_初始界面_是否完成")
local ret
tap(116,1220)--项目
sleep(2000)
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_learning_score_content"},false)if ret ~= nil then
if math.floor(ret.text)>=80 then
log("初始界面:已通过...findOne")tap(361,1220)--任务
sleep(2000)returntrue
end
end
tap(361,1220)--任务
sleep(2000)log("初始界面:暂未通过2")returnfalse
end
function判断_研修阶段_点击()
local ret = nodeLib.findAll(0,0,0,0,{class="android.widget.LinearLayout",id="com.yanxiu.gphone.faceshow:id/ll_score"},false)if ret ~= nil then
print(#ret)if #ret>=1 then
for i=1,#ret,1do
local r = nodeLib.findInNode(ret[i],{class="android.widget.TextView"},true,true)
local a=nil
local b=nil
if r~= nil then
if #r==2 then
a=r[1].text
b=r[2].text
print(a)print(b)if a and b then
b=string.match(b,"%d%d")if a~=b then
nodeLib.click(ret[i])sleep(3000)returntrue
end
end
end
end
end
end
end
print("判断_研修阶段_点击:第一次失败")returnfalse
end
function判断_看课界面_是否完成()
local ret,x,y,a,b
if找色(301-5,574-5,301+5,574+5,"1B9F8B") then
log("看课界面:完成...找色")returntrue
end
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_total_score",text="满分:.*分"},true)if ret ~= nil then
a=string.match(ret.text,"%d%d分")
end
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_score",text="已得:.*分"},true)if ret ~= nil then
b=string.match(ret.text,"%d%d分")
end
if a and b and a==b then
log("看课界面:完成...findOne")returntrue
end
log("看课界面:未完成")
end
function判断_讲师界面_是否完成()
local ret,x,y
if找色(615-5,459-5,615+5,459+5,"00B69B") then
log("讲师界面:完成...找色")returntrue
end
log("讲师界面:未完成")
end
function单个视频观看结束()
local intX,intY,ret
intX,intY =findMultiColor(370,438,384,454,"000000-050505","1|0|FFFFFF-050505|2|1|FFFFFF-050505|5|1|000000-050505|8|1|000000-050505|12|1|FFFFFF-050505|11|5|000000-050505|7|5|FFFFFF-050505|3|5|000000-050505",0,0.95)if intX>-1 and intY>-1 then
log("单个视频观看结束:findMultiColor")log("单个视频观看结束")返回(2)returntrue
end
local ret = nodeLib.findOne(294,405,428,471,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_replay",index="2",text="重新播放"},false)if ret ~= nil then
log("单个视频观看结束:findnode")log("单个视频观看结束")返回(2)returntrue
end
returnfalse
end
function观看视频()--只处理观看视频界面
local index,ret,intX,intY
log("观看视频")点击_防检测(358,180)显示运行时长()点击_播放按钮()点击_课程目录()点击_点击重试()if单个视频观看结束() then
returntrue
end
end
function判断_初始界面()
local ret,x,y
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="切换项目"},false)if ret ~= nil then
log("发现:切换项目...findOne")点击_首页任务()sleep(2000)
end
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="研修工具"},false)if ret ~= nil then
log("发现:研修工具...findOne")点击_首页任务()sleep(2000)
end
if找色(253-10,215-10,253+10,215+10,"204ADC") then
log("初始界面_判断:color")returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="研修阶段"},false)if ret ~= nil then
log("初始界面_判断:node")returntrue
end
returnfalse
end
function判断_看课界面()
local ret,x,y
if找色(127-10,545-10,127+10,545+10,"00B69B") then
log("看课界面_判断:color")returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="满分:.*分"},true)if ret ~= nil then
log("看课界面_判断:node")returntrue
end
returnfalse
end
function判断_讲师界面()
local ret,x,y
intX,intY =findMultiColor(327,83,360,118,"FFFFFF-050505","1|0|989898-050505|2|0|000000-050505|5|0|5A5A5A-050505|6|0|FFFFFF-050505|4|4|B5B5B5-050505|0|-3|000000-050505|2|4|000000-050505|0|4|292929-050505|-1|4|D6D6D6-050505",0,0.95)if intX>-1 and intY>-1 then
log("讲师界面_判断:findMultiColor")returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="讲师:.*"},true)if ret ~= nil then
log("讲师界面_判断:node1")returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="课程列表"},true)if ret ~= nil then
log("讲师界面_判断:node2")returntrue
end
returnfalse
end
function判断_播放界面()
local ret
if找色(57,596,419,666,"204ADC") then
log("播放界面_判断:color")returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",level="15",text="课程详情"},false)if ret ~= nil then
log("播放界面_判断:node")returntrue
end
--log("不在_播放界面")returnfalse
end
function判断_已观看时间()log("判断_已观看时间")
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_watch_time",text="您已观看.*分钟"},true)if ret ~= nil then
local s=ret.text
s=string.match(s,"%d+")if s~="0" then
local yk=tonumber(s)tap(354,354)sleep(1000)
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/exo_duration"},false)if ret ~= nil then
local tt=string.match(ret.text,"%d+")
local total=tonumber(tt)if math.abs(yk-total)<=1 then
log("视频时长:"..total.."分钟|已看:"..yk.."分钟")returntrue
end
end
end
end
end
function初始页面()--首页链接坐标_Y()if判断_初始界面_是否完成() then
returntrue
end
if判断_研修阶段_点击()==false then
print("模拟滑动")模拟滑动(300,900,300,300)--上
判断_研修阶段_点击()
end
点击_知道了()点击_看课()
end
functionmain()whiletruedosleep(1000)log("running......")if判断_初始界面() then
if初始页面() then
break
end
elseif 判断_看课界面() then
if判断_看课界面_是否完成() then
返回(1)点击_首页任务()else点击_看课()
end
elseif 判断_讲师界面() then
if判断_讲师界面_是否完成() then
返回(2)else点击_讲师()if判断_已观看时间() then
返回(2)
end
end
elseif 判断_播放界面() then
观看视频()elseif判断_是否前台运行("com.yanxiu.gphone.faceshow")==false then
log("需要启动app")runApp("com.yanxiu.gphone.faceshow")sleep(6000)
end
end
-----------------------帐号掉线(password)点击_观看下一节()点击_知道了()点击_点我继续计时()点击_提交()点击_取消确定()点击_关闭程序()点击_首页任务()
end
end
------------------------test
------------------------test
读取界面配置()--username password 卡密
--sleep(9999999)print("卡密:"..卡密)--DDMC_卡密登录(卡密)DDMC_初始化_检测更新()初始化()main()whiletruedolog("任务完成")sleep(2000)
end
--common.lua
print("common被调用了")
Y_arr_1={}--记录首页点击_的坐标Yfunctionlog(msg)--toast(msg)print(msg)
console.println(3,msg)
end
function首页链接坐标_Y()if #Y_arr_1==0 then
log("首页链接坐标_Y:初始化......")模拟滑动(300,900,300,300)
local ret = nodeLib.findAll(0,0,0,0,{class="android.view.View",id="com.yanxiu.gphone.faceshow:id/progress_task_item_step"},false)if ret ~= nil then
log(#ret)for i=1,#ret,1do
y=ret[i].bounds.t
log(y)
table.insert(Y_arr_1,y)
end
end
end
end
function点击_看课()log("点击_看课")tap(230,541)sleep(3000)
end
function点击_讲师()log("点击_讲师")setDict(0,"dict.txt")useDict(0)for x=0,100,10dofor i=1,4,1do
local str =ocr(585,416+(i-1)*217,662,493+(i-1)*217,"000000-050505",0.95)if str ~= nil and #str>0 then
log(str)
local n=tonumber(str)if n<=x then
tap(585,416+(i-1)*217)sleep(3000)returntrue
end
end
end
end
tap(345,430)sleep(3000)
end
function点击_课程目录()
local ret
if找色(334,599,387,663,"204ADC") then
returntrue
end
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="课程目录"},false)if ret ~= nil then
log("点击_课程目录:findOne")
nodeLib.click(ret)sleep(2000)returntrue
end
returnfalse
end
function点击_观看下一节()找色点击(350,378,366,445,"48C5A0")
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_next"},false)if ret ~= nil then
log("点击_观看下一节:node")
nodeLib.click(ret)sleep(2000)returntrue
end
returnfalse
end
function点击_播放按钮()if找色(67,516,70,528,"FFFFFF") then
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.ImageButton",desc="播放",id="com.yanxiu.gphone.faceshow:id/exo_play",text=""},true)if ret ~= nil then
log("点击_播放按钮1")
nodeLib.click(ret)sleep(2000)returntrue
end
end
if找色(350,349,361,361,"FFFFFF") then
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.ImageView",id="com.yanxiu.gphone.faceshow:id/iv_do_play"},true)if ret ~= nil then
log("点击_播放按钮:首次进入的播放")
nodeLib.click(ret)sleep(2000)returntrue
end
end
returnfalse
end
function点击_知道了()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="知道了"},false)if ret ~= nil then
log("点击_知道了")
nodeLib.click(ret)--知道了
sleep(2000)
end
end
function点击_首页任务()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="切换项目"},false)if ret ~= nil then
log("点击_首页任务:findOne")tap(359,1222)sleep(3000)returntrue
end
--174,1842 214BDD
if找色(350-5,1227-5,350+5,1227+5,"214BDD") then
log("点击_首页任务:找色")tap(359,1222)sleep(3000)returntrue
end
returnfalse
end
function点击_点我继续计时()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="点我继续计时"},false)if ret ~= nil then
log("点我继续计时")
nodeLib.click(ret)sleep(2000)
end
end
function点击_提交()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="提交"},false)if ret ~= nil then
log("提交")tap(813,1557)sleep(2000)
nodeLib.click(ret)--提交
sleep(2000)
end
end
function点击_取消确定()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_fixed_confirm",text="确定"},false)if ret ~= nil then
log("确定...findOne")
nodeLib.click(ret)--确定|取消
sleep(2000)
end
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_fixed_cancel",text="取消"},false)if ret ~= nil then
log("取消")
nodeLib.click(ret)--确定|取消
sleep(2000)
end
local intX,intY =findMultiColor(532,1158,566,1191,"305AED-050505","-6|-12|305AEC-050505|-6|-9|305AED-050505|-6|-6|305AEC-050505|-6|-3|305AED-050505|-6|0|305AEC-050505|-3|0|315BED-050505|-3|0|315BED-050505|0|0|305AED-050505|3|0|315BEE-050505",0,0.95)if intX>-1 and intY>-1 then
log("确定...findMultiColor")tap(532,1158)sleep(2000)
end
end
function点击_点击重试()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="点击重试"},false)if ret ~= nil then
log("点击重试")
nodeLib.click(ret)--点击重试
sleep(2000)
end
end
function点击_关闭程序()
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="关闭程序"},false)if ret ~= nil then
log("关闭程序")
nodeLib.click(ret)--关闭程序
sleep(3000)
end
end
function帐号掉线(pwd)
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.Button",text="忘记密码?"},false)if ret ~= nil then
log("帐号掉线pwd:"..pwd)if #pwd>0 then
local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.EditText",id="com.yanxiu.gphone.faceshow:id/et_password",text="请输入密码"},false)if ret ~= nil then
tap(577,595)sleep(2000)
nodeLib.setText(ret,pwd)
ret = nodeLib.findOne(0,0,0,0,{class="android.widget.ImageView",id="com.yanxiu.gphone.faceshow:id/iv_privacy",selected=false},false)if ret ~= nil then
print("我同意")tap(182,883)sleep(2000)
end
tap(359,793)sleep(6000)
end
returntrue
end
end
returnfalse
end
--dm.lua
print("dm被调用了")functionlog(msg)--toast(msg)print(msg)
console.println(3,msg)
end
function初始化()lockScreen()--Android 9:720*1280 320dpi
local ret =setDisplaySize(720,1280,320)log("修改真实物理分辨率:"..tostring(ret))
local w,h =getDisplaySize()log("真实物理分辨率:"..w..","..h)
local dpi=getDisplayDpi()log("真实物理dpi:"..dpi)--toast("Version:"..version)--sleep(2000)
end
function返回(n)for i=1,n,1dokeyPress("back")sleep(2000)
end
end
function点击_防检测(x,y)
local x0,y0
math.randomseed(os.time())
local r = math.random(1,30)if r==3 then
log("随机点击_防检测")
x0=x+math.random(-10,10)
y0=y+math.random(-10,10)tap(x0,y0)sleep(2000)
end
end
function判断_是否前台运行(pkg)--影响稳定性
local ret =appIsFront(pkg)if ret ==false then
runApp(pkg)sleep(6000)
end
end
function显示运行时长()
local t1 =tickCount()/1000
local h=t1//3600
local m=(t1-(h*3600))//60
local s=t1-(h*3600)-(m*60)
h=math.floor(h)
m=math.floor(m)
s=math.floor(s)if h>0 then
toast("已运行:"..tostring(h).."时"..tostring(m).."分"..tostring(s).."秒")
elseif m>0 then
toast("已运行:"..tostring(m).."分"..tostring(s).."秒")elsetoast("已运行:"..tostring(s).."秒")
end
end
function找色(x1,y1,x2,y2,color)
local x,y,ret
ret,x,y=findColor(x1,y1,x2,y2,color.."-050505",0,0.95)if x~=-1 and y ~=-1 then
returntrue
end
returnfalse
end
function找色点击(x1,y1,x2,y2,color)
local x,y,ret
ret,x,y=findColor(x1,y1,x2,y2,color.."-050505",0,0.95)if x~=-1 and y ~=-1 then
log("找色点击:ok..."..color..x..","..y)tap(x,y)sleep(1000)returntrue
end
returnfalse
end
function等待颜色出现(x1,y1,x2,y2,color,t)
local x,y,ret
for i=1,t,1dolog("等待颜色出现:"..color.."|用时:"..t.."秒")sleep(1000)
ret,x,y=findColor(x1,y1,x2,y2,color.."-050505",0,0.95)if x~=-1 and y ~=-1 then
log("等待颜色出现:ok..."..color)returntrue
end
end
log("等待颜色出现:超时..."..color)returnfalse
end
function模拟滑动(x1,y1,x2,y2)log("模拟滑动:start")touchDown(1,x1,y1)sleep(100)touchMoveEx(1,x2,y2,1000)touchUp(1500)log("模拟滑动:end")returnfalse
end
--[===[local path =getSdPath().."/version.txt"writeFile(path,version)--print(ret)]===]--[===[local path =getSdPath().."/version.txt"
version =readFile(path)print("version:"..version)]===]function读取界面配置()log("读取界面配置:start")
local arr =getUIConfig("main.config")if arr ~=null then
for k, v inpairs(arr)doprint("key:==>"..k.." val:==>"..v)if k=="username" and #v>0 then
username=v
end
if k=="password" and #v>0 then
password=v
end
if k=="kami" and #v>0 then
卡密=v
end
end
print("username:"..username)print("password:"..password)print("卡密:"..卡密)
end
--ui.saveProfile("/sdcard/test.txt")log("读取界面配置:end")returnfalse
end
functionxxx()log("dm:start")log("dm:end")returnfalse
end
--DDMC.lua
print("DDMC被调用了")
DDMControl =require("DDMControlV2")--http://api.privateapi.xyz:9000/ 脚本管理后台 qq736592720
--更新内容填版本号,方便版本比对
云控项目UUID="f657e4313e4a4b05b3faa0b82148f53"--在后台创建项目之后,会生成一个云控项目UUID
作者UUID="60433592-365a-457a-973-c7bef4642b35"--注册号账号之后,在个人信息页面可以看到
项目UUID="30938e86-86fd-4161-a209-bc7844d06d1"--卡密:在后台创建卡密项目后会生成
项目密钥="dfdad9a7-c80f-4de7-3a8-cf77ddc9b162"--卡密:在后台创建卡密项目后会生成
云数据访问Token="4ccf45a7b444bf7a88f0946178a5598"
心跳失败次数 =0
UpdateTime="2000-00-00 00:00:00"
云控项目_初始化=falsefunction云项目_初始化()--用的就是DDMControl.初始化
local res = DDMControl.初始化("api.privateapi.xyz","9000",云控项目UUID,作者UUID,"")print(res)if res ~=true then
returnfalse
end
DDMControl.云控_连接云控系统()
end
-------------------------------------------------------------------卡密登录
function心跳回调(msg)print(msg)if msg.code ==1 then
print("心跳成功")toast("有效期到:"..msg.endTime)sleep(2000)
心跳失败次数 =0
elseif msg.code ==-5 then
print("请重新登录,一般是卡密被禁用,删除,设备被解绑!")exitScript()
elseif msg.code ==-8 then
toast("卡密到期")sleep(2000)exitScript()
elseif msg.code ==-9999 then
print("心跳失败,网络错误!")
心跳失败次数 = 心跳失败次数+1if 心跳失败次数 >=10 then --心跳连续失败10次,则停止运行
exitScript()
end
elseif msg.code ==-6666 then
print("有人尝试破解卡密系统!",msg.cdkey)exitScript()elseprint("未知错误!",msg.msg)exitScript()
end
end
functionDDMC_卡密登录(卡密)云项目_初始化()print("脚本开始函数")
local res = DDMControl.卡密_卡密登录(项目UUID,项目密钥,卡密,心跳回调,true,false,60)print(res)if res.code ==0 then
toast("卡密被禁用")sleep(2000)exitScript()
elseif res.code ==-1 then
toast("网络错误,请检查网络!")sleep(2000)exitScript()
elseif res.code ==1 then
print("卡密登录成功!")
elseif res.code ==-9 then
toast("卡密授权窗口达到上限,登录失败!")sleep(2000)exitScript()
elseif res.code ==-7 then
toast("卡密过期!")sleep(2000)exitScript()
elseif res.code ==-6666 then
print("有人尝试破解卡密系统!",res.cdkey)exitScript()elseprint("未知错误!",res.msg)toast(res.msg)sleep(2000)exitScript()
end
print("res:"..res.msg)return res.code
end
-------------------------------------------------------------------检测更新
function显示更新内容(更新时间,更新内容)
local 布局名称 ="DDMControlDataupdate"
local 循环 =truefunction立即更新()--UpdateTime
local path =getSdPath().."/version.txt"writeFile(path,UpdateTime)print("立即更新")toast("立即更新",0,0,12)
ui.dismiss(布局名称)whiletruedo
local res = DDMControl.热更新_下载Lr且更新()print("res:"..res)if res.code ~=-9999 then
break
end
end
循环 =falseprint("更新完成version:"..UpdateTime)
end
function取消()
ui.dismiss(布局名称)
循环 =false
end
ui.newLayout(布局名称,-1)
ui.setTitleText(布局名称,"更新提示")
ui.addLine(布局名称,"lineId1",-1,5)
ui.setBackground("lineId1","#ff000000")
ui.newRow(布局名称,"row0",-2,-2)
ui.addTextView(布局名称,"tvId","更新内容:\n\t\t\t\t"..更新内容,-1,-1)
ui.newRow(布局名称,"row2",-2,-2)
ui.addTextView(布局名称,"tvId","更新时间:"..更新时间)
ui.setGravity("row2",5)
ui.newRow(布局名称,"row3",-2,-2)
ui.addButton(布局名称,"ButUpdate","立即更新")
ui.addButton(布局名称,"ButCancel","取消")
ui.setOnClick("ButUpdate","立即更新()")
ui.setOnClick("ButCancel","取消()")
ui.setBackground("ButCancel","#ffe1e1e1")
ui.setTextColor("ButCancel","#ff000000")
ui.setGravity("row3",17)
ui.show(布局名称,false)while 循环 dosleep(100)
end
end
functionDDMC_初始化_检测更新()云项目_初始化()
local res=DDMControl.热更新_检测更新不直接更新()print(res)if res.code ==1 then
print("更新内容:"..res.UpdateInfo.UpdateLog)print(res.UpdateInfo.Version)--读取当前版本号
--如果更新文件版本号 <> 当前版本号
--更新
local path =getSdPath().."/version.txt"
version =readFile(path)print("当前版本version:"..version)--version="4"if version~=res.UpdateInfo.UpdateTime then
print(res.UpdateInfo.UpdateTime)
UpdateTime=res.UpdateInfo.UpdateTime
显示更新内容(res.UpdateInfo.UpdateTime,res.UpdateInfo.UpdateLog)returntrueelseprint("无需更新")
end
elseif res.code ==-1 then
print(res.msg)
elseif res.code ==0 then
print(res.msg)elseprint(res.msg)
end
end
functionDDMC_初始化_直接更新()云项目_初始化()
local res = DDMControl.热更新_检测更新并直接更新()if res.code ~=-9999 then
print("热更新_检测更新并直接更新:"..res.code)
end
end
-------------------------------------------------------------------云数据
--云项目_初始化()