妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

coder-pig發表於1970-01-01

引言

在上上週的週六和週日,我發了兩篇利用itchat實現微信機器人的文章(Python):

通過把指令碼掛到伺服器上,自此告別手動擋,不用去手動轉發小宇宙,不用手動加好友,然後把別人一個個拉到我的Py交易群裡。正當我暗自竊喜的時候,微信並沒有放過我這隻小貓咪。

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

我還記得那天早上,我興高采烈早早來到公司,更新了一波程式碼準備為我的機器人添磚加瓦的時候,當我關閉了阿里雲上的指令碼,這時候意外來了,我的機器人小號,再也無法通過微信網頁端的介面登入了!!!掃描完二維碼,永遠提示的都是下面這樣一句話:

<
error>
<
ret>
1203<
/ret>
<
message>
當前登入環境異常。為了你的帳號安全,暫時不能登入web微信。你可以通過Windows微信、Mac微信或者手機客戶端微信登入。<
/message>
<
/error>
複製程式碼

是的,就是這樣一句話,找不到申訴渠道,也不知道何時才可能會解封。(客戶端任可正常使用)而現在另外新申請的微信小號是無法登入微信網頁端的,其實這是微信在慢慢關停網頁版登入,最主要的原因就是機器人氾濫!

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

沒有了網頁版微信,日子還是要過的,難道只能迴歸手動檔麼?幾種解決方案:

  • 1.研究客戶端協議(這個成本巨高,而且官方稍微改點東西,夠你哭的)
  • 2.APP逆向,利用Xposed框架,hook相關的方法,也是有些研究成本的;
  • 3.利用類似與按鍵精靈的東西,編寫指令碼讓他自動點點點,自動化測試工具或者本節講的這個無障礙服務——AccessibilityService

AccessibilityService其實不是一個新的東西了,老久之前就有了,官方原意:優化殘障人士的使用體驗的,而在我大天朝:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

搶紅包,自動安裝,一鍵XXX等等,可謂欣欣向榮。

使用AccessibilityService也非常Easy,核心要點就是:

通過UI Automator找到節點,通過resource-id,text,content-desc等唯一特徵定位到具體的節點,接著執行各種模擬操作,點,滾動,填充,用法比較簡單的,大部分時間會花在試錯和邏輯調整上

來一發通過AccessibilityService實現的自動加好友以及拉人進群聊的Gif體驗下:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

Gif加速了一點,不過完成加好友以及拉人總共也就耗時15s,是相當客觀的啦。下面就來介紹下AccessibilityService這個玩意怎麼用吧~


AccessibilityService用法簡介

1.自定義Service繼承AccessibilityService

如題,自定義一個AccessibilityService類,重寫兩個主要方法:

onInterrupt( ):輔助功能中斷的回撥,基本不用理,核心還是onAccessibilityEvent(AccessibilityEvent event) 上。

當介面發生了什麼事情,比如頂部Notification,介面更新,內容變化等,會觸發這個方法,你可以根據不同的事件響應不同的操作,比如小豬這個就是當頂部出現加好友的Notification的event時,跳轉到加好友頁。點開AccessibilityEvent類可以看到一堆的事件型別~

事件型別 描述
TYPE_VIEW_CLICKED View被點選
TYPE_VIEW_LONG_CLICKED View被長按
TYPE_VIEW_SELECTED View被選中
TYPE_VIEW_FOCUSED View獲得焦點
TYPE_VIEW_TEXT_CHANGED View文字變化
TYPE_WINDOW_STATE_CHANGED 開啟了一個PopupWindow,Menu或Dialog
TYPE_NOTIFICATION_STATE_CHANGED Notification變化
TYPE_VIEW_HOVER_ENTER 一個View進入懸停
TYPE_VIEW_HOVER_EXIT 一個View退出懸停
TYPE_TOUCH_EXPLORATION_GESTURE_START 觸控瀏覽事件開始
TYPE_TOUCH_EXPLORATION_GESTURE_END 觸控瀏覽事件完成
TYPE_WINDOW_CONTENT_CHANGED 視窗的內容發生變化,或子樹根佈局發生變化
TYPE_VIEW_SCROLLED View滾動
TYPE_VIEW_TEXT_SELECTION_CHANGED Edittext文字選中發生改變事件
TYPE_ANNOUNCEMENT 應用產生一個通知事件
TYPE_VIEW_ACCESSIBILITY_FOCUSED 獲得無障礙焦點事件
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED 無障礙焦點事件清除
TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY 在給定的移動粒度下遍歷檢視文字的事件
TYPE_GESTURE_DETECTION_START 開始手勢監測
TYPE_GESTURE_DETECTION_END 結束手勢監測
TYPE_TOUCH_INTERACTION_START 觸控螢幕事件開始
TYPE_TOUCH_INTERACTION_END 觸控螢幕事件結束
TYPE_WINDOWS_CHANGED 螢幕上的視窗變化事件,需要API 21+
TYPE_VIEW_CONTEXT_CLICKED View中的上下文點選事件
TYPE_ASSIST_READING_CONTEXT 輔助使用者讀取當前螢幕事件

