手机模拟脚本思路总结1.以UI界面为分步判断条件,建立脚本整体框架2.优先以节点进行条件判断,以图色为辅助进行条件判断3.推荐以上2中判断模式同步使用,稳定性更好4.图色模式优先使用FindColor区域找色,不要使用GetPointColor5.图色模式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 rettap(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 thenif math.floor(ret.text)>=80 thenlog("初始界面:已通过...findOne")tap(361,1220)--任务sleep(2000)returntrueendendtap(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 thenprint(#ret)if #ret>=1 thenfor i=1,#ret,1dolocal r = nodeLib.findInNode(ret[i],{class="android.widget.TextView"},true,true)local a=nillocal b=nilif r~= nil thenif #r==2 thena=r[1].textb=r[2].textprint(a)print(b)if a and b thenb=string.match(b,"%d%d")if a~=b thennodeLib.click(ret[i])sleep(3000)returntrueendendendendendendendprint("判断_研修阶段_点击:第一次失败")returnfalse
end
function判断_看课界面_是否完成()local ret,x,y,a,bif找色(301-5,574-5,301+5,574+5,"1B9F8B") thenlog("看课界面:完成...找色")returntrueendlocal 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 thena=string.match(ret.text,"%d%d分")endlocal ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",id="com.yanxiu.gphone.faceshow:id/tv_score",text="已得:.*分"},true)if ret ~= nil thenb=string.match(ret.text,"%d%d分")endif a and b and a==b thenlog("看课界面:完成...findOne")returntrueendlog("看课界面:未完成")
end
function判断_讲师界面_是否完成()local ret,x,yif找色(615-5,459-5,615+5,459+5,"00B69B") thenlog("讲师界面:完成...找色")returntrueendlog("讲师界面:未完成")
end
function单个视频观看结束()local intX,intY,retintX,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 thenlog("单个视频观看结束:findMultiColor")log("单个视频观看结束")返回(2)returntrueendlocal 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 thenlog("单个视频观看结束:findnode")log("单个视频观看结束")返回(2)returntrueendreturnfalse
end
function观看视频()--只处理观看视频界面local index,ret,intX,intYlog("观看视频")点击_防检测(358,180)显示运行时长()点击_播放按钮()点击_课程目录()点击_点击重试()if单个视频观看结束() thenreturntrueend
end
function判断_初始界面()local ret,x,ylocal ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="切换项目"},false)if ret ~= nil thenlog("发现:切换项目...findOne")点击_首页任务()sleep(2000)endlocal ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="研修工具"},false)if ret ~= nil thenlog("发现:研修工具...findOne")点击_首页任务()sleep(2000)endif找色(253-10,215-10,253+10,215+10,"204ADC") thenlog("初始界面_判断:color")returntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="研修阶段"},false)if ret ~= nil thenlog("初始界面_判断:node")returntrueendreturnfalse
end
function判断_看课界面()local ret,x,yif找色(127-10,545-10,127+10,545+10,"00B69B") thenlog("看课界面_判断:color")returntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="满分:.*分"},true)if ret ~= nil thenlog("看课界面_判断:node")returntrueendreturnfalse
end
function判断_讲师界面()local ret,x,yintX,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 thenlog("讲师界面_判断:findMultiColor")returntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="讲师:.*"},true)if ret ~= nil thenlog("讲师界面_判断:node1")returntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="课程列表"},true)if ret ~= nil thenlog("讲师界面_判断:node2")returntrueendreturnfalse
end
function判断_播放界面()local retif找色(57,596,419,666,"204ADC") thenlog("播放界面_判断:color")returntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",level="15",text="课程详情"},false)if ret ~= nil thenlog("播放界面_判断:node")returntrueend--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 thenlocal s=ret.texts=string.match(s,"%d+")if s~="0" thenlocal 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 thenlocal tt=string.match(ret.text,"%d+")local total=tonumber(tt)if math.abs(yk-total)<=1 thenlog("视频时长:"..total.."分钟|已看:"..yk.."分钟")returntrueendendendend
end
function初始页面()--首页链接坐标_Y()if判断_初始界面_是否完成() thenreturntrueendif判断_研修阶段_点击()==false thenprint("模拟滑动")模拟滑动(300,900,300,300)--上判断_研修阶段_点击()end点击_知道了()点击_看课()
end
functionmain()whiletruedosleep(1000)log("running......")if判断_初始界面() thenif初始页面() thenbreakendelseif 判断_看课界面() thenif判断_看课界面_是否完成() then返回(1)点击_首页任务()else点击_看课()endelseif 判断_讲师界面() thenif判断_讲师界面_是否完成() then返回(2)else点击_讲师()if判断_已观看时间() then返回(2)endendelseif 判断_播放界面() then观看视频()elseif判断_是否前台运行("com.yanxiu.gphone.faceshow")==false thenlog("需要启动app")runApp("com.yanxiu.gphone.faceshow")sleep(6000)endend-----------------------帐号掉线(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 thenlog("首页链接坐标_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 thenlog(#ret)for i=1,#ret,1doy=ret[i].bounds.tlog(y)table.insert(Y_arr_1,y)endendend
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,1dolocal str =ocr(585,416+(i-1)*217,662,493+(i-1)*217,"000000-050505",0.95)if str ~= nil and #str>0 thenlog(str)local n=tonumber(str)if n<=x thentap(585,416+(i-1)*217)sleep(3000)returntrueendendendendtap(345,430)sleep(3000)
end
function点击_课程目录()local retif找色(334,599,387,663,"204ADC") thenreturntrueendret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="课程目录"},false)if ret ~= nil thenlog("点击_课程目录:findOne")nodeLib.click(ret)sleep(2000)returntrueendreturnfalse
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 thenlog("点击_观看下一节:node")nodeLib.click(ret)sleep(2000)returntrueendreturnfalse
end
function点击_播放按钮()if找色(67,516,70,528,"FFFFFF") thenlocal 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 thenlog("点击_播放按钮1")nodeLib.click(ret)sleep(2000)returntrueendendif找色(350,349,361,361,"FFFFFF") thenlocal ret = nodeLib.findOne(0,0,0,0,{class="android.widget.ImageView",id="com.yanxiu.gphone.faceshow:id/iv_do_play"},true)if ret ~= nil thenlog("点击_播放按钮:首次进入的播放")nodeLib.click(ret)sleep(2000)returntrueendendreturnfalse
end
function点击_知道了()local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="知道了"},false)if ret ~= nil thenlog("点击_知道了")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 thenlog("点击_首页任务:findOne")tap(359,1222)sleep(3000)returntrueend--174,1842 214BDDif找色(350-5,1227-5,350+5,1227+5,"214BDD") thenlog("点击_首页任务:找色")tap(359,1222)sleep(3000)returntrueendreturnfalse
end
function点击_点我继续计时()local ret = nodeLib.findOne(0,0,0,0,{class="android.widget.TextView",text="点我继续计时"},false)if ret ~= nil thenlog("点我继续计时")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 thenlog("提交")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 thenlog("确定...findOne")nodeLib.click(ret)--确定|取消sleep(2000)endlocal 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 thenlog("取消")nodeLib.click(ret)--确定|取消sleep(2000)endlocal 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 thenlog("确定...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 thenlog("点击重试")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 thenlog("关闭程序")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 thenlog("帐号掉线pwd:"..pwd)if #pwd>0 thenlocal ret = nodeLib.findOne(0,0,0,0,{class="android.widget.EditText",id="com.yanxiu.gphone.faceshow:id/et_password",text="请输入密码"},false)if ret ~= nil thentap(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 thenprint("我同意")tap(182,883)sleep(2000)end tap(359,793)sleep(6000)endreturntrueendendreturnfalse
end
--dm.lua
print("dm被调用了")functionlog(msg)--toast(msg)print(msg)console.println(3,msg)
end
function初始化()lockScreen()--Android 9:720*1280 320dpilocal 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,y0math.randomseed(os.time())local r = math.random(1,30)if r==3 thenlog("随机点击_防检测")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 thenrunApp(pkg)sleep(6000)end
end
function显示运行时长()local t1 =tickCount()/1000local h=t1//3600local m=(t1-(h*3600))//60local s=t1-(h*3600)-(m*60)h=math.floor(h)m=math.floor(m)s=math.floor(s)if h>0 thentoast("已运行:"..tostring(h).."时"..tostring(m).."分"..tostring(s).."秒")elseif m>0 thentoast("已运行:"..tostring(m).."分"..tostring(s).."秒")elsetoast("已运行:"..tostring(s).."秒")end
end
function找色(x1,y1,x2,y2,color)local x,y,retret,x,y=findColor(x1,y1,x2,y2,color.."-050505",0,0.95)if x~=-1 and y ~=-1 thenreturntrueendreturnfalse
end
function找色点击(x1,y1,x2,y2,color)local x,y,retret,x,y=findColor(x1,y1,x2,y2,color.."-050505",0,0.95)if x~=-1 and y ~=-1 thenlog("找色点击:ok..."..color..x..","..y)tap(x,y)sleep(1000)returntrueendreturnfalse
end
function等待颜色出现(x1,y1,x2,y2,color,t)local x,y,retfor 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 thenlog("等待颜色出现:ok..."..color)returntrueendendlog("等待颜色出现:超时..."..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 thenusername=vendif k=="password" and #v>0 thenpassword=vendif k=="kami" and #v>0 then卡密=vendendprint("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 thenreturnfalseendDDMControl.云控_连接云控系统()
end
-------------------------------------------------------------------卡密登录
function心跳回调(msg)print(msg)if msg.code ==1 thenprint("心跳成功")toast("有效期到:"..msg.endTime)sleep(2000)心跳失败次数 =0elseif msg.code ==-5 thenprint("请重新登录,一般是卡密被禁用,删除,设备被解绑!")exitScript()elseif msg.code ==-8 thentoast("卡密到期")sleep(2000)exitScript()elseif msg.code ==-9999 thenprint("心跳失败,网络错误!")心跳失败次数 = 心跳失败次数+1if 心跳失败次数 >=10 then --心跳连续失败10次,则停止运行exitScript()endelseif msg.code ==-6666 thenprint("有人尝试破解卡密系统!",msg.cdkey)exitScript()elseprint("未知错误!",msg.msg)exitScript()end
end
functionDDMC_卡密登录(卡密)云项目_初始化()print("脚本开始函数")local res = DDMControl.卡密_卡密登录(项目UUID,项目密钥,卡密,心跳回调,true,false,60)print(res)if res.code ==0 thentoast("卡密被禁用")sleep(2000)exitScript()elseif res.code ==-1 thentoast("网络错误,请检查网络!")sleep(2000)exitScript()elseif res.code ==1 thenprint("卡密登录成功!")elseif res.code ==-9 then toast("卡密授权窗口达到上限,登录失败!")sleep(2000)exitScript()elseif res.code ==-7 thentoast("卡密过期!")sleep(2000)exitScript()elseif res.code ==-6666 thenprint("有人尝试破解卡密系统!",res.cdkey)exitScript()elseprint("未知错误!",res.msg)toast(res.msg)sleep(2000)exitScript()endprint("res:"..res.msg)return res.code
end
-------------------------------------------------------------------检测更新
function显示更新内容(更新时间,更新内容)local 布局名称 ="DDMControlDataupdate"local 循环 =truefunction立即更新()--UpdateTimelocal path =getSdPath().."/version.txt"writeFile(path,UpdateTime)print("立即更新")toast("立即更新",0,0,12)ui.dismiss(布局名称)whiletruedolocal res = DDMControl.热更新_下载Lr且更新()print("res:"..res)if res.code ~=-9999 thenbreakendend循环 =falseprint("更新完成version:"..UpdateTime)endfunction取消()ui.dismiss(布局名称)循环 =falseendui.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 thenprint("更新内容:"..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 thenprint(res.UpdateInfo.UpdateTime)UpdateTime=res.UpdateInfo.UpdateTime显示更新内容(res.UpdateInfo.UpdateTime,res.UpdateInfo.UpdateLog)returntrueelseprint("无需更新")endelseif res.code ==-1 thenprint(res.msg)elseif res.code ==0 thenprint(res.msg)elseprint(res.msg)end
end
functionDDMC_初始化_直接更新()云项目_初始化()local res = DDMControl.热更新_检测更新并直接更新()if res.code ~=-9999 thenprint("热更新_检测更新并直接更新:"..res.code)end
end
-------------------------------------------------------------------云数据
--云项目_初始化()