内容简介:em...是我,那个『敲最屌的码,输最多的钱』的傻雕开发仔,故事的最后:没有暴富,没有嫩模,也没有穴深妹...再次奉劝各位一句:好的,碎碎念的那么多,说回本节,写这一篇原因是,我的Py交易群里,童鞋问的最多的问题都是和机器人有关,基本都是下面这类问题:
em...是我,那个『敲最屌的码,输最多的钱』的傻雕开发仔,故事的最后:没有暴富,没有嫩模,也没有穴深妹...
再次奉劝各位一句: 远离投机倒把,保持身心健康 !(当然,后面如果学到机器学习的东西,可能会有续集 ~(╯▽╰ )真香~~)
好的,碎碎念的那么多,说回本节,写这一篇原因是,我的Py交易群里,童鞋问的最多的问题都是和机器人有关,基本都是下面这类问题:
- 1.群主,你的机器人怎么实现的?
- 2.群主,你的apk怎么运行了没反应?
- 3.群主,支持自动添加手机号为好友吗?
- ...等等
一开始我还是比较热衷帮忙解决问题的,但是耐心这种东西呢,是最容易被消磨殆尽的。而且学习 Python 之后,我变得越来越懒,一些繁琐重复的操作,我都会想办法自动化....当然,如果你是给我打钱或者是妹子,我也是很乐意的。
其实Android无障碍服务和itchat都是一些老生常谈的东西了,我也写过好几篇文章了:
- 妙用AccessibilityService黑科技实现微信自动加好友拉人进群聊
- 自动抢红包,点赞朋友圈,AccessibilityService解放你的双手
- 利用itchat搭建微信机器人详解(附三个实用示例)
- 小猪的Python学习之旅 —— 18.Python微信转发小宇宙早报
- 小猪的Python学习之旅 —— 19.Python微信自动好友验证,自动回复,发送群聊链接
东西就那些,套路也是那些,大部分时候,你只需要一点 『灵性』 !
就好像七巧板一样,只有七块,但是却能拼接出许多图形。要有灵性!本文先说下无障碍服务和itchat的一些核心要点,然后以读者编写的机器人脚本为例,教你实现一波,希望可以给你带来一些提示,然后去扩展自己想要的功能。
开源的微信个人号接口库——itchat
itchat库基于微信网页版,出了好几年的了,微信正在慢慢收窄网页端的功能(因为滥用的微信机器人),很多以前能用的接口慢慢都 不能用 了,比如: 拉人进群,添加好友等 ,反正网页端没有的,现在都不能用。丢几个链接:
- 微信网页端登录 : wx2.qq.com/
- itchat的仓库地址 : github.com/littlecoder…
- itchat官方文档 : itchat.readthedocs.io/zh/latest/
itchat现在能做的: 监听加好友的信息,监听聊天信息(包括群聊),发送信息。 基本上常用的而且可用的就这三个。当然如果你愿意掏钱的话,你不需要折腾那么多,网上有其他付费的途径:微友助手,王二狗机器人,还有xposed插件等。基于其他协议且可用的免费开源库目前还没见过,有知道的欢迎在评论区告知下!
使用要点提炼
如果可以,我希望你,尽可能的学会使用『 正则表达式处理字符串 』(基本功),丢个以前写过的文章: 小猪的Python学习之旅 —— 3.正则表达式 ,正则对于字符串匹配,提取非常实用!
1.监听并通过加好友的请求
@itchat.msg_register(itchat.content.FRIENDS) def deal_with_friend(msg): # 自动将新好友的消息录入,不需要重载通讯录 itchat.add_friend(**msg['Text']) 复制代码
通过上面的 add_friend
函数,就可以完成添加好友的操作了。但是,有时我们可能需要做一些过滤, 不然乱七八糟的人都能加你了,是吧,比如对添加内容进行过滤,包含某些字眼才通过验证,或者 获取加你的人的相关信息,比如姓名,验证信息,个性签名,性别等。我们直接把上面的msg打印出来, 内容如下:
{'MsgId': '6930655840618917667', 'FromUserName': 'fmessage', 'ToUserName': '@64fc6691440834f2dfba5489d652e5dbae06da4d57d550757403094424f7ec9c', 'MsgType': 37, 'Content': '<msg fromusername="wxid_gvr9a3le939h22" encryptusername="v1_6a48a18b8d6164b69dfdd3949311cd4bc58d96dfe00636d5e9814ff36b8d107164e768ccd9b2fb911ea9b87ccac42978@stranger" fromnickname="Robot Pig" content="我是Robot Pig" shortpy="ROBOTPIG" imagestatus="3" scene="30" country="AD" province="" city="" sign="(´v`o)♡" percard="1" sex="2" alias="" weibo="" albumflag="0" albumstyle="0" albumbgimgid="" snsflag="1" snsbgimgid="http://mmsns.qpic.cn/mmsns/icDH6NcE3zNVBleeQZUzzlnhWk16tIfPKyvsmqWIpUwAxHkvricuNCL2RvGPjS3pVq7miaZQoju8TU/0" snsbgobjectid="12478275406675193980" mhash="197adbfd7de1668f30895d20dfb09b67" mfullhash="197adbfd7de1668f30895d20dfb09b67" bigheadimgurl="http://wx.qlogo.cn/mmhead/ver_1/jYcpsrqBVaxDuMbFgPZwH5F3mvcgKMNVrQXlmmbGWx5p9iaFrYALPyia1b7Izn4YCUYDRXz1jnfKw0eUXvEeEH5NouILblzLDjog40fqOjIjc/0" smallheadimgurl="http://wx.qlogo.cn/mmhead/ver_1/jYcpsrqBVaxDuMbFgPZwH5F3mvcgKMNVrQXlmmbGWx5p9iaFrYALPyia1b7Izn4YCUYDRXz1jnfKw0eUXvEeEH5NouILblzLDjog40fqOjIjc/96" ticket="v2_4ed4b6a5ac5ccf04c68c7bd64e4b543ba5babde23ce2985d4317bfc4bb62dcdfd78bb701551e1410fe64836f9bd147199de3e4493a031ea2daf52187816d9207@stranger" opcode="2" googlecontact="" qrticket="" chatroomusername="" sourceusername="" sourcenickname=""><brandlist count="0" ver="688441058"></brandlist></msg>', 'Status': 3, 'ImgStatus': 1, 'CreateTime': 1541558757, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '@156957cc475e1113c0a8fa340eae41ac1d92a12a523302a8eb06208616d656b3', 'NickName': 'Robot Pig', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '我是Robot Pig', 'Signature': '(´v`o)♡', 'Alias': '', 'Scene': 30, 'VerifyFlag': 0, 'AttrStatus': 50467109, 'Sex': 2, 'Ticket': 'v2_4ed4b6a5ac5ccf04c68c7bd64e4b543ba5babde23ce2985d4317bfc4bb62dcdfd78bb701551e1410fe64836f9bd147199de3e4493a031ea2daf52187816d9207@stranger', 'OpCode': 2}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 6930655840618917667, 'OriContent': '', 'EncryFileName': '', 'User': <User: {'UserName': '@156957cc475e1113c0a8fa340eae41ac1d92a12a523302a8eb06208616d656b3', 'MemberList': <ContactList: []>}>, 'Type': 'Friends', 'Text': {'status': 3, 'userName': '@156957cc475e1113c0a8fa340eae41ac1d92a12a523302a8eb06208616d656b3', 'verifyContent': '', 'autoUpdate': {'UserName': '@156957cc475e1113c0a8fa340eae41ac1d92a12a523302a8eb06208616d656b3', 'NickName': 'Robot Pig', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '我是Robot Pig', 'Signature': '(´v`o)♡', 'Alias': '', 'Scene': 30, 'VerifyFlag': 0, 'AttrStatus': 50467109, 'Sex': 2, 'Ticket': 'v2_4ed4b6a5ac5ccf04c68c7bd64e4b543ba5babde23ce2985d4317bfc4bb62dcdfd78bb701551e1410fe64836f9bd147199de3e4493a031ea2daf52187816d9207@stranger', 'OpCode': 2}}} 复制代码
看上去和Json有点类似是吧,但是不是Json,你用Json格式化 工具 试试就知道了,而是一种类似于字典的东东 (跟下源码就知道了,msg的类:itchat.storage.messagequeue.Message)一大串有点乱,用PyCharm新建一个json文件, 复制粘贴格式化下:
因为类似于字典,你可以通过键的形式获取所需的值,比如打印下msg['Content']:
<msg fromusername="wxid_gvr9a3le939h22" encryptusername="v1_6a48a18b8d6164b69dfdd3949311cd4bc58d96dfe00636d5e9814ff36b8d107164e768ccd9b2fb911ea9b87ccac42978@stranger" fromnickname="Robot Pig" content="我是Robot Pig" shortpy="ROBOTPIG" imagestatus="3" scene="30" country="AD" province="" city="" sign="(´v`o)♡" percard="1" sex="2" alias="" weibo="" albumflag="0" albumstyle="0" albumbgimgid="" snsflag="1" snsbgimgid="http://mmsns.qpic.cn/mmsns/icDH6NcE3zNVBleeQZUzzlnhWk16tIfPKyvsmqWIpUwAxHkvricuNCL2RvGPjS3pVq7miaZQoju8TU/0" snsbgobjectid="12478275406675193980" mhash="197adbfd7de1668f30895d20dfb09b67" mfullhash="197adbfd7de1668f30895d20dfb09b67" bigheadimgurl="http://wx.qlogo.cn/mmhead/ver_1/jYcpsrqBVaxDuMbFgPZwH5F3mvcgKMNVrQXlmmbGWx5p9iaFrYALPyia1b7Izn4YCUYDRXz1jnfKw0eUXvEeEH5NouILblzLDjog40fqOjIjc/0" smallheadimgurl="http://wx.qlogo.cn/mmhead/ver_1/jYcpsrqBVaxDuMbFgPZwH5F3mvcgKMNVrQXlmmbGWx5p9iaFrYALPyia1b7Izn4YCUYDRXz1jnfKw0eUXvEeEH5NouILblzLDjog40fqOjIjc/96" ticket="v2_4ed4b6a5ac5ccf04c68c7bd64e4b543b5c95f344b73e23e05f71c527683d8693969fcef8a893316431a660ac382032b022ec8af4d8ae5372e680931064da1ce3@stranger" opcode="2" googlecontact="" qrticket="" chatroomusername="" sourceusername="" sourcenickname=""><brandlist count="0" ver="688441086"></brandlist></msg> 复制代码
同样新建一个xml文件,复制粘贴格式化下:
里面有我们想要的信息,接着我们用正则来提取这些想要的数据:
msg_pattern = re.compile('<msg fromusername="(.*?)".*?fromnickname="(.*?)" content="(.*?)".*?sign="(.*?)".*?sex="(\d)".*?bigheadimgurl="(.*?)"',re.S) 复制代码
接着修改一波代码:
@itchat.msg_register(itchat.content.FRIENDS) def deal_with_friend(msg): result = msg_pattern.search(msg['Content']) if result is not None: print('添加人微信id:', result.group(1)) print('添加人用户名', result.group(2)) print('验证内容', result.group(3)) print('添加人个性签名', result.group(4)) print('添加人性别', result.group(5)) print('添加人头像大图', result.group(6)) 复制代码
添加试试,打印结果如下:
行吧,什么加你人的信息都拿到了,你想干嘛就干嘛!
2.监听聊天信息
这个也很简单,你可以监听多种类型的信息,如下表所示:
信息类型 | 解释 |
---|---|
itchat.content.TEXT | 文本内容 |
itchat.content.MAP | 位置文本 |
itchat.content.Card | 名片 |
itchat.content.Note | 通知文本 |
itchat.content.Sharing | 分享名称 |
itchat.content.RECORDING | 录音 |
itchat.PICTURE | 图片/表情 |
itchat.content.VOICE | 录音 |
itchat.content.ATTACHMENT | 附件 |
itchat.content.VIDEO | 短视频 |
itchat.content.FRIENDS | 好友邀请 |
itchat.content.SYSTEM | 系统信息 |
可注册多个信息监听,后注册的信息优先级高于先注册信息,带参数信息高于不带参数信息。 核心代码示例如下:
@itchat.msg_register(itchat.content.TEXT) def reply_msg(msg): if msg['Content'] == u'你好': itchat.send_msg(msg['User']['NickName'] + "你好啊!", msg['FromUserName']) 复制代码
和上面的监听并通过加好友的请求玩法一样,根据键拿值,或者正则提取需要的数据。这里提供几个常用的键:
msg['Content'] # 获取用户发送的内容,后面的匹配值建议加上u,代表Unicode编码 msg['User']['NickName'] # 发送信息的用户名 msg['FromUserName'] # 接收信息的用户名,这个不是直接的用户昵称或微信号!!! 复制代码
3.发送信息
itchat支持下述几种类型的信息(不支持语音):
函数名 | 作用 |
---|---|
send_msg() | 发送文字信息 |
send_file() | 发送文件 |
send_video() | 发送视频 |
send_image() | 发送图片 |
核心代码示例如下:
user_info = itchat.search_friends(name='一朵死去的花') if len(user_info) > 0: # 拿到用户名 user_name = user_info[0]['UserName'] # 发送文字信息 itchat.send_msg('培杰你好啊!', user_name) # 发送图片 time.sleep(10) itchat.send_image('cat.jpg', user_name) # 发送文件 time.sleep(10) itchat.send_file('19_2.py', user_name) # 发送视频 time.sleep(10) itchat.send_video('sport.mp4', user_name) 复制代码
建议加入延时,避免信息发送过于频繁,导致账号被封!
4.获得群聊成员列表
核心代码示例如下:
@itchat.msg_register(itchat.content.TEXT, isGroupChat=True) def reply_msg(msg): print("收到一条群信息:", msg['ActualNickName'], msg['Content']) 复制代码
玩法和之前的一样,另外,还可以调用** msg.isAt
**判断是否有人@自己。
5.监控加群信息
核心代码示例如下:
@itchat.msg_register([NOTE], isGroupChat=True) def revoke_msg(msg): if '邀请' in str(msg['Text']): # 进行相关操作 复制代码
就是判断提示信息里是否有加群字眼,好吧,常用的大概就这些,其他的自行查阅文档。
Android无障碍服务——AccessibilityService
其实就是一个自动点点点的东西,没什么技术含量,真的!!!在开始讲解AccessibilityService之前,先要明确一点:
什么是自动化?
下面是我个人的理解:
把本该人做的,重复性高,单调,机械化的操作,交给程序去完成。
举个例子,小猪每天都要用微信拉人进群,所需的操作步骤如下所示:
是的,你拉一个人,需要20多秒,每次拉人的操作都是机械重复的。如果每天有30个人 进群,你需要花费:600s,10分钟我都够开一把王者荣耀的了,别人在上分,我还在拉人???
多捞哦,用AccessibilityService写个自动点点点的工具就可以把我从中解放出来。
1.自定义Service继承AccessibilityService
自定义一个AccessibilityService类,重写两个主要方法: onInterrupt ( ):辅助功能中断的回调,基本不用理, 核心还是: onAccessibilityEvent (AccessibilityEvent event) 。
当界面发生改变,比如顶部Notification,界面更新,内容变化等,就会触发** onAccessibilityEvent
方法。 点开 AccessibilityEvent**类可以看到一堆的事件类型:
上面一大堆,其实并没有什么用,我一般是习惯直接把event.toString()给打印出来,然后自行判断:
代码示例如下:
这里做的事情就是,当无障碍相关的Event触发时,去判断Event类型以及触发事件的类名,再去执行相关操作:点击,滚动,填充文本等。
2.获取结点的几个方法
可以通过 resource-id,text 来定位到结点,如果可以,建议使用后者,因为一般APP更新后,这个id都会发生变化,(所以微信更新后都需要做适配,就是更新这个id)
1)通过UI Automator来查看布局层次
旧版的Android Studio,Ctrl + alt + A,输入 monitor 可以找到,新版的 Android Studio是找不到的,你需要来到** android-sdk/tools
**目录下:
连接手机后,点击顶部的:
接着可以看到当前页面的层次结构图:
有一点务必注意: resource-id不一定是唯一 !!!
getRootInActiveWindow( ):获取当前整个活动窗口的根节点,返回的是一个 AccessibilityNodeInfo 类,代表View的状态信息, 提供了下述几个非常实用的方法:
-
findAccessibilityNodeInfosByViewId
:通过视图id查找节点元素。 -
findAccessibilityNodeInfosByText
:通过字符串查找节点元素。 -
getParent
:获取父节点。 -
getChild
:获取子节点。
另外,找结点要注意判空,找不到对应结点直接调用其他方法是会空指针异常的!!! 找到结点然后就是一些动作了,常用的点击,长按,滚动和输入文字。代码示例如下:
/* 点击 */ node.performAction(AccessibilityNodeInfo.ACTION_CLICK) /* 长按 */ node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK) /* 滚动 */ listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) //向上滚动 listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) //向下滚动 /* 输入文字 */ val arguments = Bundle() arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,"xxx") editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) /* 通过粘贴板输入文字 */ public static void sendTextForEditText(Context context, AccessibilityNodeInfo editNode, String text) { if (editNode != null) { ClipboardManager clipboard = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("text", text); clipboard.setPrimaryClip(clip); //获得焦点 editNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS); //粘贴内容 edittext.performAction(AccessibilityNodeInfo.ACTION_PASTE); } } 复制代码
除此之外,还有AccessibilityService本身特有的方法,如模拟回退键,Home键等。
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) //回退 performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME) //Home键 performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS) //点击notification 复制代码
大概的玩法就这些,除了通过UI Automator获得id外,还可以通过其他几种方式来获取id。
2)开发者助手
如果你手机root了的话,可以安装一个** 『开发者助手』
**,点击当前界面分析,点击想查看的节点即可,如图所示。
3)通过adb命令
依次键入:
adb shell uiautomator dump /mnt/sdcard/window_dump.xml adb pull /mnt/sdcard/window_dump.xml 复制代码
运行结果如图所示:
接着可以把这个xml文件丢到as里,格式化下,折叠下一层层拆开,然后去找对应的结点:
这种方法是不怎么推荐的,除非这个结点很明显,比如文本啊,之类的,层级很多的时候,可能会找死你...
3.AccessibilityService注意事项
在使用AccessibilityService服务时,有几点要注意:
首先需要手动开启无障碍服务!!!程序转了跑,没反应,多半是因为没有开启无障碍服务! 无障碍服务一般在: 辅助功能->无障碍 ,(不同的手机可能不同)找到自己的点点点APK,开启,如图所示:
另外,有一点要注意,有时可能因为异常导致程序意外终止了,你需要到无障碍中关掉对应的服务,然后重启。 还有一点最重要的无障碍服务的适用范围:
原生的Android APP!!!是的 原生 !!!现在很多应用都是混合应用,对于 H5 的页面,无障碍服务是 无能为力 的!因为此时的控件点击事件 不是通过onClick 来产生的,而是直接判断 TouchEvent 。而Android的无障碍服务没有提供发送down,move,up事件的api。而替代方案只能使用root后的手机,向系统发送全局点击命令。
一般是拿到结点,然后获得结点所在的区域,然后执行相关的命令,比如点击,常用代码示例如下:
/* 执行 Shell 命令 */ public static void execShellCmd(String cmd) { try { // 申请获取root权限,这一步很重要,不然会没有作用 Process process = Runtime.getRuntime().exec("su"); // 获取输出流 OutputStream outputStream = process.getOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeBytes(cmd); dataOutputStream.flush(); dataOutputStream.close(); outputStream.close(); } catch (Throwable t) { t.printStackTrace(); } } /* 点击某个结点 */ public static void perforGlobalClick(AccessibilityNodeInfo info) { Rect rect = new Rect(); info.getBoundsInScreen(rect); perforGlobalClick(rect.centerX(), rect.centerY()); } /* 点击某个坐标点 */ public static void perforGlobalClick(int x, int y) { execShellCmd("input tap " + x + " " + y); } /* 全局滑动 */ public static void perforGlobalSwipe(int x0, int y0, int x1, int y1) { execShellCmd("input swipe " + x0 + " " + y0 + " " + x1 + " " + y1); } /* 全局返回 */ public static void perforGlobalHome(long delay) { execShellCmd("input keyevent " + KeyEvent.KEYCODE_HOME); } /* 全局Home键 */ public static void perforGlobalHome(long delay) { execShellCmd("input keyevent " + KeyEvent.KEYCODE_BACK); } 复制代码
行吧,关于AccessibilityService的玩法大概就这些了,接着教大家撸一个我的半自动微信机器人。
3.动手撸一个自己的微信机器人
先罗列下我的需求:
- 1.自动通过别人加好友的验证,发送欢迎图和欢迎信息;
- 2.监听用户发送的信息,响应对应的信息
- 菜单:返回菜单回复词
- 1:加入「Python学习交流群」
- 2:加入「Android学习交流群」
- 3:加入「闲聊扯淡群」
- 4:加入「抠腚男孩的妙妙屋」
- 5:关注公众号「抠腚男孩」
- 6:小猪的「个人博客」
- 7:小猪的「Github」
- 8:给小猪「打赏」
- 9:小猪的「微信」(不闲聊!)
- 其他,默认回复黑人问号图。
好的,问题来了,itchat现在不支持拉人进群,怎么办?一个折中的方法,就是利用Android AccessibilityService, 完成自动拉人进群,我们可以采集发送进群的人的用户名,然后定时(比如两小时)发送一次到文件传输助手,然后 复制粘贴下用户名到我们编写的无障碍脚本里,完成自动拉人的操作,因为还要人去复制粘贴,所以只能算半自动!
接着第二个问题,数据的传输格式,如果只是一个群的话,最简单的,用户名拼接,回车作为分隔:
小A 小B 小C 复制代码
这里的话,因为我有多个群,读者可以选择加入自己想加入的群,回车换行或者添加分隔符的方式显得有点low, 而且不方便扩展,这里,我决定使用Json字符串,存储加每个群的人的用户名,最后拼接成一个Json,示例如下:
{ "Python": [], "Python2": [ "朱伟", "程命沆(hàng)", "双枪老汉" ] "Speak": [ "程命沆(hàng)" ], "Android": [ "朱伟", "程命沆(hàng)" ], "Guy": [] } 复制代码
PS:这里有Python2的原因是,一群满了,所以要做下判断,如果人数达到495的,把加群的人添加到二群。 所以还需要监控群群人员变化的信息,有新的人进群,获取一下一群当前的,加入到二群中。还有,要对 加群的人做下判断,如果已经在群里了,就不要添加到列表中。逻辑都弄清楚了,那就直接上代码吧:
# -*- coding:utf-8 -*- # 微信小宇宙助手 import datetime import re import time import random import json import itchat from itchat.content import * from apscheduler.schedulers.blocking import BlockingScheduler # 群聊人员列表 member_python_list = [] member_python_list_2 = [] member_android_list = [] member_speak_list = [] # 加群人员的列表 group_python_list = [] # Python group_python_list_2 = [] # Python 2群 group_android_list = [] # Android group_speak_list = [] # 闲聊 # 获取群聊人员的列表的正则 nickname_compile = re.compile(r"\<ChatroomMember:.*?'NickName': '(.*?)'", re.S) # 获取群聊名称的正则 group_name_compile = re.compile("'NickName': '(.{1,40})', 'HeadImgUrl':", re.S) # 添加好友通过欢迎词 welcome_words = '(˶ᵔᵕᵔ˶)嘤嘤嘤,:kissing_heart::kissing_heart::kissing_heart:\n我是智障机器人小Pig,发送关键字:「菜单」 \n 查看更多小Pig的更多功能!' # 菜单回复词 menu_answer = '(˶ᵔᵕᵔ˶)锵锵锵~:tada::tada::tada:,\n' \ '可用关键词如下(输入对应数字,比如1):\n' \ ' :pig: 1.加入「Python学习交流群」\n' \ ' :pig: 2.加入「Android学习交流群」\n' \ ' :pig: 3.加入「闲聊扯淡群」\n' \ ' :pig: 4.关注公众号「抠腚男孩」\n' \ ' :pig: 5.小猪的「个人博客」\n' \ ' :pig: 6.小猪的「GitHub」\n' \ ' :pig: 7.给小猪「打赏」\n' \ ' :pig: 8.小猪「微信」(不闲聊哦~)\n' \ '注:请不要回复过于频繁,智障机器人不会聊天哦!:dog:' # 加群统一回复词 add_group_answer = ':no_entry_sign::no_entry_sign::no_entry_sign:FBI Warning!:no_entry_sign::no_entry_sign::no_entry_sign:\n(`・ω・´)ゞ非常抱歉的通知您:\n\n微信粑粑把拉人接口禁掉了,你的加群请求已收到,小猪童鞋会尽快把你拉到群中。\n\nヾノ≧∀≦)o 麻烦耐心等候哦!' # 重复加群回复词 add_repeat_answer = '<(`^´)>哼,敲生气,你都在群里了,加什么群鸭!:angry::angry::angry:' # 捐献回复词 donate_answer = '(˶ᵔᵕᵔ˶)您的打赏,会让小猪更有动力肝♂出更Interesting的文章,谢谢支持~:blush::blush::blush:' # 小猪回复词 pig_answer = '(˶ᵔᵕᵔ˶)小猪童鞋不闲聊哦,有问题欢迎到群里讨论哦~' # 404回复词 no_match_answer = '!!!非常抱歉,您输入的关键词粗错了,请发送「菜单」查看支持的数字关键字ヽ(・ω・´メ)' msg_pattern = re.compile( '<msg fromusername="(.*?)".*?fromnickname="(.*?)" content="(.*?)".*?sign="(.*?)".*?sex="(\d)".*?bigheadimgurl="(.*?)"', re.S) # 自动通过加好友 @itchat.msg_register(itchat.content.FRIENDS) def deal_with_friend(msg): result = msg_pattern.search(msg['Content']) if result is not None: print('添加人微信id:', result.group(1)) print('添加人用户名', result.group(2)) print('验证内容', result.group(3)) print('添加人个性签名', result.group(4)) print('添加人性别', result.group(5)) print('添加人头像大图', result.group(6)) # itchat.add_friend(**msg['Text']) # 自动将新好友的消息录入,不需要重载通讯录 # time.sleep(random.randint(1, 3)) # itchat.send_msg(welcome_words, msg['RecommendInfo']['UserName']) # time.sleep(random.randint(1, 3)) # itchat.send_image('welcome.png', msg['RecommendInfo']['UserName']) # 自动回复配置 @itchat.msg_register([TEXT]) def deal_with_msg(msg): text = msg['Content'] if text == u'菜单': time.sleep(random.randint(1, 3)) itchat.send(menu_answer, msg['FromUserName']) # 加入Python交流群 elif text == u'1': time.sleep(random.randint(1, 3)) nickname = msg['User']['NickName'] if nickname not in member_python_list and nickname not in member_python_list_2: itchat.send_msg("【" + nickname + "】童鞋\n" + add_group_answer, msg['FromUserName']) if nickname is not None: # 人数超过阀值拉入二群 if len(member_python_list) >= 495: if nickname not in group_python_list_2: group_python_list_2.append(nickname) else: if nickname not in group_python_list: group_python_list.append(nickname) else: itchat.send_msg(add_repeat_answer, msg['FromUserName']) # 加入Android交流群 elif text == u'2': time.sleep(random.randint(1, 3)) nickname = msg['User']['NickName'] if nickname not in member_android_list: itchat.send_msg("【" + nickname + "】童鞋\n" + add_group_answer, msg['FromUserName']) if nickname is not None and nickname not in group_android_list: group_android_list.append(nickname) else: itchat.send_msg(add_repeat_answer, msg['FromUserName']) # 加入闲聊群 elif text == u'3': time.sleep(random.randint(1, 3)) nickname = msg['User']['NickName'] if nickname not in member_speak_list: itchat.send_msg("【" + nickname + "】童鞋\n" + add_group_answer, msg['FromUserName']) if nickname is not None and nickname not in group_speak_list: group_speak_list.append(nickname) else: itchat.send_msg(add_repeat_answer, msg['FromUserName']) # 公众号 elif text == u'4': time.sleep(random.randint(1, 3)) itchat.send_image('gzh.jpg', msg['FromUserName']) # 个人博客 elif text == u'5': time.sleep(random.randint(1, 3)) return 'coder-pig的个人主页-掘金:https://juejin.im/user/570afb741ea493005de84da3' # GitHub elif text == u'6': time.sleep(random.randint(1, 3)) return 'https://github.com/coder-pig' # 打赏 elif text == u'7': time.sleep(random.randint(1, 3)) itchat.send_image('ds.gif', msg['FromUserName']) time.sleep(random.randint(1, 3)) itchat.send_msg(donate_answer, msg['FromUserName']) time.sleep(random.randint(1, 3)) itchat.send_image('wxpay.png', msg['FromUserName']) # 小猪微信 elif text == u'8': time.sleep(random.randint(1, 3)) itchat.send_msg(pig_answer, msg['FromUserName']) time.sleep(random.randint(1, 3)) itchat.send_image('scan_code.png', msg['FromUserName']) # 其他默认回复: else: time.sleep(random.randint(1, 3)) itchat.send_image('hrwh.png', msg['FromUserName']) time.sleep(random.randint(1, 3)) itchat.send_msg(no_match_answer, msg['FromUserName']) @itchat.msg_register([NOTE], isGroupChat=True) def revoke_msg(msg): result = group_name_compile.search(str(msg)) if result is not None: group_name = result.group(1) if '邀请' in str(msg['Text']): results = nickname_compile.findall(str(msg)) if group_name == '小猪的Python学习交流群': member_python_list.clear() for result in results: member_python_list.append(result) elif group_name == '小猪的Android学习交流群': member_python_list.clear() results = nickname_compile.findall(str(msg)) for result in results: member_android_list.append(result) elif group_name == '技♂术交流:u7981:': member_python_list.clear() results = nickname_compile.findall(str(msg)) for result in results: member_speak_list.append(result) # 发送加群人信息列表 def send_friend_group(): friend_dict = {"Python": [], "Android": [], "Speak": [], "Python2": []} for p in group_python_list: friend_dict['Python'].append(p) for a in group_android_list: friend_dict['Android'].append(a) for s in group_speak_list: friend_dict['Speak'].append(s) for p2 in group_python_list_2: friend_dict['Python2'].append(p2) if len(friend_dict['Python']) > 0 or len(friend_dict['Android']) > 0 or len(friend_dict['Speak']) > 0 or len( friend_dict['Python2']) > 0: itchat.send_msg(str(json.dumps(friend_dict, ensure_ascii=False, indent=4)), toUserName="filehelper") group_python_list.clear() group_python_list_2.clear() group_android_list.clear() group_speak_list.clear() # 登陆成功后开启定时任务 def after_login(): sched.add_job(send_friend_group, 'interval', hours=2) sched.start() # 登陆时先获取群聊的UserName,获取群成员昵称会用到 def get_member_list(): python_chat_rooms = itchat.search_chatrooms(name='小猪的Python学习交流1群') if len(python_chat_rooms) > 0: group_username = python_chat_rooms[0]['UserName'] result = itchat.update_chatroom(group_username, detailedMember=True) member_python_list.clear() results = nickname_compile.findall(str(result)) for result in results: member_python_list.append(result) python_chat_rooms_2 = itchat.search_chatrooms(name='小猪的Python学习交流2群') if len(python_chat_rooms_2) > 0: group_username = python_chat_rooms_2[0]['UserName'] result = itchat.update_chatroom(group_username, detailedMember=True) member_python_list_2.clear() results = nickname_compile.findall(str(result)) for result in results: python_chat_rooms_2.append(result) android_chat_rooms = itchat.search_chatrooms(name='小猪的Android学习交流群') if len(android_chat_rooms) > 0: group_username = android_chat_rooms[0]['UserName'] result = itchat.update_chatroom(group_username, detailedMember=True) member_android_list.clear() results = nickname_compile.findall(str(result)) for result in results: member_android_list.append(result) speak_chat_rooms = itchat.search_chatrooms(name='技♂术交流:u7981:') if len(android_chat_rooms) > 0: group_username = speak_chat_rooms[0]['UserName'] result = itchat.update_chatroom(group_username, detailedMember=True) member_speak_list.clear() results = nickname_compile.findall(str(result)) for result in results: member_speak_list.append(result) if __name__ == '__main__': sched = BlockingScheduler() itchat.auto_login(loginCallback=get_member_list, enableCmdQR=1) itchat.run(blockThread=False) after_login() 复制代码
运行后可以测试下我们的自动回复:
可以,自动回复的功能就做好了,接着是搭配着无障碍服务自动拉人。 先是五个群名称:
接着写一个Bean类,用来放Json数据。
接着就是无障碍服务类了,感觉没什么好讲的,直接上代码吧:
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 com.orhanobut.hawk.Hawk /** * 描述:无障碍服务类 * * @author CoderPig on 2018/04/12 13:47. */ class HelperService : AccessibilityService() { private val TAG = "HelperService" private val handler = Handler() private var curGroup = "" private var mMember = Member() override fun onInterrupt() {} override fun onAccessibilityEvent(event: AccessibilityEvent) { val eventType = event.eventType val classNameChr = event.className val className = classNameChr.toString() Log.d(TAG, event.toString()) when (eventType) { AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> { if (Hawk.get(Constant.ADD_FRIENDS, false)) { when (className) { "com.tencent.mm.ui.LauncherUI" -> openGroup() "com.tencent.mm.ui.contact.ChatroomContactUI" -> searchGroup() "com.tencent.mm.ui.chatting.ChattingUI" -> openGroupSetting() "com.tencent.mm.chatroom.ui.ChatroomInfoUI" -> openSelectContact() "com.tencent.mm.ui.contact.SelectContactUI" -> addMembers() } } if (className == "com.tencent.mm.ui.widget.a.c") { dialogClick() } } } } //1.打开群聊 private fun openGroup() { mMember = Hawk.get<Member>(Constant.MEMBER) if(mMember.python_1.size != 0 || mMember.android.size != 0 || mMember.speak.size != 0 || mMember.python_2.size != 0 || mMember.guy.size != 0) { curGroup = when { mMember.python_1.size > 0 -> Constant.GROUP_NAME_1 mMember.python_2.size > 0 -> Constant.GROUP_NAME_2 mMember.android.size > 0 -> Constant.GROUP_NAME_3 mMember.speak.size > 0 -> Constant.GROUP_NAME_4 mMember.guy.size > 0 -> Constant.GROUP_NAME_5 else -> "" } val nodeInfo = rootInActiveWindow if (nodeInfo != null) { val tabNodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cw2") for (tabNode in tabNodes) { if (tabNode.text.toString() == "通讯录") { tabNode.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK) handler.postDelayed({ val newNodeInfo = rootInActiveWindow if (newNodeInfo != null) { val tagNodes = newNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/lv") for (tagNode in tagNodes) { if (tagNode.text.toString() == "群聊") { tagNode.parent.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK) break } } } }, 500L) } } } } } //2.搜索群聊 private fun searchGroup() { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { val nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/m6") for (info in nodes) { if (info.text.toString() == curGroup) { info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK) break } } } } //3.打开群聊设置 private fun openGroupSetting() { when (curGroup) { Constant.GROUP_NAME_1 -> { if(mMember.python_1.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j1")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } Constant.GROUP_NAME_2 -> { if(mMember.python_2.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j1")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } Constant.GROUP_NAME_3 -> { if(mMember.android.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j1")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } Constant.GROUP_NAME_4 -> { if(mMember.speak.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j1")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } Constant.GROUP_NAME_5 -> { if(mMember.guy.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j1")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } else -> { performBackClick() } } } //4.滚动后点击添加按钮,打开添加成员页面 private fun openSelectContact() { if(curGroup != "") { var members = arrayListOf<String>() when (curGroup) { Constant.GROUP_NAME_1 -> members = mMember.python_1 Constant.GROUP_NAME_2 -> members = mMember.python_2 Constant.GROUP_NAME_3 -> members = mMember.android Constant.GROUP_NAME_4 -> members = mMember.speak Constant.GROUP_NAME_5 -> members = mMember.guy } if (members.size > 0) { val nodeInfo = rootInActiveWindow if (nodeInfo != null) { val numText = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/text1")[0].text.toString() val memberCount = numText.substring(numText.indexOf("(") + 1,numText.indexOf(")")).toInt() val listNode = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/list")[0] if(memberCount > 100) { listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) } val scrollNodeInfo = rootInActiveWindow if (scrollNodeInfo != null) { handler.postDelayed({ val nodes = scrollNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/dnm") for (info in nodes) { if (info.contentDescription.toString() == "添加成员") { info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK) break } } }, 1000L) } } } } else { performBackClick() } } //5.添加成员 private fun addMembers() { var members = arrayListOf<String>() //最后一次的时候清空记录,并且点击顶部确定按钮 when (curGroup) { Constant.GROUP_NAME_1 -> members = mMember.python_1 Constant.GROUP_NAME_2 -> members = mMember.python_2 Constant.GROUP_NAME_3 -> members = mMember.android Constant.GROUP_NAME_4 -> members = mMember.speak Constant.GROUP_NAME_5 -> members = mMember.guy } if (members.size > 0) { for (i in 0 until members.size) { handler.postDelayed({ val nodeInfo = rootInActiveWindow if (nodeInfo != null) { val editNodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/b26") if (editNodes != null && editNodes.size > 0) { val editNode = editNodes[0] val arguments = Bundle() arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, members[i]) editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) } } }, 500L * (i + 1)) handler.postDelayed({ val cbNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/om") if (cbNodes != null) { val cbNode: AccessibilityNodeInfo? if (cbNodes.size > 0) { cbNode = cbNodes[0] cbNode?.parent?.performAction(AccessibilityNodeInfo.ACTION_CLICK) } } //最后一次的时候清空记录,并且点击顶部确定按钮 if (i == members.size - 1) { val m = Hawk.get<Member>(Constant.MEMBER) when (curGroup) { Constant.GROUP_NAME_1 -> m.python_1 = arrayListOf() Constant.GROUP_NAME_2 -> m.python_2 = arrayListOf() Constant.GROUP_NAME_3 -> m.android = arrayListOf() Constant.GROUP_NAME_4 -> m.speak = arrayListOf() Constant.GROUP_NAME_5 -> m.guy = arrayListOf() } Hawk.put(Constant.MEMBER, m) curGroup = "" val sureNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j0") if (sureNodes != null && sureNodes.size > 0) { sureNodes[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) } } }, 800L * (i + 1)) } } } //对话框自动点击 private fun dialogClick() { val inviteNode = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/au_")[0] inviteNode.performAction(AccessibilityNodeInfo.ACTION_CLICK) } private fun performBackClick() { handler.postDelayed({ performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }, 1300L) } } 复制代码
接着复制itchat返回的加群人的数据,写入后,点击打开微信,接下来就是享受自动加群了,如动图所述(加速过...)
行吧,关于Android无障碍服务 X itchat打造微信半自动机器人,就说这么多,如果你看完 还不会,我是真的没办法了...无障碍服务不止可以应用于微信,其他原生APP也可以做,比如最常见的 自动打卡,自动签到等,读者学会了方法后,可以自行拓展~
4.仓库地址
-
ItChatWXHelper :配合无障碍服务器拉人用的基于itchat的机器人
-
WechatHelper :利用Android AccessibilityService 实现自动加好友,拉人进群聊
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Facebook发布机器学习框架PyRobot,助力机器人开源社区
- 创建聊天机器人,第 3 部分: 使用认知(或人工智能)服务增强聊天机器人
- 基于 Python 的僵尸网络将 Linux 机器变成挖矿机器人
- 协作机器人行业
- 开源机器人自学指南
- python图灵机器人
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
拆掉互联网那堵墙
庄良基 / 经济日报出版社 / 2014-6 / 25.80
都在说道互联网、说道电子商务、说道移动APP、说道微信、说道互联网金融......我们该如何认识互联网?中小微企业该如何借力互联网?互联网很神秘吗?很高深莫测吗? 其实互联网并没有什么神秘的,也没有什么高深莫测的!互联网无非是人类发明的工具而已,既然是工具,我们就一定可以驾驭和使用它。既然可以双重使用,就理当让所有有人都容易掌握并轻松驾驭。 互联网离我们很远吗?互联网界的成功故事都是那......一起来看看 《拆掉互联网那堵墙》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
XML、JSON 在线转换
在线XML、JSON转换工具