Android微信搶紅包輔助

閱讀原文:http://www.lxweimin.com/p/5a44b6eaba20

快到年底了,又到了拼手速搶紅包的時候了;其實很早之前就做過搶紅包軟件了,包括QQ和微信;但是大家都懂的,自己一個月前寫的代碼現在看起來都像是一坨shit一樣;所以自己開始重新寫一個搶紅包的軟件(其實是因為實在是太簡單了),只做微信,因為QQ發紅包的確用的太少了,而且QQ紅包花樣也太多了,什么唱歌、畫畫、成語接龍...

luckCat.png-97.5kB

目標

  1. 快,天下武功無堅不摧、唯快不破,肯定要比人的手速快
  2. 準,只要你手機解鎖了,在任意一個界面都可以快速搶到紅包
  3. 狠,其實狠不狠沒什么關系了,最重要的是全自動,自己不用任何操作,不然怎么解放雙手
  4. 穩,肯定要能一直搶紅包,來一個搶一個,來兩個搶兩個,搶紅包一時爽,一直搶一直爽;

手機配置要求

  1. Android系統 7.0及以上,輔助功能7.0以上支持模擬點擊,模擬點擊不是必須的,但是對于實現很重要
  2. 手機不能太垃圾了,手機慢有外掛也發揮不出來呀

實現原理

實現方法就是利用Android輔助功能,開啟輔助功能相當于開啟了一個服務,在手機界面改變的時候,就能監聽到該頁面的一些信息并且能拿到界面的一些控件,然后可以對控件進行模擬點擊,從而實現我們想要的功能。

除此以外,不僅能夠對獲取到的控件進行模擬點擊,在Android7.0及以上的版本,我們可以模擬任意位置的點擊包括觸摸、滑動等等,就是說我們可以實現任何人能夠進行的操作,這個是很有用的,可以做出很多有意思的東西,如果再配上截圖、錄屏和圖像識別,就更有意思了。

模擬點擊,就是說我們的手機界面自己動,整個流程像是一只手在幫你操作一樣的;其實我見過更牛逼的方法,連解鎖都不需要直接就領了紅包,界面沒有任何變化的;感覺上是通過通信,發數據給微信服務器實現的,當然這種是需要root權限的,并且得去解析微信的通信協議,我自然沒時間去搞(其實有時間也不一定能搞出來)。

具體實現

輔助功能

首先是輔助功能,新建一個Service繼承AccessibilityService

public class LuckMoneyService extends AccessibilityService

然后去AndroidManifest文件里面去注冊一下這個Service

    <service
            android:name=".service.LuckMoneyService"
            android:label="小圓臉的紅包助手"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessible_service_wx_config" />
        </service>

meta-data節點下有個resource值,這是個xml文件,里面配置了該輔助的一些信息,在res目錄下新建一個文件夾,名字叫xml,然后新建一個xml文件,名字和resource配置的一樣就行了

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeNotificationStateChanged"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews|flagReportViewIds"
    android:canRetrieveWindowContent="true"
    android:canRequestFilterKeyEvents="true"
    android:description="@string/wx_luck_money"
    android:canRequestEnhancedWebAccessibility="true"
    android:notificationTimeout="20"
    android:packageNames="com.tencent.mm"
    android:canPerformGestures="true" />

里面配置了一些參數,比如notificationTimeout是指定多少毫秒監聽一次界面變化的,packageNames是指定監聽哪個應用的,刪掉這個配置就是監聽全局,建議一定要刪除掉,我這里只是展示用,description是對于該輔助的描述,其他配置不管也罷。

然后在LuckMoneyService里面重寫一下onAccessibilityEvent方法

  @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    //界面發生了變化
    }

每當界面改變的時候就會回調這個方法,通過event我們就可以獲取到界面的信息包括界面上的控件

簡單的用法

//獲取當前界面包名
String packageName = event.getPackageName().toString();
//獲取當前類名
String className = event.getClassName().toString();
//獲取當前界面父布局的控件
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
//在父布局里面根據子控件**顯示的文字**找到該子控件
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
//在父布局里面根據子控件的**id**找到該子控件
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
//點擊該控件
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);

上面的操作都比較基礎,根據控件顯示的文字查找控件,找出來的肯定是TextView和Button了,根據ID查找控件,ID就是指的寫布局文件的時候設置的控件的ID

