logo头像
博客-Leo

基于无障碍服务的微信抢红包神器

背景

抢红包一直都是一个存在的话题,过年过节凑个热闹,发个红包到家人群同事群热闹热闹,很多时候呢,在红包数量少的时候,都是无缘于红包(只怪别人手速太快),所以,在这个人工智能(人工智障)的时代,能让程序自动化帮你操作,岂不是美滋滋。

目前,抢红包插件有两种实现方式(目前能用的),法1:基于xposed去hook微信红包消息方法名,;法2:利用无障碍服务实现模拟点击操作,当然也可以两者结合使用。

目前android手机对权限的管理越来越严,安全性也提高了,xposed的使用越来越不便利,也可以用一些三方黑科技比如VirtualXposed、太极等等,速度很快,缺点是容易封号。而无障碍服务呢,是android系统面向具有视听障碍人士设计的一个辅助功能,应用所感知到的模拟点击也会被认为是人为操作,所以不存在封号的情况(速度太快另计)。所以本文介绍使用无障碍服务实现一个简单的微信抢红包插件,功能简单,有兴趣的可以基于此优化。

需求

设计一个插件,开启后,在任何一个微信群里面停留,当有红包消息出现时,自动快速点击拆开红包。

流程图(装逼惯例)

上代码

1.页面UI

像这样简单就可以了,有个开关,控制下是否自动抢即可。UI功能完成三件事:

  1. 引导开启无障碍服务开关
  2. 控制自动化抢红包开关
  3. 检查当前手机微信版本是否更新了,更新了就引导提示升级红包插件

部分源码如下:

//引导开启无障碍服务开关
private void alertOpenService() {
    new AlertDialog.Builder(this)
            .setTitle(R.string.app_name)
            .setCancelable(false)
            .setMessage("请开启"+getString(R.string.app_name)+"描辅助功能")
            .setPositiveButton("去开启", (dialog, which) -> {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                Util.postDelayed(() -> Util.toast(MainActivity.this, "请打开: 无障碍中"+getString(R.string.app_name)+"服务开关"), 1000);
            })
            .create().show();
}

//校验适配微信版本
String wxVersionName = Util.getVersionName(getApplicationContext(), "com.tencent.mm");
int    wxVer         = Integer.valueOf(wxVersionName.replace(".", ""));
if(wxVer > Integer.valueOf(Configs.WX_VERSION.replace(".", ""))) {
    new AlertDialog.Builder(this)
            .setTitle(R.string.app_name)
            .setCancelable(false)
            .setMessage("当前手机安装的微信版本不适用,可能无法正常工作,请安装不高于"+Configs.WX_VERSION+"版本微信,或者联系开发者进行工具升级\n\n"
                    +"查看微信版本号:\n微信->我->设置->关于微信->查看Version")
            .setPositiveButton("我知道了", (dialog, which) -> dialog.cancel())
            .create().show();
}

2.添加无障碍服务功能

  • 定义类AccServiceWX继承AccessibilityService,主页复写onAccessibilityEvent方法。

  • res目录下新建xml目录,里面新增文件accessibility_service_info.xml,内容如下:

      <?xml version="1.0" encoding="utf-8"?>
      <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
             android:accessibilityEventTypes="typeAllMask"
             android:accessibilityFeedbackType="feedbackGeneric"
             android:accessibilityFlags="flagReportViewIds|flagDefault|flagEnableAccessibilityVolume|flagIncludeNotImportantViews|flagRequestAccessibilityButton|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents|flagRequestFingerprintGestures|flagRequestTouchExplorationMode|flagRetrieveInteractiveWindows"
             android:canPerformGestures="true"
             android:canRetrieveWindowContent="true"
             android:description="@string/app_name"
             android:notificationTimeout="100"
             android:packageNames="com.tencent.mm" />
  • AndroidManifest文件中添加Service声明。

      <service
          android:name="vip.okfood.luckmoneywx.wx_acc.AccServiceWX"
          android:canPerformGestures="true"
          android:enabled="true"
          android:exported="true"
          android:label="@string/app_name"
          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/accessibility_service_info" />
      </service>
  • 复写onAccessibilityEvent方法,遍历节点,按照流程图处理event

      HelperUtil.iterateNode(this, rootNode, rootNode, (rootNode13, node, accessibilityService) -> {
              if(working) return;
              if(node == null || node.getViewIdResourceName() == null) return;
              if(node.getViewIdResourceName().equals(Configs.WXID.PKG_ITEM_WORD)) {//微信红包
                  LogUtil.d(TAG, "===>发现红包");
                  //找到红包布局根节点
                  AccessibilityNodeInfo clickableParent = HelperUtil.findClickableParent(node);
                  AtomicBoolean         notClick        = new AtomicBoolean(true);
                  HelperUtil.iterateNode(accessibilityService, clickableParent, clickableParent, (rootNode1, node1, accessibilityService1) -> {
                      if(node1 == null || node1.getViewIdResourceName() == null) return;
                      if(node1.getViewIdResourceName().equals(Configs.WXID.PKG_ITEM_NOT_CLICK_ID)) {//已点击
                          notClick.getAndSet(false);
                      }
                  });
                  if(notClick.get()) {
                      LogUtil.d(TAG, "===>发现红包,红包还没点过");
                      working = true;
                      clickableParent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                      mHandler.postDelayed(() -> {
                          AccessibilityNodeInfo nodeButtonOpen = HelperUtil.loopFindById(accessibilityService, Configs.WXID.PKG_BUTTON_OPEN, "看看大家的手气");
                          if(nodeButtonOpen == null) {
                              LogUtil.d(TAG, "===>没发现开按钮,执行下返回并延迟开启事件进入");
                              performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                              mHandler.postDelayed(() -> working = false, 800);
                          } else {
                              LogUtil.d(TAG, "===>发现开按钮,点击>开");
                              nodeButtonOpen.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                          }
                      }, 800);
                  } else {
                      LogUtil.d(TAG, "===>发现红包,红包已点");
                  }
              }
          });
  • 点击开后,详情页可以做个延时关闭,再进行下一轮,避免过快速度而被封号

      if(eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
          String className = "";
          try {
              className = event.getClassName().toString();
          } catch(Exception e) {LogUtil.e(TAG, "Exception:===>"+e.getMessage());}
          LogUtil.v(TAG, "className="+className);
          if(className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
              mHandler.postDelayed(() -> {
                  performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                  working = false;
              }, 500);
          }
      }

核心代码就上面这些,用浏览器扫描下面二维码体验试试,有兴趣可以在首页联系QQ联系方式,共同探讨。

APK下载:

支付宝打赏 微信打赏

赞赏是不耍流氓的鼓励

评论系统未开启,无法评论!