├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TODO.md ├── bot.py ├── config.py ├── export_puid.py ├── requirements.txt ├── single_group.py └── tools.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Expected Behavior 3 | 4 | 5 | 6 | ## Current Behavior 7 | 8 | 9 | 10 | ## Possible Solution 11 | 12 | 13 | 14 | ## Steps to Reproduce (for bugs) 15 | 16 | 17 | 1. 18 | 2. 19 | 3. 20 | 4. 21 | 22 | ## Context 23 | 24 | 25 | 26 | ## Your Environment 27 | 28 | * Version used: 29 | * Environment name and version (e.g. PHP 5.4 on nginx 1.9.1): 30 | * Server type and version: 31 | * Operating System and version: 32 | 33 | 34 | 35 | 36 | ## 预期动作 37 | 38 | 39 | 40 | ## 当前动作 41 | 42 | 43 | 44 | ## 可能的解决方案 45 | 46 | 47 | ## 重现步骤 (只有 bugs 需要) 48 | 49 | 1. 50 | 2. 51 | 3. 52 | 4. 53 | 54 | ## 内容 55 | 56 | 57 | 58 | ## 你的环境 59 | 60 | * 使用的版本: 61 | * 环境名和版本信息(e.g. PHP 5.4 on nginx 1.9.1): 62 | * 服务器类型和版本: 63 | * 操作系统类型和版本: 64 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 描述 4 | 5 | 6 | ## 动机或前后关系 7 | 8 | 9 | 10 | ## 如何测试相关代码 11 | 12 | 13 | ## 截图 (如果方便的话): 14 | 15 | ## 变更类型 16 | 17 | - [ ] 修复Bug (non-breaking change which fixes an issue) 18 | - [ ] 新特性 (non-breaking change which adds functionality) 19 | - [ ] 较大变化 (fix or feature that would cause existing functionality to change) 20 | 21 | ## 检查清单: 22 | 23 | 24 | - [ ] 我的代码遵循了项目的代码风格 25 | - [ ] 我的代码需要更改文档 26 | - [ ] 我相应的更新了文档 27 | - [ ] 我查看了 **CONTRIBUTING** 文档. 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # git ignore file for LCBot 2 | 3 | # 跟踪 .py类型的文件 4 | !*.py 5 | 6 | # 过滤以下类型的文件 7 | *.pyc 8 | *.pkl 9 | *.log 10 | *.swp 11 | .idea/ 12 | .python-version 13 | data 14 | LCBot -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ### 2017.06.07 5 | 1. Import config from config.py -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 贡献指南 2 | 3 | 感谢您参与到本项目,在参与项目之前,建议您先看下 `Pull Request 流程` 4 | 5 | 6 | ## Pull Request 流程 7 | 8 | 1. 确保非代码文件不会被加入到版本控制内,如 `*.pyc` 9 | 2. 如果修改了变量、配置等,请确保修改了 `README.md` 10 | 4. 你的代码在经过 代码评审 后,可以合并到主分支,如果您没有相应权限,可以请求 代码评审者 帮您合并 11 | 12 | ## 行为准则 13 | 14 | ### 我们的承诺 15 | 16 | 为了促进开放和欢迎的环境,我们作为贡献者和维护者保证,无论年龄,身体,大小,残疾,种族,性别认同和 17 | 表达,无论年龄,身体,大小,残疾,种族,性别认同和表达 经验水平,国籍,个人表现,种族,宗教或性别 18 | 认同和取向。 19 | 20 | ### 我们的标准 21 | 22 | 有助于创造积极环境的行为举例,包括: 23 | 24 | * 使用欢迎和包容性语言 25 | * 尊重不同的观点和经验 26 | * 优雅地接受建设性批评 27 | * 关注什么对于社区是最重要的 28 | * 对其他社区成员有同情心 29 | 30 | 参与者不能接受的行为的例子包括: 31 | 32 | * 使用性语言或意象和不受欢迎的性关注或进步 33 | * 侮辱/贬损评论,以及个人或政治攻击 34 | * 公共或私人骚扰 35 | * 未经明确许可,发布他人的私人信息,如真实地址或电子地址 36 | * 在专业环境中合理地认为不合适的其他行为 37 | 38 | ### 我们的责任 39 | 40 | 项目维护者负责澄清可接受行为的标准,并预期会对任何不能接 41 | 受的行为的情况采取适当和公正的纠正措施。 42 | 43 | 项目维护者有权删除,编辑或拒绝与本行为准则不一致的评论, 44 | 提交,代码,wiki编辑,问题和其他贡献,或暂时或永久地禁止 45 | 任何其他行为的贡献者 他们认为不适当,威胁,冒犯或有害。 46 | 47 | ### 范围 48 | 49 | 当个人代表项目或其社区时,本“行为准则”既适用于项目空间内, 50 | 也适用于公共空间。 代表项目或社区的示例包括使用官方项目 51 | 电子邮件地址,通过官方社交媒体帐户发布,或在在线或离线活动 52 | 中担任指定代表。 53 | 54 | 一个项目的代表可以由项目维护者进一步定义和澄清。 55 | 56 | 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LINUX.CN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LCBot Linux中国微信机器人 2 | 3 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/bestony/LCBot/blob/master/LICENSE) ![](https://img.shields.io/badge/Language-Python-blue.svg) ![](https://img.shields.io/badge/Python-3.X-red.svg) 4 | 5 | ![](https://postimg.aliavv.com/mbp/b69eb.png) 6 | 7 | LCBot 是一个为 Linux 中国服务的微信机器人,主要为 Linux 中国下的微信群做管理、维护等工作。 8 | 9 | **注:使用本项目有被微信网页版封号的风险,故而现已基本停止开发。如继续使用本项目,产生的后果请自行承担。** 10 | 11 | ## 演示 12 | 13 | 视频教程地址:[点我查看视频教程集合](http://dwz.cn/lcbot) | [Linux/macOS 使用教程](https://v.qq.com/x/page/k03996ry5o1.html) |[完整版 Win 10 使用教程](https://v.qq.com/x/page/y03990en5qu.html) | [单群版机器人使用教程](https://v.qq.com/x/page/p05007bqjv1.html) 14 | 15 | 注:该系列视频使用的是旧版 LCBot,新版请以本文的文字内容为准。 16 | 17 | 免登录意见反馈:[我要新功能!](https://wj.qq.com/s/1334670/bb03/) 18 | 19 | ## 特性 20 | 21 | >> 注:使用本项目有被微信网页版封号的风险,故而现已基本停止开发。如继续使用本项目,产生的后果请自行承担。 22 | 23 | 1. 关键词添加好友自动拉群 24 | 2. 私聊发送关键词自动加群 25 | 3. 新用户进群自动发送欢迎 26 | 4. 设定管理员,管理员可以通过发送命令T人(需要机器人是群主) 27 | 5. 监控群每小时发送心跳包 28 | 6. 管理员踢人监控群内留底 29 | 7. 被拉黑的用户,无法被再次拉群。需要管理员手动释放。 30 | 8. 管理员在监控群发送指令获取状态或进行特定操作(现支持“状态”、“重启”、“刷新”) 31 | 32 | ## 需要 33 | - Python 3 34 | - wxpy 35 | 36 | ## 安装 37 | 下载源码 38 | ``` 39 | git clone https://github.com/LCTT/LCBot.git 40 | ``` 41 | 安装拓展 42 | ``` 43 | cd LCBot 44 | pip3 install -U -r requirements.txt 45 | ``` 46 | ## Docker 版本 47 | 感谢 [@OSMeteor](https://github.com/OSMeteor/pythonlcbot) 提供的 Docker 版本 48 | 49 | 使用方法: 50 | 51 | ``` 52 | docker pull osmeteor/pythonlcbot 53 | # 或者使用国内镜像: docker pull registry.docker-cn.com/osmeteor/pythonlcbot 54 | docker run -i -t osmeteor/pythonlcbot /bin/bash 55 | cd /opt/LCBot 56 | python bot.py 57 | ``` 58 | 59 | ## 使用 60 | 首先,进行机器人相关设定(注:现已废弃旧版中使用 puid 的配置方案,不再需要导出 puid 数据) 61 | 62 | 编辑 config.py 中第 11 行和 22 行,分别填入管理员群的名称(`admin_group_name`)和被管理群的前缀(`group_prefix`) 63 | 64 | 编辑 config.py 中第 68 行,填入监控群的名称(`alert_group`) 65 | 66 | 编辑 config.py 中第 70 行,填入图灵机器人的 APIKEY(`turing_key`) 67 | 68 | 以及 config.py 中的欢迎语、邀请消息、邀请关键词等相关配置 69 | 70 | 随后执行以下命令运行机器人(首次登录需要扫二维码) 71 | ``` 72 | python3 bot.py 73 | ``` 74 | 75 | > 如果执行后,如果二维码展示不正常,可以执行 `sudo localectl set-locale LANG=C.UTF-8` 76 | 77 | ## FAQ 78 | 查看 [FAQ](https://github.com/LCTT/LCBot/wiki/FAQ) 79 | ## Todo 80 | 81 | 查看 [Todo](TODO.md) 82 | 83 | ## 贡献指南 84 | 85 | 查看 [CONTRIBUTING](CONTRIBUTING.md) 86 | 87 | ## LICENSE 88 | 89 | 代码基于 [MIT](LICENSE) 协议开源 90 | 91 | 文档以 [CC-BY-ND](https://creativecommons.org/licenses/by-nd/4.0/) 协议分享 92 | 93 | ## 致谢 94 | 95 | - [wxpy](https://github.com/youfou/wxpy) : 微信机器人 / 优雅而强大的微信个人号 API 96 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # 待开发清单 2 | 3 | - [x] 验证信息拉群:针对不同的关键词将用户拉进不同的群。 4 | - [x] 私聊拉群:针对用户发送的消息,将用户拉进不同的群。 5 | ------------ 6 | - [x] 举报受理:机器人作为群主接收并执行管理员的踢人的指令。 7 | - [x] 黑名单:被机器人踢掉的人无法再次被拉群 8 | ------------ 9 | - [x] 随机加群:某一个关键词允许设置多个不同的群,允许随机拉到某个群内/或者拉人没有满的群。 10 | - [ ] 群人数上限:拉人没有满的群时,可以设置判断人满不满的条件 11 | - [ ] 自动建群:当所有可以拉的群人数都满了以后,创建新群。群名称以某种格式定义 12 | - [ ] 自动拉群:自动建群时,拉指定的人加群。 13 | ------------ 14 | - [ ] 数据表:列举当前可拉的群和群内人数 15 | - [ ] 机器人群内命令:规则参看 https://github.com/LCTT/LCBot/issues/7 16 | - [ ] 自动T人:移除一个月未发言的人。 https://github.com/LCTT/LCBot/issues/2 17 | ------------ 18 | - [ ] 注册码发放 19 | - [ ] 对接 MySQL 数据库 20 | ------------ 21 | - [ ] 支持Linux 命令查询 :https://github.com/LCTT/LCBot/issues/1 22 | 23 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | from wxpy import * 5 | import config 6 | import re 7 | from wxpy.utils import start_new_thread 8 | import time 9 | import os 10 | import platform 11 | from importlib import reload 12 | ''' 13 | 使用 cache 来缓存登陆信息,同时使用控制台登陆 14 | ''' 15 | console_qr = (False if platform.system() == 'Windows' else True) 16 | bot = Bot('bot.pkl', console_qr=console_qr) 17 | bot.messages.max_history = 0 18 | ''' 19 | 开启 PUID 用于后续的控制 20 | ''' 21 | bot.enable_puid('wxpy_puid.pkl') 22 | ''' 23 | 邀请信息处理 24 | ''' 25 | rp_new_member_name = ( 26 | re.compile(r'^"((\n?.?)+)"通过'), 27 | re.compile(r'邀请"((\n?.?)+)"加入'), 28 | re.compile(r'invited "((\n?.?)+)" to the group chat'), 29 | ) 30 | 31 | # 远程踢人命令: 移出 @<需要被移出的人> 32 | rp_kick = re.compile(r'^(?:移出|移除|踢出|拉黑)\s*@((\n?.?)+?)(?:\u2005?\s*$)') 33 | 34 | # 监控群监控等级 35 | alert_level = 30 # DEBUG: 10, INFO: 20, WARNING: 30, ERROR: 40, FATAL: 50 36 | 37 | # 下方为函数定义 38 | 39 | 40 | def fresh_groups(): 41 | ''' 42 | 管理员群及被管理群初始化 43 | ''' 44 | global groups, admin_group 45 | # 格式化被管理群 Groups 46 | try: 47 | allgroups = bot.groups(update=True) 48 | groups = list( 49 | filter(lambda x: x.name.startswith(config.group_prefix), 50 | allgroups.search(config.group_prefix))) 51 | groups += list( 52 | filter(lambda x: x.name in config.additional_groups, allgroups)) 53 | except: 54 | print("查找被管理群出错!请检查被管理群前缀(group_prefix)是否配置正确") 55 | quit() 56 | 57 | # 格式化管理员群 Admin_group 58 | try: 59 | admin_group = ensure_one( 60 | bot.groups(update=True).search(config.admin_group_name)) 61 | except: 62 | print("查找管理员群出错!请检查管理群群名(admin_group_name)是否配置正确") 63 | print("现将默认设置为只有本帐号为管理员") 64 | admin_group = None 65 | 66 | 67 | def get_time(): 68 | return str(time.strftime("%Y-%m-%d %H:%M:%S")) 69 | 70 | 71 | def get_logger(): 72 | ''' 73 | 机器人消息提醒设置 74 | ''' 75 | global alert_receiver, logger 76 | if config.alert_group: 77 | try: 78 | alert_receiver = ensure_one(bot.groups().search( 79 | config.alert_group)) 80 | except: 81 | print("警报群设置有误,请检查群名是否存在且唯一") 82 | alert_receiver = bot.file_helper 83 | else: 84 | alert_receiver = bot.file_helper 85 | 86 | try: 87 | for h in logger.handlers: 88 | logger.removeHandler(h) 89 | except: 90 | pass 91 | 92 | logger = get_wechat_logger(alert_receiver, level=alert_level) 93 | 94 | 95 | def heartbeat(): 96 | ''' 97 | 定时报告进程状态 98 | ''' 99 | while bot.alive: 100 | time.sleep(3600) 101 | # noinspection PyBroadException 102 | try: 103 | logger.error(status()) 104 | except ResponseError as e: 105 | if 1100 <= e.err_code <= 1102: 106 | logger.critical('LCBot offline: {}'.format(e)) 107 | _restart() 108 | 109 | 110 | def random_sleep(): 111 | ''' 112 | 随机延时 113 | ''' 114 | from random import randrange 115 | rnd_time = randrange(2, 7) 116 | time.sleep(rnd_time) 117 | 118 | 119 | def _restart(): 120 | ''' 121 | 重启机器人 122 | ''' 123 | os.execv(sys.executable, [sys.executable] + sys.argv) 124 | 125 | 126 | def status(): 127 | ''' 128 | 状态汇报 129 | ''' 130 | status_text = get_time() + " 机器人目前在线,共有好友 【" + str(len( 131 | bot.friends())) + "】 群 【" + str(len(bot.groups())) + "】" 132 | return status_text 133 | 134 | 135 | def condition_invite(user): 136 | ''' 137 | 条件邀请 138 | ''' 139 | if user.sex == 2: 140 | female_groups = bot.groups().search(config.female_group)[0] 141 | try: 142 | female_groups.add_members(user, use_invitation=True) 143 | pass 144 | except: 145 | pass 146 | if (user.province in config.city_group.keys() 147 | or user.city in config.city_group.keys()): 148 | try: 149 | target_city_group = bot.groups().search( 150 | config.city_group[user.province])[0] 151 | pass 152 | except: 153 | target_city_group = bot.groups().search( 154 | config.city_group[user.city])[0] 155 | pass 156 | try: 157 | if user not in target_city_group: 158 | target_city_group.add_members(user, use_invitation=True) 159 | except: 160 | pass 161 | 162 | 163 | def from_admin(msg): 164 | """ 165 | 判断 msg 中的发送用户是否为管理员 166 | :param msg: 167 | :return: 168 | """ 169 | if not isinstance(msg, Message): 170 | raise TypeError('expected Message, got {}'.format(type(msg))) 171 | from_user = msg.member if isinstance(msg.chat, Group) else msg.sender 172 | return (from_user in admin_group) if admin_group else from_user == bot.self 173 | 174 | 175 | def remote_kick(msg): 176 | ''' 177 | 远程踢人命令 178 | ''' 179 | if msg.type is TEXT: 180 | match = rp_kick.search(msg.text) 181 | if match: 182 | name_to_kick = match.group(1) 183 | 184 | if not from_admin(msg): 185 | if not config.silence_mode: 186 | return '感觉有点不对劲… @{}'.format(msg.member.name) 187 | else: 188 | print('非管理员 {} 想踢人...'.format(msg.member.name)) 189 | return 190 | 191 | member_to_kick = ensure_one( 192 | list( 193 | filter(lambda x: x.name == name_to_kick, 194 | msg.sender.members))) 195 | if member_to_kick == bot.self: 196 | return '无法移出 @{}'.format(member_to_kick.name) 197 | if member_to_kick in admin_group.members: 198 | return '无法移出 @{}'.format(member_to_kick.name) 199 | 200 | logger_msg = get_time() + str( 201 | " 【" + member_to_kick.name + "】 被 【" + msg.member.name + 202 | "】 移出 【" + msg.sender.name + "】") 203 | try: 204 | member_to_kick.set_remark_name("[黑名单]-" + get_time()) 205 | except: 206 | logger_msg += "\n" + str( 207 | "为 【" + member_to_kick.name + "】 设置黑名单时出错") 208 | 209 | if member_to_kick in msg.chat: 210 | msg.chat.remove_members(member_to_kick) 211 | kick_info = '成功移出 @{}'.format(member_to_kick.name) 212 | else: 213 | kick_info = '@{} 已不在群中'.format(member_to_kick.name) 214 | 215 | for ready_to_kick_group in groups: 216 | if member_to_kick in ready_to_kick_group: 217 | ready_to_kick_group.remove_members(member_to_kick) 218 | ready_to_kick_group.send( 219 | str("【" + member_to_kick.name + "】 因其在 【" + 220 | msg.sender.name + "】 的行为被系统自动移出")) 221 | logger_msg += "\n" + str( 222 | "【" + member_to_kick.name + "】 被系统自动移出 " + 223 | ready_to_kick_group.name) 224 | 225 | logger.error(logger_msg) 226 | return kick_info 227 | 228 | 229 | def get_new_member_name(msg): 230 | ''' 231 | 邀请消息处理 232 | ''' 233 | # itchat 1.2.32 版本未格式化群中的 Note 消息 234 | from itchat.utils import msg_formatter 235 | msg_formatter(msg.raw, 'Text') 236 | 237 | for rp in rp_new_member_name: 238 | match = rp.search(msg.text) 239 | if match: 240 | return match.group(1) 241 | 242 | 243 | def invite(user, keyword): 244 | ''' 245 | 定义邀请用户的方法。 246 | 按关键字搜索相应的群,如果存在相应的群,就向用户发起邀请。 247 | ''' 248 | from random import randrange 249 | group = bot.groups().search(config.keyword_of_group[keyword]) 250 | if len(group) > 0: 251 | for i in range(0, len(group)): 252 | if user in group[i]: 253 | content = "您已经加入了 {} [微笑]".format(group[i].nick_name) 254 | user.send(content) 255 | return 256 | if len(group) == 1: 257 | target_group = group[0] 258 | else: 259 | index = randrange(len(group)) 260 | target_group = group[index] 261 | try: 262 | target_group.add_members(user, use_invitation=True) 263 | except: 264 | user.send("邀请错误!机器人邀请好友进群已达当日限制。请您明日再试") 265 | else: 266 | user.send("该群状态有误,您换个关键词试试?") 267 | 268 | 269 | fresh_groups() 270 | get_logger() 271 | logger.error(str("机器人登陆成功!" + get_time())) 272 | 273 | start_new_thread(heartbeat) 274 | 275 | # 下方为消息处理 276 | 277 | 278 | @bot.register(msg_types=FRIENDS) 279 | def new_friends(msg): 280 | ''' 281 | 处理加好友请求信息。 282 | 如果验证信息文本是字典的键值之一,则尝试拉群。 283 | ''' 284 | random_sleep() 285 | user = msg.card.accept() 286 | if msg.text.lower() in config.keyword_of_group.keys(): 287 | invite(user, msg.text.lower()) 288 | else: 289 | user.send(config.invite_text) 290 | random_sleep() 291 | condition_invite(user) 292 | 293 | 294 | @bot.register(Friend, msg_types=TEXT) 295 | def exist_friends(msg): 296 | random_sleep() 297 | if msg.sender.name.find("黑名单") != -1: 298 | return "您已被拉黑!" 299 | else: 300 | if msg.text.lower() in config.keyword_of_group.keys(): 301 | invite(msg.sender, msg.text.lower()) 302 | else: 303 | return config.invite_text 304 | 305 | 306 | # 管理群内的消息处理 307 | @bot.register(groups, except_self=False) 308 | def wxpy_group(msg): 309 | ret_msg = remote_kick(msg) 310 | random_sleep() 311 | if ret_msg: 312 | return ret_msg 313 | elif msg.is_at and not config.silence_mode: 314 | if config.turing_key: 315 | tuling = Tuling(api_key=config.turing_key) 316 | tuling.do_reply(msg) 317 | else: 318 | return "忙着呢,别烦我!" 319 | pass 320 | 321 | 322 | @bot.register(groups, NOTE) 323 | def welcome(msg): 324 | name = get_new_member_name(msg) 325 | if name and not config.silence_mode: 326 | random_sleep() 327 | return config.welcome_text.format(name) 328 | 329 | 330 | @bot.register(alert_receiver, except_self=False) 331 | def alert_command(msg): 332 | if from_admin(msg): 333 | random_sleep() 334 | if msg.text == "状态": 335 | return status() 336 | elif msg.text == "重启": 337 | _restart() 338 | elif msg.text == "刷新": 339 | fresh_groups() 340 | return "群信息已更新,现有被管理群 【{}】,管理员 【{}】".format( 341 | len(groups), 342 | len(admin_group) if admin_group else 1) 343 | elif msg.text == "更新": 344 | reload(config) 345 | fresh_groups() 346 | get_logger() 347 | return "配置重载成功,现有被管理群 【{}】,管理员 【{}】".format( 348 | len(groups), 349 | len(admin_group) if admin_group else 1) 350 | 351 | 352 | embed() 353 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | ''' 4 | 定义管理员群的名称 5 | 6 | 在该群内的人都将被设为管理员, 7 | 管理员在被管理的群中享有高级管理权限,可进行如踢人等的操作 8 | 注:群名为部分匹配,请尽量输入全名以保证搜索到的群的唯一性 9 | ''' 10 | admin_group_name = '机器人管理群' 11 | 12 | ''' 13 | 定义被管理群的群名前缀 14 | 所有以此为前缀的群都将设为被管理的群 15 | 注:前缀大小写敏感 16 | 17 | 如:设定为'Linux中国◆',则将自动搜索到 18 | 「Linux中国◆微信机器人群」「Linux中国◆LFS群」等以其为开头的群, 19 | 并将其设为被管理的群 20 | ''' 21 | group_prefix = '机器人' 22 | 23 | ''' 24 | 定义非特定前缀的群 25 | 26 | 注:必须输入完整名称 27 | ''' 28 | additional_groups = ('另一个机器人群', ) 29 | 30 | # 新人入群的欢迎语 31 | welcome_text = '''🎉 欢迎 @{} 的加入! 32 | 😃 有问题请私聊我。 33 | ''' 34 | 35 | invite_text = """欢迎您,我是「Linux 中国」微信群助手, 36 | 请输入如下关键字加入群: 37 | - 运维 开发 安全 嵌入式 学生 找工作 38 | - 运维密码 机器人 39 | - DBA PHP Python Golang Docker LFS vim 40 | 进群四件事: 41 | 1、阅读群公告, 42 | 2、修改群名片, 43 | 3、做自我介绍, 44 | 4、发个总计一元、一百份的红包 45 | 请言行遵守群内规定,违规者将受到处罚,拉入黑名单。""" 46 | 47 | 48 | ''' 49 | 设置群组关键词和对应群名 50 | * 关键词必须为小写,查询时会做相应的小写处理 51 | 52 | 关于随机加群功能: 53 | 针对同类的群有多个的场景,例如群名 LFS群1、LFS群2、LFS群3... 54 | 设置关键词字典如下: 55 | keyword_of_group = { 56 | "lfs":"LFS群", 57 | } 58 | 机器人会以"LFS群"为群名搜索,搜索结果为同类群名的列表, 59 | 再从列表中随机选取一个发出加群邀请。 60 | ''' 61 | keyword_of_group = { 62 | "测试": "机器人测试群", 63 | } 64 | 65 | ''' 66 | 地区群 67 | ''' 68 | city_group = { 69 | "北京": "机器人北京群", 70 | } 71 | 72 | keyword_of_group.update(city_group) 73 | 74 | female_group = "机器人测试群" 75 | 76 | alert_group = "机器人测试群" 77 | 78 | turing_key = '' 79 | 80 | # 以下为功能配置选项 81 | ''' 82 | 全局静默开关 83 | ''' 84 | silence_mode = False 85 | -------------------------------------------------------------------------------- /export_puid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | from wxpy import * 5 | import platform 6 | 7 | ''' 8 | 使用 cache 来缓存登陆信息,同时使用控制台登陆 9 | ''' 10 | console_qr=(False if platform.system() == 'Windows' else True) 11 | bot = Bot('bot.pkl', console_qr=console_qr) 12 | 13 | 14 | ''' 15 | 开启 PUID 用于后续的控制 16 | ''' 17 | bot.enable_puid('wxpy_puid.pkl') 18 | 19 | friends = bot.friends() 20 | groups = bot.groups() 21 | 22 | with open('data', 'w',encoding='UTF-8') as output: 23 | output.write("-----Bots-------\n") 24 | output.write(str(bot.self.name + '--->'+ bot.self.puid + "\n")) 25 | output.write("-----Friends-------\n") 26 | for i in friends: 27 | output.write(str(i.nick_name + " ---> " + i.puid + "\n")) 28 | 29 | output.write("-----Groups-------\n") 30 | for i in groups: 31 | output.write(str(i.name + " ---> " + i.puid + "\n")) 32 | 33 | print("数据输出成功!") 34 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wxpy 2 | -------------------------------------------------------------------------------- /single_group.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | """ 4 | 功能说明: 5 | 1. 用户无需发送关键词,添加好友后,自动发送邀请消息 6 | 2. 添加好友后,会自动发送回复语(下方可修改) 7 | 3. 用户进群后,自动发送相关的邀请信息。 8 | """ 9 | """ 10 | 定义区,下方数据修改为你自己对应的内容 11 | """ 12 | # 欢迎语,{} 会变成新入群用户的昵称 13 | welcome_text = '''🎉 欢迎 @{} 的加入! 14 | 😃 有问题请私聊 @Linux中国 15 | ''' 16 | 17 | # 回复语,在发送群邀请后会回复这个 18 | reply_text = """你好,欢迎加入我们群XXX 19 | 群规是XXX 20 | """ 21 | 22 | # 群名 23 | group_name = '"机器人测试群"' 24 | 25 | """ 26 | 代码区,下方的内容不要修改 27 | """ 28 | from wxpy import * 29 | import re 30 | import platform 31 | console_qr=(False if platform.system() == 'Windows' else True) 32 | bot = Bot('bot.pkl', console_qr=console_qr) 33 | 34 | target_group = bot.groups().search(group_name)[0] 35 | 36 | ''' 37 | 邀请消息处理 38 | ''' 39 | def get_new_member_name(msg): 40 | # itchat 1.2.32 版本未格式化群中的 Note 消息 41 | from itchat.utils import msg_formatter 42 | msg_formatter(msg.raw, 'Text') 43 | 44 | for rp in rp_new_member_name: 45 | match = rp.search(msg.text) 46 | if match: 47 | return match.group(1) 48 | ''' 49 | 邀请信息处理 50 | ''' 51 | rp_new_member_name = ( 52 | re.compile(r'^"(.+)"通过'), 53 | re.compile(r'邀请"(.+)"加入'), 54 | ) 55 | 56 | ''' 57 | 处理加好友请求信息。 58 | 如果验证信息文本是字典的键值之一,则尝试拉群。 59 | ''' 60 | @bot.register(msg_types=FRIENDS) 61 | def new_friends(msg): 62 | user = msg.card.accept() 63 | target_group.add_members(user,use_invitation=True) 64 | user.send(reply_text) 65 | 66 | @bot.register(target_group, NOTE) 67 | def welcome(msg): 68 | name = get_new_member_name(msg) 69 | if name: 70 | return welcome_text.format(name) 71 | 72 | embed() 73 | -------------------------------------------------------------------------------- /tools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | from wxpy import * 5 | import platform 6 | 7 | 8 | console_qr=(False if platform.system() == 'Windows' else True) 9 | bot = Bot('bot.pkl', console_qr=console_qr) 10 | 11 | 12 | bot.enable_puid('wxpy_puid.pkl') 13 | 14 | def search_group_puid(puid): 15 | try: 16 | return bot.groups().search(puid=puid)[0] 17 | pass 18 | except: 19 | return "查无此群" 20 | pass 21 | 22 | def search_user_puid(puid): 23 | try: 24 | return bot.friends().search(puid=puid)[0] 25 | pass 26 | except: 27 | return "查无此人" 28 | pass 29 | 30 | 31 | embed() 32 | --------------------------------------------------------------------------------