好吧,上面的表其實並沒什麼大用,我還是習慣直接把event.toString()給列印出來,然後自行去判斷~

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

如圖就可以拿到event型別,以及產生對應事件的類名,核心是這兩個,除此之外還有Text和ContentDescription等。

比如我那個監聽Notification跳轉到新增好友頁的:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

這裡就是對事件型別做了下判斷,然後獲取contentIntent,跳轉而已。簡單點講就是:

你在這個方法裡,去判斷一波事件型別和className,然後再獲取控制元件,做一些點選,滾動,填充文字等。


2.服務的配置

自定義完這個服務要想讓他啟用你還得執行下面的操作:

Step 1:在res資料夾下建立xml資料夾,新建一個配置的xml檔案(名字自己定)

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊
<
?
xml version="1.0" encoding="utf-8"?>
<
accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:notificationTimeout="100" android:packageNames="com.tencent.mm" android:settingsActivity="com.coderpig.wechathelper.MainActivity" />
複製程式碼

屬性簡介如下

  • accessibilityEventTypes:設定監聽的事件種類,用|隔開,監聽所有可以用typeAllMask;
  • accessibilityFeedbackType:服務提供的反饋型別,feedbackGeneric通用反饋;
  • accessibilityFlags:輔助功能附加的標誌,flagDefault預設的配置
  • canRetrieveWindowContent:輔助功能服務是否能夠取回活動視窗內容的屬性
  • notificationTimeout:響應時間
  • packageNames:監聽的應用包名,不填,預設監聽所有應用的事件
  • settingsActivity:允許使用者修改輔助功能的activity類名

Step 2:接著AndroidManifest.xml檔案中對該Service進行配置

先是新增一個許可權:

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"  複製程式碼

接著是Service的配置:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

這裡是你那個配置檔案xml檔案的檔名,其他照抄。

Step 3:安裝到手機後,需要在手機設定的無障礙處開啟服務

一般在設定的輔助功能處能找到:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

如果Logcat那裡能看到列印的LOG,說明服務正常執行,接下來要找控制元件節點


3.找控制元件

這裡可以用到神器UI Automator來檢視佈局層次,開啟Android Studio,Ctrl + alt + A,輸入 monitor

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

依次點選:選中裝置 ->
Dump View Hierarchy for UI Automator

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

稍等一會,右側就會出現當前頁面的佈局層次圖,如圖隨手選中一個邀請的節點:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

右側可以拿到對應的資訊,一般比較常用的是這幾個,有一點要注意!!!resource-id不一定是唯一的

獲得控制元件基本都會通過下述這個方法:

getRootInActiveWindow( ):獲取當前整個活動視窗的根節點返回的是一個**AccessibilityNodeInfo**類,代表View的狀態資訊,提供了下述幾個非常實用的方法:

  • getParent:獲取父節點。
  • getChild:獲取子節點。
  • performAction:在節點上執行一個動作。
  • findAccessibilityNodeInfosByText:通過字串查詢節點元素。
  • findAccessibilityNodeInfosByViewId:通過檢視id查詢節點元素。

後面的這兩個方法會返回一個AccessibilityNodeInfo列表,一般操作是遍歷,然後篩選特定節點,比如我程式裡的,獲得底部Tab節點為”通訊錄”,然後點選,跳轉後遍歷,篩選”群聊”的節點,點選。

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

另外,UI Automator有時並不可靠(實時問題),我建議寫多一個遍歷節點的方法,可以更清楚裡面的控制元件情況:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

拿到控制元件,接著就到觸發事件了。


4.觸發事件

通過呼叫**performAction**()傳入一個時間型別即可觸發相應時間,比如點選,長按等事件就多了,自己點開AccessibilityNodeInfo類檢視吧,這裡介紹下最常用的幾個事件:

//點選performAction(AccessibilityNodeInfo.ACTION_CLICK);
//長按performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
//滾動performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
//向下滾一下performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
//向上滾一下//填充EditText(API版本需要>
18可用方法1,API>
21兩種方法都可以使用)
//方法1:ClipboardManager clipboard = (ClipboardManager)this.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("text", "填充內容");
clipboard.setPrimaryClip(clip);
//獲得焦點info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
////貼上進入內容 info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
//方法2:Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "填充內容");
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
複製程式碼

除了控制元件觸發事件外,AccessibilityService提供了一個**performGlobalAction(),用於執行一些通用的事件**:

GLOBAL_ACTION_BACK    點選返回按鈕GLOBAL_ACTION_HOME    點選homeGLOBAL_ACTION_NOTIFICATIONS    開啟通知GLOBAL_ACTION_RECENTS    開啟最近應用GLOBAL_ACTION_QUICK_SETTINGS    開啟快速設定GLOBAL_ACTION_POWER_DIALOG    開啟長按電源鍵的彈框複製程式碼

另外在實際開發中,直接呼叫這些全域性方法又是並沒有生效,我在調GLOBAL_ACTION_BACK的時候就發現有時不會回退,個人的解決方案是使用**handler.postDelay()**延時執行:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

除了這樣玩以外,我還利用時間差,序列去執行幾個任務,比如:

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

上面的步驟是:

進入群聊聊天資訊頁後,列表滾動兩次,接著依次:

  • 1.延時1s後,找到新增成員按鈕並點選;
  • 2.延時2.3s後,把名字填充到EditText裡
  • 3.延時3s後,點選確定按鈕

就不用過於依賴onAccessibilityEvent方法,除了用handler.postDelay外,還可以用Thread.sleep(休眠時長),用到的點大概就這麼多,其餘的自行探究吧。


小結

本節講解一波如何通過AccessibilityService來實現自動加好友以及拉人進群,之前是打算用xposed來寫的,後面發現沒我想像中簡單,而且很多用安卓機的都不會搞機(基),root也不會,後來還是選擇了AccessibilityService,簡單易用,當然後面還是會研究一波xposed實現的,敬請期待~對了,還有,之前那個網頁端的機器人被封原因估計是資訊秒回,如果有還用itchat那個做機器人的,建議回覆的時間可以稍微延長些;

關於AccessibilityService更多內容可見


