日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

js微信抢红包脚本代码_微信抢红包插件示例代码及其实现原理

發(fā)布時(shí)間:2023/12/20 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js微信抢红包脚本代码_微信抢红包插件示例代码及其实现原理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這個(gè)Android插件可以幫助你在微信群聊搶紅包時(shí)戰(zhàn)無(wú)不勝。當(dāng)檢測(cè)到紅包時(shí),插件會(huì)自動(dòng)點(diǎn)擊屏幕,人工點(diǎn)擊的速度無(wú)法比擬。

你正在查看的是dev分支,這個(gè)分支包含大量實(shí)驗(yàn)性的修改,不再更新。如果你希望有一個(gè)可以立即使用的插件請(qǐng)切換到stable分支。

注: stable分支的插件只點(diǎn)擊最新的紅包,根據(jù)目前測(cè)試搶紅包成功率100%。dev分支在stable分支的基礎(chǔ)上嘗試了大量修改和優(yōu)化,能使用但無(wú)法保證穩(wěn)定性。

下面的文檔僅針對(duì)dev分支。

預(yù)期特性

可以搶屏幕上顯示的所有紅包,同類(lèi)插件往往只能獲取最新的一個(gè)紅包。

智能跳過(guò)已經(jīng)戳過(guò)的紅包,避免頻繁點(diǎn)擊影響正常使用。

紅包日志 (默認(rèn)未開(kāi)啟),方便查看搶過(guò)的紅包內(nèi)容。

性能優(yōu)化,感受不到插件的存在,可一直后臺(tái)開(kāi)啟,不影響日常聊天。

由于這是一份教學(xué)代碼,項(xiàng)目的文檔和注釋都比較完整,代碼適合閱讀。

實(shí)現(xiàn)原理

1. 搶紅包流程的邏輯控制

這個(gè)插件通過(guò)一個(gè)Stage類(lèi)來(lái)記錄當(dāng)前對(duì)應(yīng)的階段。Stage類(lèi)被設(shè)計(jì)成單例并惰性實(shí)例化,因?yàn)橐粋€(gè)Service不需要也不應(yīng)該處在不同的階段。對(duì)外暴露階段常量和entering和getCurrentStage兩個(gè)方法,分別記錄和獲取當(dāng)前的階段。

public class Stage {

private static Stage stageInstance;

public static final int FETCHING_STAGE = 0, OPENING_STAGE = 1, FETCHED_STAGE = 2, OPENED_STAGE = 3;

private int currentStage = FETCHED_STAGE;

private Stage() {}

public static Stage getInstance() {

if (stageInstance == null) stageInstance = new Stage();

return stageInstance;

}

public void entering(int _stage) {

stageInstance.currentStage = _stage;

}

public int getCurrentStage() {

return stageInstance.currentStage;

}

}

1.1 階段說(shuō)明

階段

說(shuō)明

FETCHING_STAGE

正在讀取屏幕上的紅包,此時(shí)不應(yīng)有別的操作

FETCHED_STAGE

已經(jīng)結(jié)束一個(gè)FETCH階段,屏幕上的紅包都已加入待搶隊(duì)列

OPENING_STAGE

正在拆紅包,此時(shí)不應(yīng)有別的操作

OPENED_STAGE

紅包成功搶到,進(jìn)入紅包詳情頁(yè)面

1.程序以FETCHED_STAGE 開(kāi)始,將屏幕上的紅包加入待搶隊(duì)列:

--> FETCHED_STAGE --> FETCHING_STAGE --> FETCHED_STAGE -->

2.處理待搶隊(duì)列中的紅包:

--> [CLICK] --> OPENING_STAGE --> [CLICK] --> OPENED_STAGE --> [BACK] --> FETCHED_STAGE -->(搶到)

--> [CLICK] --> OPENING_STAGE --> [BACK] --> FETCHED_STAGE -->(沒(méi)搶到)

3.不斷重復(fù)流程1和2

1.2 根據(jù)階段選擇不同的入口

在每次窗體狀態(tài)發(fā)生變化后,根據(jù)當(dāng)前所在的階段選擇入口。

switch (Stage.getInstance().getCurrentStage()) {

case Stage.OPENING_STAGE:

// .......

Stage.getInstance().entering(Stage.FETCHED_STAGE);

performGlobalAction(GLOBAL_ACTION_BACK);

break;

case Stage.OPENED_STAGE:

Stage.getInstance().entering(Stage.FETCHED_STAGE);

performGlobalAction(GLOBAL_ACTION_BACK);

break;

case Stage.FETCHED_STAGE:

if (nodesToFetch.size() > 0) {

AccessibilityNodeInfo node = nodesToFetch.remove(nodesToFetch.size() - 1);

if (node.getParent() != null) {

// .......

Stage.getInstance().entering(Stage.OPENING_STAGE);

node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);

}

return;

}