模擬觸摸

模擬觸摸就是可以模擬人的觸摸動作,也比較簡單

   protected void gestureOnScreen(Path path, long startTime, long duration,
                                   AccessibilityService.GestureResultCallback callback) {
        GestureDescription.Builder builde = new GestureDescription.Builder();
        builde.addStroke(new GestureDescription.StrokeDescription(path, startTime, duration));
        GestureDescription gestureDescription = builde.build();
        dispatchGesture(gestureDescription, callback, null);
    }

可以看到需要傳入path就是一個路徑嘛,模擬滑動的路徑,用canvas畫過畫的都知道這東西還是比較簡單的,不清楚也沒關系,繼續看,startTime就是多久后開始模擬事件,duration就是該滑動的時間,其他回調什么的為空就可以了;

輔助功能能做的東西大概就上面這些了,接下來看看

微信應用外的紅包處理

首先實現在微信界面外怎么搶紅包,在微信界面外有紅包出現必然會在通知欄會顯示微信紅包(如果沒開通知消息,那你自己開一下不就完事了嗎),只需要在回調方法里面判斷一下是不是通知消息,如果是通知消息,獲取里面的信息,判斷是不是微信紅包通知消息,是就點擊該消息,會自動跳轉到聊天界面;

因為我們是監聽界面變化來實現功能的,所以在一個界面觸發了界面變化的時候,接下來的處理就應該交給下一個界面的方法了,所以微信界面外的操作就是這些了

    /**
     * 紅包標識字段
     */
    public static final String HONG_BAO_TXT = "[微信紅包]";
    
    //通知欄消息,判斷是不是紅包消息
        if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
            Notification notification = (Notification) event.getParcelableData();
            //獲取通知消息詳情
            String content = notification.tickerText.toString();
            //解析消息
            String[] msg = content.split(":");
            String text = msg[1].trim();
            if (text.contains(HONG_BAO_TXT)) {
                PendingIntent pendingIntent = notification.contentIntent;
                try {
                    //點擊消息,進入聊天界面
                    pendingIntent.send();
                } catch (PendingIntent.CanceledException e) {
                    e.printStackTrace();
                }
            }
        }

其中PendingIntent這個東西寫過通知欄的都知道,這個是設置跳轉到哪個界面的,所以直接調用它的方法就完成了界面跳轉了

聊天界面的紅包處理

界面外的紅包點擊通知欄消息就來到了聊天界面,其實所有的界面都必須經過這個界面才能領取到紅包,所以這個界面很重要;

實現

思路是這樣的,聊天消息肯定是一個列表控件,其實是個ListView,而且肯定有控件ID,我們獲取到這個ListView,然后遍歷它的每個消息(只能遍歷到當前界面顯示的),判斷這個消息是不是微信紅包,如果是,并且未被領取,而且這個紅包還得是別人發的,不是自己發的,我們才去點擊這個消息,觸發界面變化,然后丟給下一個界面處理;

        //獲取聊天消息列表List控件
        AccessibilityNodeInfo nodeInfo = findViewByID(DETAIL_CHAT_LIST_ID);
        //這個消息列表不為空,那么肯定在聊天詳情頁
        if (nodeInfo != null) {
            //判斷有沒有未領取紅包并進行點擊
            clickItem(nodeInfo);
            return;
        }
        

    /**
     * 進行消息列表未領取紅包的點擊
     *
     * @param nodeInfo
     */
    private void clickItem(AccessibilityNodeInfo nodeInfo) {
        //遍歷消息列表的每個消息
        for (int i = 0; i < nodeInfo.getChildCount(); i++) {
            //獲取到子控件
            AccessibilityNodeInfo nodeInfoChild = nodeInfo.getChild(i);
            //獲取紅包控件
            AccessibilityNodeInfo target = findViewByID(nodeInfoChild, AUM_ID);
            //獲取頭像的控件
            AccessibilityNodeInfo avatar = findViewByID(nodeInfoChild, AVATAR_ID);
            boolean selfLuckMoney = false;
            //獲取頭像的位置,判斷紅包是否是自己發的,自己發的不搶
            if (avatar != null) {
                Rect rect = new Rect();
                avatar.getBoundsInScreen(rect);
                if (rect.left > screenWidth / 2) {
                    selfLuckMoney = true;
                }
            }
            //如果不是自己發的紅包,并且獲取到的微信紅包這個控件不為空
            if (target != null && !selfLuckMoney) {
                //已領取這個控件為空,紅包還沒有被領取
                if (findViewByID(nodeInfoChild, AUL_ID) == null) {
                    //點擊紅包控件
                    performViewClick(target);
                    return;
                }
            }
        }
    }

