小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
想要集喵糖,又不想看广告。想着写个脚本机器人协助一下,想起当年微信跳一跳的闯关脚本,这个简单版的机器人应该是可以实现的。
刷店铺集喵糖的操作很机械,分为几步:
- 点击“赚糖领红包”按钮,展开任务面板
- 点击面板中的“去浏览”按钮,开始看广告
- 广告时间 15 ~ 20 秒
- 喵糖到手,返回上一级,重新进入第 2 步
也就是说只要能确定按钮在哪,能触发点击,能返回上一级,这件事就能成。
实现思路:
adb只想手机截屏上传图片到 PC 端- PC 端进行图像识别,确定按钮位置
adb执行触屏操作
连接手机和 PC
PC 端安装 adb
brew install --cask android-platform-tools
检测安装成功与否
adb version
手机端开启调试,一般是关于手机 -> 版本号 -> 连击 N 次,开启开发者选项后,在其他设置找到入口打开 USB 调试。
Android 11 以上还可以开启无线调试模式:
- 开发者选项 -> 无线调试
- 使用配对码配对设备
- PC 端
adb pair ipaddr:port,输入配对码配对 - 配对成功后,PC 端
adb connect ipaddr:port连接成功
更具体的操作见 官方文档
adb 截屏并上传到 PC 端
import { promisify } from "util";
import { exec as nodeExec } from "child_process";
const exec = promisify(nodeExec);
async function pullCurrentScreenShot() {
// 截屏
await exec("adb shell screencap /sdcard/meow_candy.png");
// 上传
await exec("adb pull /sdcard/meow_candy.png ./images");
}
图片识别确定按钮位置
从图片中识别元素有几种方案:
- 如果元素有固定的像素分布特征,通过像素匹配得到位置
- 如果元素中包含特征文本,通过文本识别得到位置
- opencv 准确识别元素整体
简单尝试 tesseract 之后,排除文本识别。由于活动页面元素过多,并且字体并不统一,OCR 识别准确率很低。opencv 涉及图像处理算法,上手折腾需要一定时间。实现基本需求为先,尝试使用像素匹配简单实现。
像素匹配思路:
- 确定点击目标的图像,截图保存下来。
- 获取源图片和目标的像素矩阵
- 基于像素矩阵匹配查找
匹配时做全图矩阵对比消耗太大,做一丢丢优化。
由于精度要求不高,这里先对全图做灰度化,再取目标图片的四个端点构成矩形框扫描匹配,最后计算原图匹配到的区域和目标图片的像素差。
图像处理使用 Jimp 来实现。
import Jimp from "jimp";
type TapTargetType = [number, number];
async function recognizeTapTarget(
srcImgPath: string,
targetImgPath: string
): Promise<TapTargetType | null> {
// 准备目标图像端点数据
const targetImage = await Jimp.read(targetImgPath);
targetImage.greyscale();
const tWidth = targetImage.bitmap.width;
const tHeight = targetImage.bitmap.height;
const rect = [
targetImage.getPixelColor(0, 0),
targetImage.getPixelColor(tWidth, 0),
targetImage.getPixelColor(tWidth, tHeight),
targetImage.getPixelColor(0, tHeight),
];
// 读取当前的截屏图片
const image = await Jimp.read(srcImgPath);
image.greyscale();
const sWidth = image.bitmap.width;
const sHeight = image.bitmap.height;
// 开始从左上角开始扫描
let matchedPoint: TapTargetType | null = null;
for (const { x, y } of image.scanIterator(
0,
0,
sWidth - tWidth,
sHeight - tHeight
)) {
// 前四项是端点对比,通过层层验证后再进行像素差计算
if (
image.getPixelColor(x, y) === rect[0] &&
image.getPixelColor(x + tWidth, y + tHeight) === rect[3] &&
image.getPixelColor(x + tWidth, y) === rect[1] &&
image.getPixelColor(x, y + tHeight) === rect[2] &&
Jimp.diff(image.clone().crop(x, y, tWidth, tHeight), targetImage, 0.4).percent <
0.15
) {
matchedPoint = [x, y];
break;
}
}
return matchedPoint;
}
adb 触屏操作
查看 adb 输入命令的说明
adb shell input --help
点击某点
adb shell input tap <x> <y>
滑动屏幕
adb shell input swipe <x1> <y1> <x2> <y2> [duration]
回到上级
adb shell input keyevent 4
总结
简单实现,满足基本需求,点到为止。
完整代码:curly210102/meow-candy-collector: Cute meow candy automatic collector for 11.11 (github.com)