Stage.getInstance().entering(Stage.FETCHING_STAGE);

fetchHongbao(nodeInfo);

Stage.getInstance().entering(Stage.FETCHED_STAGE);

break;

}

2. 屏幕內(nèi)容檢測(cè)和自動(dòng)化點(diǎn)擊的實(shí)現(xiàn)

和其他插件一樣,這里使用的是Android API提供的AccessibilityService。這個(gè)類(lèi)位于android.accessibilityservice包內(nèi),該包中的類(lèi)用于開(kāi)發(fā)無(wú)障礙服務(wù),提供代替或增強(qiáng)的用戶(hù)反饋。

AccessibilityService 服務(wù)在后臺(tái)運(yùn)行,等待系統(tǒng)在發(fā)生 AccessibilityEvent

事件時(shí)回調(diào)。這些事件指的是用戶(hù)界面上發(fā)生的狀態(tài)變化, 比如焦點(diǎn)變更、按鈕按下等等。服務(wù)可以請(qǐng)求“查詢(xún)當(dāng)前窗口中內(nèi)容”的能力。

開(kāi)發(fā)輔助服務(wù)需要繼承該類(lèi)并實(shí)現(xiàn)其抽象方法。

2.1 配置AccessibilityService

在這個(gè)例子中,我們需要監(jiān)聽(tīng)的事件是當(dāng)紅包來(lái)或者滑動(dòng)屏幕時(shí)引起的屏幕內(nèi)容變化,和點(diǎn)開(kāi)紅包時(shí)窗體狀態(tài)的變化,因此我們只需要在配置XML的accessibility-service標(biāo)簽中加入一條

android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"

或在onAccessibilityEvent回調(diào)函數(shù)中對(duì)事件進(jìn)行一次類(lèi)型判斷

final int eventType = event.getEventType();

if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED

|| eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {

// ...

}

除此之外,由于我們只監(jiān)聽(tīng)微信,還需要指定微信的包名

android:packageNames="com.tencent.mm"

為了獲取窗口內(nèi)容,我們還需要指定

android:canRetrieveWindowContent="true"

其他配置請(qǐng)看代碼。

2.2 獲取紅包所在的節(jié)點(diǎn)

首先,我們要獲取當(dāng)前屏幕的根節(jié)點(diǎn),下面兩種方式效果是相同的:

AccessibilityNodeInfo nodeInfo = event.getSource();

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

這里返回的AccessibilityNodeInfo是窗體內(nèi)容的節(jié)點(diǎn),包含節(jié)點(diǎn)在屏幕上的位置、文本描述、子節(jié)點(diǎn)id、能否點(diǎn)擊等信息。從AccessibilityService的角度來(lái)看,窗體上的內(nèi)容表示為輔助節(jié)點(diǎn)樹(shù),雖然和視圖的結(jié)構(gòu)不一定一一對(duì)應(yīng)。換句話(huà)說(shuō),自定義的視圖可以自己描述上面的輔助節(jié)點(diǎn)信息。當(dāng)輔助節(jié)點(diǎn)傳遞給AccessibilityService之后就不可更改了,如果強(qiáng)行調(diào)用引起狀態(tài)變化的方法會(huì)報(bào)錯(cuò)。

在聊天頁(yè)面,每個(gè)紅包上面都有“領(lǐng)取紅包”這幾個(gè)字,我們把它作為識(shí)別紅包的依據(jù)。如果你收到了這四個(gè)字的文本消息,可能其他的插件會(huì)做出誤判。因?yàn)槲覀兗尤肓穗A段的概念,因此不會(huì)出現(xiàn)這個(gè)問(wèn)題。

AccessibilityNodeInfo的API中有一個(gè)findAccessibilityNodeInfosByText方法允許我們通過(guò)文本來(lái)搜索界面中的節(jié)點(diǎn)。匹配是大小寫(xiě)敏感的,它會(huì)從遍歷樹(shù)的根節(jié)點(diǎn)開(kāi)始查找。API文檔中特別指出,為了防止創(chuàng)建大量實(shí)例,節(jié)點(diǎn)回收是調(diào)用者的責(zé)任,這一點(diǎn)會(huì)在接下來(lái)的部分中講到。

List node1 = nodeInfo.findAccessibilityNodeInfosByText("領(lǐng)取紅包");

2.3 對(duì)節(jié)點(diǎn)進(jìn)行操作

