最近一直在研究冰狐智能辅助,上次利用冰狐智能辅助实现了一个免费的个人收款系统,用起来还行,说实话刚开始我有怀疑过平台的稳定性,现在看来是有点多虑了。最近看官网更新了一个针对菜鸟设计的通过配置直接生成脚本功能「自动构建」,个人觉得很好奇,有点高大上,于是利用约会的时间研究了一把。用「自动构建」,没写一行代码,实现了一个薅羊毛程序(纯属个人兴趣研究,禁止用于任何商业用途),现把该薅羊毛程序作为例子进行讲解,分享菜鸟(不会编程的同学)是如何快速实现辅助功能的。

准备工作

  1. 手机端安装「冰狐智能辅助」,详细安装过程请看这里。
  2. 手机端安装「爱奇艺极速版」v1.10.6,注意,必须安装这个版本,不同的版本操作界面可能不同,可能会导致辅助功能失效。
  3. 注册「爱奇艺极速版」用户并登陆。

制作辅助功能

  1. 进入「移动端」/「自动构建」,新建名为”爱奇艺极速版“的自动构建。
  2. 点击“配置源码”按钮,将如下json数据粘贴进去,保存。
{
	"type": "root",
	"children": [{
		"type": "declareVar",
		"var": [{
			"name": "ret",
			"type": "normal",
			"initType": "null",
			"initValue": "null"
		}, {
			"name": "view",
			"type": "normal",
			"initType": "null",
			"initValue": "null"
		}, {
			"name": "viewContainer",
			"type": "normal",
			"initType": "null",
			"initValue": "null"
		}, {
			"name": "item",
			"type": "normal",
			"initType": "null",
			"initValue": "null"
		}],
		"desc": "变量声明"
	}, {
		"type": "enterApp",
		"desc": "进入APP",
		"packageName": "com.qiyi.video.lite",
		"findTag": "txt:首页-",
		"autoInstall": false,
		"autoUpdate": false,
		"wait": "1000",
		"failed": "exitPopup",
		"maxStep": "30",
		"children": [{
			"type": "clickAndEnter",
			"desc": "进入赚钱任务频道",
			"class": "1",
			"checkTag": "txt:活动规则-",
			"click": "true",
			"failed": "exitPopupWithAD",
			"descendant": [],
			"duration": "500",
			"maxStep": "8",
			"beforeWait": "0",
			"afterWait": "0",
			"children": [{
				"type": "log",
				"desc": "日志",
				"log_type": "log",
				"info": ["string-进入任务"],
				"logType": "log"
			}, {
				"type": "searchAndLoopDeal",
				"desc": "赚钱任务",
				"root": "",
				"retryCount": "0",
				"failed": "exitPopupWithAD",
				"duration": "500",
				"maxStep": "8",
				"beforeWait": "0",
				"afterWait": "0",
				"dealItems": [{
					"key": "txt^:免费抽大奖$&1",
					"value": {
						"type": "clickAndEnter",
						"desc": "进入大转盘",
						"class": "2",
						"checkTag": "txt:幸运大转盘-",
						"click": "true",
						"failed": "exitPopupWithAD",
						"descendant": [],
						"duration": "500",
						"maxStep": "5",
						"beforeWait": "0",
						"afterWait": "3000",
						"target": "view",
						"children": [{
							"type": "searchAndLoopDeal",
							"desc": "抽奖",
							"root": "",
							"failed": "exitPopupWithAD",
							"duration": "500",
							"maxStep": "8",
							"beforeWait": "0",
							"afterWait": "0",
							"dealItems": [{
								"key": "txt^:恭喜到账-traverse_one_by_one$&30",
								"value": {
									"type": "traverse",
									"desc": "退出到账",
									"class": "2",
									"descendant": [-1, -1],
									"startIndex": "0",
									"lastIndex": "0",
									"target": "view",
									"children": [{
										"type": "if",
										"desc": "条件处理",
										"expr": "item.clickable",
										"children": [{
											"type": "click",
											"desc": "点击控件",
											"class": "2",
											"click": "false",
											"descendant": [],
											"beforeWait": "0",
											"afterWait": "3000",
											"target": "item",
											"mode": "2"
										}, {
											"type": "break",
											"desc": "跳出循环",
											"class": 1
										}]
									}]
								}
							}, {
								"key": "txt$:后再来-$&1",
								"value": {
									"type": "break",
									"desc": "跳出循环",
									"class": 1
								}
							}, {
								"key": "txt^:lottery-list-$&20",
								"value": {
									"type": "custom",
									"desc": "抽奖",
									"code": "var txt = view.parent.parent.parent[2][0].text;\nconsole.log('txt:' + txt);\nvar num = parseInt(txt);\nif (num > 0) {\ngestureClick((view.left + view.right) / 2, (view.top + view.bottom) / 2, {afterWait: 8000});\nconsole.log('点击开始抽奖');\n} else {\nconsole.log('抽奖结束');\nbreak;\n}"
								}
							}],
							"retryCount": "0"
						}, {
							"type": "back2Page",
							"desc": "返回页面",
							"returnVar": "",
							"tag": "txt:我的-",
							"root": "",
							"backFirst": "true",
							"failed": "exitPopupWithAD",
							"duration": "500",
							"maxStep": "5",
							"beforeWait": "0",
							"afterWait": "0"
						}],
						"mode": "2"
					}
				}, {
					"key": "txt$:每日可领|txt:开宝箱领金币-$&1",
					"value": {
						"type": "click",
						"desc": "点击控件",
						"class": "2",
						"click": "true",
						"descendant": [],
						"beforeWait": "0",
						"afterWait": "3000",
						"target": "view",
						"mode": "2"
					}
				}, {
					"key": "txt^:再赚-$&10",
					"value": {
						"type": "click",
						"desc": "点击控件",
						"class": "2",
						"mode": "1",
						"descendant": [],
						"beforeWait": "0",
						"afterWait": "3000",
						"target": "view"
					}
				}],
				"false": {
					"type": "scroll",
					"desc": "滚动处理",
					"class": 1,
					"direction": "up",
					"minDistance": "0.98",
					"maxDistance": "0.98",
					"minDuration": "500",
					"maxDuration": "500",
					"beforeWait": "0",
					"afterWait": "0"
				}
			}, {
				"type": "back2Page",
				"desc": "返回页面",
				"returnVar": "",
				"tag": "txt:首页-",
				"root": "",
				"backFirst": "false",
				"failed": "exitPopupWithAD",
				"duration": "500",
				"maxStep": "5",
				"beforeWait": "0",
				"afterWait": "0"
			}, {
				"type": "log",
				"desc": "日志",
				"log_type": "log",
				"info": ["string-任务处理完成"]
			}],
			"false": {
				"type": "log",
				"desc": "日志",
				"log_type": "error",
				"info": ["string-进入任务失败"]
			},
			"mode": "2",
			"clickTag": "txt:赚钱-",
			"root": ""
		}, {
			"type": "clickAndEnter",
			"desc": "进入电视剧频道",
			"class": 1,
			"clickTag": "txt:首页-",
			"root": "",
			"checkTag": "txt:电视剧-",
			"click": "true",
			"failed": "exitPopupWithAD",
			"descendant": [],
			"duration": "500",
			"maxStep": "8",
			"beforeWait": "0",
			"afterWait": "0",
			"children": [{
				"type": "log",
				"desc": "日志",
				"log_type": "log",
				"info": ["string-进入看电视剧"]
			}, {
				"type": "loop",
				"desc": "在一段时间内观看多个电视剧",
				"class": 1,
				"minRunTime": "0.8",
				"maxRunTime": "1",
				"children": [{
					"type": "scroll",
					"desc": "滚动容器寻找喜欢的电视剧",
					"class": 1,
					"direction": "up",
					"minDistance": "0.98",
					"maxDistance": "0.98",
					"minDuration": "500",
					"maxDuration": "500",
					"beforeWait": "2000",
					"afterWait": "0"
				}, {
					"type": "traverse",
					"desc": "遍历电视剧容器",
					"class": "1",
					"tag": "cn:androidx.recyclerview.widget.RecyclerView-",
					"failed": "exitPopupWithAD",
					"descendant": [],
					"startIndex": "0",
					"lastIndex": "0",
					"retryCount": "3",
					"randomPick": true,
					"children": [{
						"type": "clickAndEnter",
						"desc": "点击进入电视剧",
						"class": "2",
						"checkTag": "txt*:/|txt:金蛋奖励-",
						"click": "true",
						"failed": "exitPopup",
						"descendant": [],
						"duration": "500",
						"maxStep": "5",
						"beforeWait": "0",
						"afterWait": "0",
						"target": "item",
						"children": [{
							"type": "loop",
							"desc": "看电视剧",
							"class": 1,
							"minRunTime": "0.2",
							"maxRunTime": "1",
							"children": [{
								"type": "click",
								"desc": "关闭广告",
								"class": 1,
								"tag": "txt:关闭此广告-",
								"root": "",
								"failed": "exitPopup",
								"maxStep": "5",
								"click": "true",
								"descendant": [],
								"duration": "500",
								"beforeWait": "0",
								"afterWait": "0",
								"mode": "2"
							}, {
								"type": "break",
								"desc": "若电视结束就跳出循环",
								"class": "2",
								"breakTag": "txt:完成-",
								"failed": "exitPopup"
							}]
						}, {
							"type": "log",
							"desc": "日志",
							"log_type": "log",
							"info": ["string-开始back到首页"]
						}, {
							"type": "back2Page",
							"desc": "返回首页",
							"returnVar": "",
							"tag": "txt:首页-",
							"root": "",
							"backFirst": "true",
							"failed": "exitPopup",
							"duration": "500",
							"maxStep": "5",
							"beforeWait": "0",
							"afterWait": "0"
						}],
						"false": {
							"type": "log",
							"desc": "日志",
							"log_type": "log",
							"info": ["string-进入电视剧失败"]
						},
						"mode": "1"
					}]
				}, {
					"type": "click",
					"desc": "点击领金币",
					"class": 1,
					"tag": "txt:领金币|txt^:领-",
					"root": "",
					"failed": "",
					"maxStep": "5",
					"mode": "2",
					"descendant": [],
					"duration": "500",
					"beforeWait": "0",
					"afterWait": "0"
				}]
			}],
			"false": {
				"type": "log",
				"desc": "日志",
				"log_type": "error",
				"info": ["string-进入电视剧频道失败"]
			},
			"mode": "2"
		}, {
			"type": "clickAndEnter",
			"desc": "进入短视频频道",
			"class": 1,
			"clickTag": "txt:短视频-",
			"root": "",
			"checkTag": "txt:推荐-",
			"click": "true",
			"failed": "exitPopupWithAD",
			"descendant": [],
			"duration": "500",
			"maxStep": "8",
			"beforeWait": "0",
			"afterWait": "0",
			"children": [{
				"type": "log",
				"desc": "日志",
				"log_type": "log",
				"info": ["string-进入短视频"]
			}, {
				"type": "loop",
				"desc": "观看多个视频",
				"class": "2",
				"minCount": "5",
				"maxCount": "10",
				"children": [{
					"type": "loop",
					"desc": "观看短视频",
					"class": 1,
					"minRunTime": "0.1",
					"maxRunTime": "0.6"
				}, {
					"type": "scroll",
					"desc": "滚动屏幕看下一个短视频",
					"class": 1,
					"direction": "up",
					"minDistance": "0.98",
					"maxDistance": "0.98",
					"minDuration": "500",
					"maxDuration": "500",
					"beforeWait": "0",
					"afterWait": "0"
				}]
			}],
			"false": {
				"type": "log",
				"desc": "日志",
				"log_type": "error",
				"info": ["string-进入短视频失败"]
			},
			"mode": "2"
		}]
	}, {
		"type": "popup",
		"desc": "弹窗处理",
		"name": "exitPopupWithAD",
		"false": ["exitPopup", "dealAD"]
	}, {
		"type": "popup",
		"desc": "弹窗处理",
		"name": "exitPopup",
		"false": [],
		"dealItems": [{
			"key": "txt:暂不升级|txt:我知道了-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}, {
			"key": "txt:猜你想看-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": 1,
				"tag": "txt:取消-",
				"root": "",
				"failed": "",
				"maxStep": "5",
				"click": "true",
				"descendant": [],
				"duration": "500",
				"beforeWait": "0",
				"afterWait": "0",
				"mode": "2"
			}
		}, {
			"key": "txt:恭喜获得-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [-1, 4],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}, {
			"key": "txt^:恭喜到账-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [-1, 2, 0],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}, {
			"key": "txt:继续赚钱-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [-1, -1, -1, 1],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}, {
			"key": "txt^:打开日历提醒-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "false",
				"descendant": [-1, 7],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}]
	}, {
		"type": "ad",
		"desc": "广告处理",
		"enterWindowList": [],
		"enterTags": "txt*:s后可领取奖励-",
		"breakTags": "txt:开宝箱|txt:我的|txt^:再赚|txt:继续赚钱|txt:幸运大转盘-",
		"dealItems": [{
			"key": "txt:关闭-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "3"
			}
		}, {
			"key": "txt:恭喜获得-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": "2",
				"click": "true",
				"descendant": [-1, 4],
				"beforeWait": "0",
				"afterWait": "0",
				"target": "view",
				"mode": "2"
			}
		}, {
			"key": "txt:继续观看视频可获得奖励-",
			"value": {
				"type": "click",
				"desc": "点击控件",
				"class": 1,
				"tag": "txt:继续观看-",
				"root": "",
				"failed": "",
				"maxStep": "5",
				"click": "true",
				"descendant": [],
				"duration": "500",
				"beforeWait": "0",
				"afterWait": "0",
				"mode": "2"
			}
		}, {
			"key": "cn:android.widget.ImageView-find_all|clickable",
			"value": {
				"type": "traverse",
				"desc": "遍历容器",
				"class": "2",
				"descendant": [],
				"startIndex": "0",
				"lastIndex": "0",
				"target": "viewContainer",
				"children": [{
					"type": "custom",
					"desc": "自定义模块,直接写入代码",
					"code": "if (item.width == item.height && item.width < 80 && item.width > 40 && item.top < 100 && item.right > (rsScreenWidth - 20)) {\nclick(item, {click: true});\nconsole.log('click x');\nbreak;\n}"
				}]
			}
		}]
	}, {
		"type": "windowChange",
		"desc": "窗口变化处理",
		"dealItems": [{
			"key": "com.mcto.sspsdk.ssp.activity.QyDetailPageActivityNew",
			"value": {
				"type": "back",
				"desc": "按返回键",
				"count": "1",
				"duration": "1000",
				"beforeWait": "0",
				"afterWait": "0"
			}
		}]
	}, {
		"type": "notification",
		"children": []
	}],
	"desc": "根模块"
}
  1. 点击“编译”按钮,把构建编译成js代码。
  2. 进入「移动端」/「移动端脚本」,系统会自动创建一个“爱奇艺极速版”js脚本,点击“发布”按钮,发布脚本。
  3. 点击“运行”按钮,选择设备,执行脚本,一个自动薅羊毛的辅助程序就完成啦😊。