附:關鍵程式碼(都可以在:github.com/coder-pig/W… 找到):程式碼有Bug的話正常,後續會優化下邏輯,感覺寫得有點雜~

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊
package com.coderpig.wechathelper;
import android.accessibilityservice.AccessibilityService;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.List;
/** * 描述:微信監控服務類 * * @author CoderPig on 2018/04/04 13:46. */public class HelperService extends AccessibilityService {
private static final String TAG = "HelperService";
private Handler handler = new Handler();
private String userName = "123";
@Override public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
CharSequence classNameChr = event.getClassName();
String className = classNameChr.toString();
Log.d(TAG, event.toString());
switch (eventType) {
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: if (event.getParcelableData() != null &
&
event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event.getParcelableData();
String content = notification.tickerText.toString();
if (content.contains("請求新增你為朋友")) {
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();

} catch (PendingIntent.CanceledException e) {
e.printStackTrace();

}
}
} break;
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: switch (className) {
case "com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI": addFriend();
break;
case "com.tencent.mm.plugin.profile.ui.SayHiWithSnsPermissionUI": verifyFriend();
break;
case "com.tencent.mm.plugin.profile.ui.ContactInfoUI": performBackClick();
break;
case "com.tencent.mm.ui.LauncherUI": if (!userName.equals("123")) {
openGroup();

} break;
case "com.tencent.mm.ui.contact.ChatroomContactUI": if (!userName.equals("123")) {
inviteGroup();

} break;
case "com.tencent.mm.ui.chatting.ChattingUI": if (!userName.equals("123")) {
openGroupSetting();

} break;
case "com.tencent.mm.plugin.chatroom.ui.ChatroomInfoUI": if (userName.equals("123")) {
performBackClick();

} else {
addToGroup();

} break;
case "com.tencent.mm.ui.base.i": dialogClick();
break;

} break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
}
} private void addFriend() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List<
AccessibilityNodeInfo>
list = nodeInfo .findAccessibilityNodeInfosByText("接受");
if (list != null &
&
list.size() >
0) {
for (AccessibilityNodeInfo n : list) {
n.performAction(AccessibilityNodeInfo.ACTION_CLICK);

}
} else {
performBackClick();

}
}
} private void verifyFriend() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
//獲得使用者名稱 if (nodeInfo != null) {
userName = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d0n").get(0).getText().toString();
AccessibilityNodeInfo finishNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hd").get(0);
finishNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);

}
} private void openGroup() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List<
AccessibilityNodeInfo>
nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ca5");
for (AccessibilityNodeInfo info : nodes) {
if (info.getText().toString().equals("通訊錄")) {
info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
handler.postDelayed(new Runnable() {
@Override public void run() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List<
AccessibilityNodeInfo>
nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j5");
for (AccessibilityNodeInfo info : nodes) {
if (info.getText().toString().equals("群聊")) {
info.getParent().getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;

}
}
}
}
}, 500L);

}
}
}
} private void inviteGroup() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List<
AccessibilityNodeInfo>
nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a9v");
for (AccessibilityNodeInfo info : nodes) {
if (info.getText().toString().equals("小豬的Python學習交流群")) {
info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;

}
}
}
} private void openGroupSetting() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/he").get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);

}
} private void addToGroup() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List<
AccessibilityNodeInfo>
listNodes = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/list");
if(listNodes != null &
&
listNodes.size() >
0) {
AccessibilityNodeInfo listNode = listNodes.get(0);
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
final AccessibilityNodeInfo scrollNodeInfo = getRootInActiveWindow();
if (scrollNodeInfo != null) {
handler.postDelayed(new Runnable() {
@Override public void run() {
List<
AccessibilityNodeInfo>
nodes = scrollNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d0b");
for (AccessibilityNodeInfo info : nodes) {
if (info.getContentDescription().toString().equals("新增成員")) {
info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;

}
}
}
},1000L);
handler.postDelayed(new Runnable() {
@Override public void run() {
List<
AccessibilityNodeInfo>
editNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/arz");
if(editNodes != null &
&
editNodes.size() >
0) {
AccessibilityNodeInfo editNode = editNodes.get(0);
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, userName);
editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);

}
}
}, 2300L);
handler.postDelayed(new Runnable() {
@Override public void run() {
List<
AccessibilityNodeInfo>
cbNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/kr");
if(cbNodes != null) {
AccessibilityNodeInfo cbNode = null;
if(cbNodes.size() == 1) {
cbNode = cbNodes.get(0);

} else if(cbNodes.size() == 2) {
cbNode = cbNodes.get(1);

} if (cbNode != null) {
cbNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
AccessibilityNodeInfo sureNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hd").get(0);
sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);

}
}
}
}, 3000L);

}
}
}
} private void dialogClick() {
AccessibilityNodeInfo inviteNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aln").get(0);
inviteNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
userName = "123";
handler.postDelayed(new Runnable() {
@Override public void run() {
List<
AccessibilityNodeInfo>
sureNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aln");
if(sureNodes != null &
&
sureNodes.size() >
0) {
AccessibilityNodeInfo sureNode = sureNodes.get(0);
sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);

}
}
},1000L);

} private void performBackClick() {
handler.postDelayed(new Runnable() {
@Override public void run() {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);

}
}, 300L);

} //遍歷控制元件的方法 public void recycle(AccessibilityNodeInfo info) {
if (info.getChildCount() == 0) {
Log.i(TAG, "child widget----------------------------" + info.getClassName().toString());
Log.i(TAG, "showDialog:" + info.canOpenPopup());
Log.i(TAG, "Text:" + info.getText());
Log.i(TAG, "windowId:" + info.getWindowId());
Log.i(TAG, "desc:" + info.getContentDescription());

} else {
for (int i = 0;
i <
info.getChildCount();
i++) {
if (info.getChild(i) != null) {
recycle(info.getChild(i));

}
}
}
} @Override public void onInterrupt() {

}
}複製程式碼

來啊,Py交易啊

想加群一起學習Py的可以加下,智障機器人小Pig,驗證資訊裡包含:PythonpythonpyPy加群交易屁眼 中的一個關鍵詞即可通過;

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

驗證通過後回覆 加群 即可獲得加群連結(不要把機器人玩壞了!!!)~~~歡迎各種像我一樣的Py初學者,Py大神加入,一起愉快地交流學♂習,van♂轉py。

妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊

來源:https://juejin.im/post/5acb49126fb9a028cc6186f1

相關文章