AccessibilityNodeInfo同樣暴露了一個(gè)API——performAction來(lái)對(duì)節(jié)點(diǎn)進(jìn)行點(diǎn)擊或者其他操作。出于安全性考慮,只有這個(gè)操作來(lái)自AccessibilityService時(shí)才會(huì)被執(zhí)行。

nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);

不過(guò),我們?cè)谡{(diào)試時(shí)發(fā)現(xiàn)"領(lǐng)取紅包"的mClickable屬性為false,說(shuō)明點(diǎn)擊的監(jiān)聽(tīng)加在它父輩的節(jié)點(diǎn)上。通過(guò)getParent獲取父節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)是可以點(diǎn)擊的。

我們還需要全局的返回操作,最方便的辦法就是performGlobalAction,不過(guò)注意這個(gè)方法是API 16后才有的。

performGlobalAction(GLOBAL_ACTION_BACK);

3. 獲取屏幕上的所有紅包

和其他插件最大的區(qū)別是,這個(gè)插件的邏輯是獲取屏幕上所有的紅包節(jié)點(diǎn),去掉已經(jīng)獲取過(guò)的之后,將待搶紅包加入隊(duì)列,再將隊(duì)列中的紅包一個(gè)個(gè)打開(kāi)。

3.1 判斷紅包節(jié)點(diǎn)是否已被搶過(guò)

實(shí)現(xiàn)這一點(diǎn)是編寫(xiě)時(shí)最大的障礙。對(duì)于一般的Java對(duì)象實(shí)例來(lái)說(shuō),除非被GC回收,實(shí)例的Id都不會(huì)變化。我最初的想法是通過(guò)正則表達(dá)式匹配下面的十六進(jìn)制對(duì)象id來(lái)表示一個(gè)紅包。

android.view.accessibility.AccessibilityNodeInfo@2a5a7c; .......

但在測(cè)試中,隊(duì)列中的部分紅包沒(méi)有被戳開(kāi)。進(jìn)一步觀(guān)察發(fā)現(xiàn),新的紅包節(jié)點(diǎn)和舊的紅包節(jié)點(diǎn)id出現(xiàn)了重復(fù),且出現(xiàn)概率較大。由于GC日志正常,我推測(cè)AccessibilityNode可能有一個(gè)實(shí)例池的設(shè)計(jì)。獲取當(dāng)前窗體節(jié)點(diǎn)樹(shù)的時(shí)候,從一個(gè)可重用的實(shí)例池中獲取一個(gè)輔助節(jié)點(diǎn)信息

(AccessibilityNodeInfo)實(shí)例。在接下來(lái)的獲取時(shí),仍然從實(shí)例池中獲取節(jié)點(diǎn)實(shí)例,這時(shí)可能會(huì)重用之前的實(shí)例。這樣的設(shè)計(jì)是有好處的,可以防止每次返回都創(chuàng)建大量的實(shí)例,影響性能。AccessibilityNodeProvider的源碼表明了這樣的設(shè)計(jì)。

也就是說(shuō),為了標(biāo)識(shí)一個(gè)唯一的紅包,只用實(shí)例id是不充分的。這個(gè)插件采用的是紅包內(nèi)容+節(jié)點(diǎn)實(shí)例id的hash來(lái)標(biāo)記。因?yàn)橥黄料?#xff0c;同一個(gè)節(jié)點(diǎn)樹(shù)下的節(jié)點(diǎn)id是一定不會(huì)重復(fù)的,滑動(dòng)屏幕后新紅包的內(nèi)容和節(jié)點(diǎn)id同時(shí)重復(fù)的概率已經(jīng)大大減小。更改標(biāo)識(shí)策略后,實(shí)測(cè)中幾乎沒(méi)有出現(xiàn)誤判。

3.2 將新出現(xiàn)的紅包加入待搶隊(duì)列

我們維護(hù)了兩個(gè)列表,分別記錄待搶紅包和搶過(guò)的紅包標(biāo)識(shí)。

private List nodesToFetch = new ArrayList<>();

private List fetchedIdentifiers = new ArrayList<>();

在每次讀取聊天屏幕的時(shí)候,會(huì)檢查這個(gè)紅包是否在fetchedIdentifiers隊(duì)列中,如果沒(méi)有,則加入nodesToFetch隊(duì)列。

for (AccessibilityNodeInfo cellNode : fetchNodes) {

String id = getHongbaoHash(cellNode);

/* 如果節(jié)點(diǎn)沒(méi)有被回收且該紅包沒(méi)有搶過(guò) */

if (id != null && !fetchedIdentifiers.contains(id)) {

nodesToFetch.add(cellNode);

}

}