里面每個細節都注釋了,獲取ListView控件,獲取到了說明是在消息界面,獲取到消息列表的每一個控件,根據 是否是紅包消息,是否是別人發的,是否是未領取的三點,去判斷是否是可以領取的紅包,然后點擊可領取的紅包,到達彈出的這個彈窗的界面;

monitor

如何獲取這個ListView控件的ID呢,而我又是如何知道是ListView的呢,可以通過一個工具來實現,就是在sdk工具下面的一個叫monitor的工具,其實之前的AndroidStudio是帶這個工具的,但是后來界面上是沒有了,但是其實還在的

/Users/Tyhj/Library/Android/sdk/tools/monitor

連上手機,打開這個工具,手機上打開你要查看的界面,點擊工具手機的小手機的圖標,就會截屏,顯示出這個界面的信息


截屏2019-12-09上午1.00.04.png-752.2kB

紅包彈窗界面處理

截屏2019-12-09上午1.11.52.png-613.4kB

同樣的紅包彈窗這個界面也是必須經過的,十分重要;你要說這個彈窗界面也比較簡單,我們判斷一下是不是這個界面,然后點擊開不就完事兒了;測試可以發現,這個彈窗出現的時候,當前的界面className是這個

    **
     * 紅包彈出的class的名字
     */
    private static final String ACTIVITY_DIALOG_LUCKYMONEY = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI";

事情沒有這么容易,當我去獲取這個的這個控件的時候,發現為空,獲取不到,其實整個彈窗都獲取不到,遇到這個問題的人肯定不少;

//獲取開的控件布局
AccessibilityNodeInfo target = findViewByID("com.tencent.mm:id/dan");

其實深究下去,發現獲取根布局都為空了,測試發現必須等待一段時間再去獲取這個彈窗才行,但是等多久呢,大概幾百毫秒吧,不定時的,不同手機也不一定,那么隨便設一個就不行,因為你時間設置小了,程序可能會卡在這里搶不了紅包了,肯定不行;設置大了,行,但是影響速度呀。那么開個循環去獲取直到獲取到不為空行嗎?不行,奇怪的就是你一次去獲取為空了,之后獲取都為空了;只有等待一段時間后第一次去獲取才不為空,這TMD就很奇怪了,看了一下的確沒法解決;這個問題其實和手機有關,在三星s9上的確有問題,在華為nova5 pro上沒問題

//獲取根布局
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();

如果獲取不到,那其實還有個辦法就是模擬點擊,在紅包彈窗彈出來的時候,我瘋狂點擊這個開字的位置,就行了;開字的位置可以通過屏幕比例來計算出來,這個就算不同的手機屏幕都可以點擊到這個開字;但是其實還是有個問題,彈窗彈出其實有個動畫,不同的手機其實彈出的時間也不不一樣,華為nova5 pro都不用等待,可以直接執行點擊操作,三星s9得等待200ms左右

        //當前為紅包彈出窗(那個開的那個彈窗)
        if (className.equals(ACTIVITY_DIALOG_LUCKYMONEY)) {
            //進行紅包開點擊
            clickOpen();
            return;
        }
/**
     * 點擊開紅包按鈕
     */
    private void clickOpen() {
        //等待紅包彈窗完成,直接使用模擬點擊比較快,根據手機性能等待響應的時長
        SystemClock.sleep(100);
        for (int i = 0; i < 20; i++) {
            SystemClock.sleep(10);
            //計算了一下這個開字在屏幕中的位置,按照屏幕比例計算
            clickOnScreen(screenWidth / 2, screenHeight * POINT_Y_SCAL, 1, null);
        }

        /*AccessibilityNodeInfo target = findViewByID("com.tencent.mm:id/dan");
        if (target != null) {
            performViewClick(target);
            return;
        } else {
            //如果沒有找到按鈕,再進行模擬點擊
            for (int i = 0; i < 20; i++) {
                SystemClock.sleep(10);
                clickOnScreen(screenWidth / 2, screenHeight * POINT_Y_SCAL, 1, null);
            }
        }*/
    }

