領(lǐng)喵幣
領(lǐng)喵幣又開始了,使用Android的輔助功能可以實(shí)現(xiàn)自動(dòng)領(lǐng)喵幣的功能;具體的效果可以下載下面的apk體驗(yàn)
Apk地址:https://github.com/tyhjh/TmallCoin/raw/master/%E5%8F%AF%E8%BF%90%E8%A1%8Capk/%E9%A2%86%E5%96%B5%E5%B8%81.apk
備用地址:http://share.tyhjh.com/share/TmallCoin.apk
功能
要做的功能就是自動(dòng)點(diǎn)擊按鈕,完成這些只需要等待15秒就可以完成的任務(wù);去年領(lǐng)喵幣還比較簡(jiǎn)單,按鈕的位置和功能都是固定的,只需要截圖,然后判斷固定位置的按鈕的顏色是紅色還是已完成的灰色,就可以點(diǎn)擊按鈕去另一個(gè)界面等待15秒后再返回,缺點(diǎn)就是只能適配一個(gè)分辨率的手機(jī),并且今年按鈕的位置不是固定的,我們需要區(qū)分哪些是可以通過腳本完成的,哪些是通過簡(jiǎn)單的腳本無法完成的
思路
具體的思路是后臺(tái)開啟服務(wù)(AccessibilityService),提供一個(gè)懸浮窗按鈕,進(jìn)入到領(lǐng)喵幣中心以后,點(diǎn)擊按鈕開始對(duì)屏幕進(jìn)行截屏;通過圖像識(shí)別識(shí)別出按鈕上的文字及位置,通過文字判斷當(dāng)前的任務(wù)是否是簡(jiǎn)單瀏覽就可以完成的,比如去瀏覽、去逛逛,去搜索等為簡(jiǎn)單操作,使用Android輔助功能進(jìn)行模擬點(diǎn)擊該位置,然后等待15秒后即可完成,然后再返回繼續(xù)進(jìn)行識(shí)別;但是比如去完成這樣的按鈕一般都是比較復(fù)雜的,難以通過腳本完成,需要避開;但是也可能去完成按鈕的任務(wù)也是一個(gè)比較簡(jiǎn)單的瀏覽的任務(wù),就需要一些技巧去判斷;
Android輔助功能建議閱讀:http://www.itdecent.cn/p/8935bde74c50
具體實(shí)現(xiàn)
在輔助服務(wù)(AccessibilityService)里面,開啟線程循環(huán)截圖進(jìn)行圖像識(shí)別,識(shí)別出所有的文字及位置;OcrResult類為識(shí)別出來的結(jié)果,包含文字結(jié)果和所在位置;文字識(shí)別使用的框架是PaddleOCR,也可以通過jitpack庫(kù)進(jìn)行引入這個(gè)封裝的庫(kù),可以看實(shí)現(xiàn)的源碼:https://github.com/tyhjh/WordsFind
implementation 'com.github.tyhjh:WordsFind:v1.0.3'
第一次識(shí)別會(huì)對(duì)屏幕右方按鈕部分的圖像進(jìn)行裁剪和識(shí)別,可以識(shí)別出大多數(shù)簡(jiǎn)單的操作由腳本去執(zhí)行;但是有部分操作按鈕顯示為去完成,實(shí)際也是瀏覽任務(wù)的,在按鈕的左側(cè)有文字進(jìn)行說明一般也是帶有瀏覽、逛逛等文字,就需要在按鈕部分識(shí)別完成并且未找到可執(zhí)行的任務(wù)時(shí),再次對(duì)全圖進(jìn)行查找,如果查找到匹配的文字就進(jìn)行點(diǎn)擊;
//獲取屏幕截屏
Bitmap bitmap = ScreenShotUtil.getInstance().getScreenShot();
//裁剪出識(shí)別區(qū)域,只識(shí)別按鈕
Bitmap wordsBitmap = Bitmap.createBitmap(bitmap, startX, startY, (int) ((1 - START_X_SCALE) * width), (int) ((1 - START_Y_SCALE) * height));
//獲取文字所在的區(qū)域
List<OcrResult> rectList = WordsFindManager.getInstance().runModel(wordsBitmap);
//遍歷文字找到按鈕進(jìn)行點(diǎn)擊
boolean isFindTxt = findBtn(rectList);
//還是沒有可以點(diǎn)擊的文字,識(shí)別全圖
if (!isFindTxt) {
//獲取文字所在的區(qū)域
rectList = WordsFindManager.getInstance().runModel(bitmap);
//繼續(xù)遍歷文字進(jìn)行點(diǎn)擊
boolean notFinished = findBtn(rectList);
}
findBtn方法就是通過識(shí)別到的文字判斷該任務(wù)是否可以執(zhí)行,如果可以執(zhí)行就進(jìn)行點(diǎn)擊;
private static final List<String> btnTexts = Arrays.asList(new String[]{"去瀏覽", "去逛逛", "去搜索", "去觀看"});
private boolean findBtn(List<OcrResult> rectList) {
//識(shí)別到去瀏覽的按鈕
for (OcrResult result : rectList) {
//獲取識(shí)別的文字
String txt = result.getTxt();
//如果文字為可數(shù)組里面的文字,表示可以點(diǎn)擊
if (btnTexts.contains(txt)) {
//獲取喵幣
getCatCoin(startX, startY, result);
return true;
}
//任務(wù)描述的文字處理,有字代表任務(wù)可以點(diǎn)
if (txt.contains("瀏覽") || txt.contains("逛一逛")) {
//判斷該任務(wù)是不是已完成的任務(wù)
if (notFinished(result)) {
//獲取喵幣
getCatCoin(startX, startY, result);
return true;
}
}
}
return false;
}
獲取喵幣的代碼還是比較簡(jiǎn)單的,就是點(diǎn)擊按鈕進(jìn)入瀏覽的界面,然后等15秒就返回;因?yàn)樘詫氻?yè)面加載等原因,等待的時(shí)間大于15秒才能完成任務(wù)
//點(diǎn)擊去瀏覽
clickOnScreen(rect.left + startX, rect.top + startY, 10, null);
//等待頁(yè)面加載3秒+瀏覽18秒
SystemClock.sleep(22 * 1000);
//返回
performBackClick();
還有個(gè)問題如果文字不是在按鈕上識(shí)別出來的,比如任務(wù)的描述文字包含瀏覽,但是其實(shí)這個(gè)任務(wù)其實(shí)已經(jīng)完成了,如果識(shí)別不到這種情況就會(huì)一直點(diǎn)擊該任務(wù),所以會(huì)保存識(shí)別出的已完成文字位置,通過對(duì)比識(shí)別出來的描述文字瀏覽和已完成文字的Y坐標(biāo),判斷是不是同一個(gè)任務(wù),判斷該任務(wù)是否已經(jīng)完成
for (Rect rect : rectListFinished) {
//計(jì)算兩個(gè)文字的頂部的距離
int value = Math.abs(rect.top - result.getRect().top);
//如果大于70像素,判斷不是一個(gè)任務(wù),該任務(wù)未被執(zhí)行過
if (value < 70) {
return false;
}
}
其中錄屏截圖框架使用的是:https://github.com/tyhjh/ScreenShot,也可以通過jitpack庫(kù)進(jìn)行引入
implementation 'com.github.tyhjh:ScreenShot:v1.0.2'
思路還是比較清晰的,圖像識(shí)別找可以瀏覽完成的任務(wù),定時(shí)瀏覽完成任務(wù);代碼也比較的簡(jiǎn)單,示例代碼有所刪減,詳細(xì)的實(shí)現(xiàn)可以看源碼;
項(xiàng)目地址:https://github.com/tyhjh/TmallCoin