4. 打開(kāi)隊(duì)列中的紅包

通過(guò)紅包打開(kāi)后顯示的文本判斷這個(gè)紅包是否可以搶,進(jìn)行接下來(lái)的操作。

4.1 判斷紅包節(jié)點(diǎn)是否被重用

這也是實(shí)現(xiàn)時(shí)的一個(gè)坑。前面提到了實(shí)例池的設(shè)計(jì),當(dāng)我們把紅包們加入待搶隊(duì)列,戳完一個(gè)紅包再回來(lái)時(shí),隊(duì)列中的其他紅包節(jié)點(diǎn)可能已被回收重用,如果再去點(diǎn)擊這個(gè)節(jié)點(diǎn),顯然沒(méi)有什么卵用。

為了解決這個(gè)問(wèn)題,我們只能退而求其次,在點(diǎn)開(kāi)前做一次檢查。如果發(fā)現(xiàn)被重用了,就舍棄這個(gè)節(jié)點(diǎn),在下一輪fetch的階段重新加入待搶隊(duì)列。確認(rèn)沒(méi)有重用立即打開(kāi),并把節(jié)點(diǎn)hash加入fetchedIdentifiers隊(duì)列。這里如果node失效getHongbaoHash會(huì)返回null。

AccessibilityNodeInfo node = nodesToFetch.remove(nodesToFetch.size() - 1);

if (node.getParent() != null) {

String id = getHongbaoHash(node);

if (id == null) return;

fetchedIdentifiers.add(id);

Stage.getInstance().entering(Stage.OPENING_STAGE);

node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);

}

可以看出這并不是很有效率的解決方案。在實(shí)測(cè)中,有時(shí)隊(duì)列中中紅包失效后被舍棄,但沒(méi)有新的AccessibilityEvent發(fā)生,接下來(lái)的操作都被掛起了。在戳過(guò)較多紅包之后,這種情況表現(xiàn)得尤為明顯,必須要顯式地改變窗體內(nèi)容才能解決。

4.2 根據(jù)紅包類(lèi)型選擇操作

紅包被戳開(kāi)前會(huì)進(jìn)行查重。戳開(kāi)后如果界面上出現(xiàn)了“拆紅包”幾個(gè)字,說(shuō)明紅包還沒(méi)有被別人搶走,立刻點(diǎn)擊“拆紅包”并將stage標(biāo)記為OPENED_STAGE。

此時(shí),另三種情況表明搶紅包失敗了,直接返回,接下來(lái)狀態(tài)會(huì)被標(biāo)記為FETCHED_STAGE。

“過(guò)期”,說(shuō)明紅包超過(guò)有效時(shí)間

“手慢了”,說(shuō)明紅包發(fā)完但沒(méi)搶到

“紅包詳情”,說(shuō)明你已經(jīng)搶到過(guò)

4.3 防止加載紅包時(shí)返回

戳開(kāi)紅包和紅包加載完之間有一個(gè)“正在加載”的過(guò)渡動(dòng)畫(huà),會(huì)觸發(fā)onAccessibilityEvent回調(diào)方法。如果在加載完之前判斷,上述文本還沒(méi)出現(xiàn),會(huì)被默認(rèn)標(biāo)記為FETCHED_STAGE并觸發(fā)返回。因此,我們要在返回前特殊判定這種情形。

我們引入了TTL來(lái)記錄嘗試次數(shù),并返回錯(cuò)誤值-1。如果到達(dá)MAX_TTL時(shí)紅包還沒(méi)有加載出來(lái)就舍棄這個(gè)紅包。

Stage.getInstance().entering(Stage.OPENING_STAGE);

ttl += 1;

return -1;

版權(quán)與免責(zé)說(shuō)明

本項(xiàng)目源自小米今年秋季發(fā)布會(huì)時(shí)演示的搶紅包測(cè)試源碼。stable分支基于此代碼繼續(xù)開(kāi)發(fā),dev分支重寫(xiě)了幾乎所有的邏輯代碼。應(yīng)用的包名com.miui.hongbao未變。

由于插件可能會(huì)改變自然的微信交互方式,這份代碼僅可用于教學(xué)目的,不得更改后用于其他用途。對(duì)于使用插件時(shí)可能發(fā)生的任何情形,由使用者自行承擔(dān),包括但不限于“禁用紅包功能”、“微信封號(hào)”。

項(xiàng)目使用MIT許可證。在理解可能的風(fēng)險(xiǎn)后,你可以將代碼用于任何用途。

總結(jié)

以上是生活随笔為你收集整理的js微信抢红包脚本代码_微信抢红包插件示例代码及其实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。