原理分析

看到这,其实最核心的就是如何得到配置json数据。下面我们一起来看下我是如何配置的:

进入「移动端」/「自动构建」,选择“爱奇艺极速版”,然后点击“配置构建”按钮。这时候弹出一个配置界面,如下图所示:

android 辅助功能 获取读屏内容 android辅助功能开发_apache

 配置由模块组成,每个模块由模块属性和子模块组成。一般的模块会直接执行子模块,有的带条件的模块,当条件为true或者执行结果为true时才执行子模块,注意:子模块是按按顺序执行。整个配置其实就是一个树形结构,上图就是根模块。下面来介绍下每个模块作用:

变量声明

        声明一些后面模块要用的通用变量,系统会自动生成几个常用的变量,个人感觉绝大部份情况下系统默认生成的就够用了😄。点击“编辑”按钮,再点击“查看/编辑变量声明”,显示如下变量:

android 辅助功能 获取读屏内容 android辅助功能开发_apache_02

进入APP

        如果打算启动和处理APP,必须添加这个模块。点击该模块的“编辑”按钮,属性和子模块如下图所示:

android 辅助功能 获取读屏内容 android辅助功能开发_android 辅助功能 获取读屏内容_03

「每个属性行的最后都有一个说明,鼠标移动上去会自动显示详细说明。」