點擊了這個開字后,進入了紅包詳情頁,進行下一步處理。

紅包詳情頁處理

進入了紅包詳情頁,紅包已經到手了,想要繼續搶紅包,肯定需要退出去,這個簡單,有返回鍵的方法;這時候你可以返回聊天界面繼續搶這個群的紅包(如果專搶一個群的,這樣效率高),也可以返回到最近消息列表(微信主頁面第一個界面),可以搶其他群的紅包(搶其多個群的紅包,這樣效率高),也可以退回手機主界面(搶紅包效率低,因為還需要點擊通知欄消息進去);可以設置一下,如果開啟專搶一個群,就退回該群聊天界面,否則退回最近消息列表界面。

        //紅包領取后的詳情頁面,自動返回
        if (className.equals(LUCKY_MONEY_DETAIL)) {
            //返回聊天界面
            performGlobalAction(GLOBAL_ACTION_BACK);
            //如果不是專搶一個群
            if (!isSingle) {
                SystemClock.sleep(50);
                performGlobalAction(GLOBAL_ACTION_BACK);
            }
            return;
        }

最近消息列表界面處理

截屏2019-12-09上午2.01.36.png-541kB

當領完紅包后,退出到最近消息列表界面是比較好的選擇;這個界面上當收到紅包消息通知欄是不會有提醒的;我們需要根據界面的顯示去判斷有沒有紅包;其實也是特別簡單,它也是一個ListView,同樣的遍歷一下每個item,判斷有沒有微信紅包消息,然后點擊進入聊天消息界面

        //在最近聊天列表,檢測有沒有紅包消息出現
        nodeInfo = findViewByID(HUMAN_LIST);
        //聯系人列表
        if (nodeInfo != null) {
            //判斷最近聊天列表有沒有未領取紅包
            clickHumanItem(nodeInfo);
            return;
        }
        
    /**
     * 進行聯系人列表的紅包消息點擊
     *
     * @param nodeInfo
     */
    private void clickHumanItem(AccessibilityNodeInfo nodeInfo) {
        for (int i = 0; i < nodeInfo.getChildCount(); i++) {
            AccessibilityNodeInfo nodeInfoChild = nodeInfo.getChild(i);
            AccessibilityNodeInfo target = findViewByID(nodeInfoChild, HUMAN_LIST_TXT_ID);
            if (target != null && target.getText() != null && target.getText().toString().contains(HONG_BAO_TXT)) {
                performViewClick(target);
                return;
            }
        }
    }

看似沒有問題,實則有一個問題,就是在這個聊天列表里面,沒法判斷這個紅包是別人發的還是你自己發的,如果是你自己發的那肯定有問題的,這是一個坑,當然可以通過保存一些數據,比如說第一次進去后發現是自己發的紅包就退出來,如果界面沒變化第二次就不再進行點擊了;但是其實問題也不大吧,最多就是你發完紅包后自己再發個消息就可以避免了。

測試總結

其實到這里就全完成了,實際效果也不錯,測了一下,4個人和一個輔助比,發了20次紅包,輔助大概能搶到18次吧,并不是百分百搶到,主要是人有準備的話瘋狂點屏幕其實也挺快的(單身20年的同學的手速不得不服,畢竟有個地方我還是sleep了100毫秒的,其實去掉應該更快的),一般情況下輔助還是有絕對優勢的。

一般情況下感覺用到的這些控件ID、布局、界面所在的類、包名什么的是不太會改變的,當然如果微信版本升級比較大,估計布局什么的有變化,還得根據新的布局去重新實現,但是思路其實都是一樣的。

更新

更新前搶一次紅包時間大概為1300毫秒左右,更新了彈窗等待那一步,不等待直接模擬點擊一次也是可以的(自己手機測試通過),更新后時間減少到1000毫秒左右;自己測試手速搶紅包,時間大概是1400毫秒以上,感覺真的比人手速快了

屏幕快照 2019-12-28 下午4.59.05.png-283.4kB

項目地址

里面有一些方法是封裝了的,方便調用,具體實現可以看代碼
原文地址:Android微信搶紅包輔助
github地址:Android微信搶紅包輔助
軟件下載地址(老版本):https://github.com/tyhjh/LuckMoney/raw/master/%E6%8A%A2%E7%BA%A2%E5%8C%85.apk

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容