属性如下:

  1. packageName,APP的包名
  2. findTag,这个是用来检测app是否已经启动。app启动了,首页中如果出现该tag,则表示启动成功。
  3. false,启动app失败后会执行该模块,可以在这里打个log😯。
  4. failed,用来处理随机弹窗或者广告问题,如果系统随机弹出什么升级啊等窗口时,这个可以自动处理🐂。
  5. maxStep,检测是否启动,最多find几次tag,默认好像是30次。

成功启动app后,会自动按顺序执行子模块:

  1. 打log,用于调试吧。
  2. 切换到任务频道,做任务。
  3. 切换到电视剧频道,看电视剧。
  4. 切换到短视频频道,看短视频。

处理弹窗

        用于处理app随机弹出的各种窗口,比如:升级、青少年模式、处理成功等等对话框。

android 辅助功能 获取读屏内容 android辅助功能开发_控件_04

 属性如下:

  1. false,模块数组。当无法处理弹窗时,会按顺序执行模块,如果模块返回true则返回,否则继续执行下一个直到返回true为止。
  2. 处理项:是tag-module对,每个tag对应弹出框中的某个控件全局唯一tag,moudule为对应的处理模块。

处理弹窗和广告

        用于即有可能出现弹窗也有可能出现广告的场景。false属性值["exitPopup","dealAD"],表示先处理弹窗,如果失败,则看看是否是广告,处理广告。

android 辅助功能 获取读屏内容 android辅助功能开发_控件_05

处理广告

        处理弹出广告的情况,当广告完成后自动退出广告。广告不会自动触发,一般是在点击某个按钮后弹出广告。

android 辅助功能 获取读屏内容 android辅助功能开发_apache_06

 属性如下:

  1. enterWindowList,广告窗口className的数组,当出现其中一个窗口时,表示进入广告。
  2. enterTags,广告窗口中某个标识性控件的tag数组,当出现其中一个tag时,表示进入了广告。
  3. breakTags,广告退出后显示页面的标志性tag数组,当出现其中一个tag时,表示广告已经退出。
  4. 处理项:是tag-module对,退出处理,当出现点击退出广告的按钮tag时,点击按钮(module)。

窗口变化处理

        可以用于处理比如。当窗口改变后,系统会自动触发该模块。

通知处理

        用于处理各种通知,比如微信消息,短信等等。当出现通知时,系统会自动触发该模块。