Python 分析在德的中国程序员,告别 996?(2)
开发需求
- 获取群聊群成员信息
- 找出所有群昵称不符合标准的群友
- 随机抽取5人,在群里发布改昵称提醒消息
- 同时将这次提醒的5人,存储进数据库
- 每天早八点晚八点两次定时启动昵称检查脚本
- 某人在将来被提醒次数超过10次,还不予配合不改昵称时,将自动踢出群
- 新群友被邀请进入群时,立刻发送群规提示改昵称
开发分解
该任务所需第三方库如下:
pip3 install wxpy pip3 install apscheduler pip3 install pymysql pip3 install DBUtils
1. 建库建表
本文采用的是MySQL,后期可以扩展支持Postgre或者MongoDB。
因为需要存储微信表情字符集,所以表的默认编码采用utf8mb4_unicode_ci。
DROP TABLE IF EXISTS `wx_chat_group`; CREATE TABLE `wx_chat_group` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', PRIMARY KEY `id` (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci; INSERT INTO `wx_chat_group` (`id`, `name`) VALUES (1, '德国IT职业信息分享群'); -- 每次抽取的不合规格的昵称将存储如表以供计数 DROP TABLE IF EXISTS `wx_chat_nickname_check`; CREATE TABLE `wx_chat_nickname_check` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `group_id` int(9) UNSIGNED NOT NULL, `wx_puid` VARCHAR(16) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', `nickname` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create time', PRIMARY KEY `id` (`id`), INDEX `idx_group_id` (`group_id`), INDEX `idx_create_time` (`create_time`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;
2. 用户设置
所有用户自定义变量存入conf文件里,如群名、临时存储路径、数据库接入信息,踢人阈值:
[wechat] group_name_1=德国IT职业信息分享群 group_id_1=1 path_tmp=/opt/tmp/ notice_random=5 kick_max=10 tuling_api_key=xxxxx [mysql] mysql_host=localhost mysql_port=3306 mysql_user=root mysql_pwd=xxxx mysql_database=wechat_group_ibot
3. 监听群消息
初始化群聊对象,并且监听群消息
# 查找群聊,并且设置附加属性,以备后用 def init_group(group_name, group_id): group = ensure_one(bot.groups().search(group_name)) group.ext_attr = lambda: None setattr(group.ext_attr, 'group_id', group_id) setattr(group.ext_attr, 'group_name', group_name) return group # 初始化微信机器人bot bot = Bot(cache_path=True, console_qr=True) # unique chat person's id bot.enable_puid() # 读取自定义参数 cf = configparser.ConfigParser() cf.read('wechat.conf') group_name_1 = cf.get('wechat', 'group_name_1') group_id_1 = cf.get('wechat', 'group_id_1') # 初始化群聊对象 group_1 = init_group(group_name_1, group_id_1) # 监听类型为NOTE的群消息,如:"aa"邀请"bbb"加入了群聊 @bot.register(group_1, NOTE) def welcome_for_group(msg): try: new_member_name = re.search(r'邀请"(.+?)"|"(.+?)"通过', msg.text).group(1) except AttributeError: return group_1.send(welcome_text.format(new_member_name, space_after_chat_at)) # 保持bot持续运行 bot.join()
4. 昵称检查
检查群友昵称,存入数据库并且发送提醒, 具体逻辑代码这里不予累述。
def check_nickname(nickname): # 正则检验群昵称是否标准 if re.match(r'([一-龥]|[ -~]|[sS])+|([一-龥]|[ -~])+|([一-龥]|[ -~])+', nickname): return True else: return False ...... # 检查群友昵称 def process_group_members(group): # 每次检查前先刷新群成员信息,避免用户改了昵称后再次被提醒 # 但刷新会改变成员临时的内部puid,所以检查昵称必须同时结合puid和nickname group.update_group(members_details=False) ...... for member in group: nickname = member.name wx_puid = member.puid if not check_nickname(nickname): invalid_member = GroupMember(nickname, wx_puid, 0) invalid_members.append(invalid_member) ..... # 随机抽取不合格的5人 random_members = random.sample(invalid_members, k=5) ...... # 将本次提醒群友存入数据库,供下次计数 def insert_invalid_name(group_id, wx_puid, nickname): bot_db.execute("INSERT INTO wx_chat_nickname_check (`group_id`, `wx_puid`, `nickname`)" " VALUES (%s, %s, %s)", (group_id, wx_puid, nickname)) # 获取昵称不合规群友被提醒计数 def get_invalid_name_count(group_id, wx_puid, nickname): result = bot_db.get_count("SELECT id FROM wx_chat_nickname_check " "WHERE group_id = %s and (wx_puid = %s or nickname = %s)", (group_id, wx_puid, nickname)) return result
5. 数据库连接池
这里的数据库连接使用了数据库连接池:DBUtils.PersistentDB
DBUtils.PooledDB: 适用于多线程频繁开启关闭数据库连接
DBUtils.PersistentDB:适用于单线程多次频繁连接数据库
如果不采用线程池而是采取直连,那么运行一段时间后,脚本将出现该错误
pymysql.err.OperationalError: 2006
这里将DBUtils再次封装了一下,写了一个单例模式BotDatabase, 提供了query(select), execute(update, delete) 以及批处理execute等常用接口。
6. 启动定时器
# 早八点晚八点各执行检查一次 def start_schedule_for_checking_member(group): scheduler = BlockingScheduler() scheduler.add_job(lambda: process_group_members(group), 'cron', hour=8, minute=1, timezone="Europe/Paris") scheduler.add_job(lambda: process_group_members(group), 'cron', hour=20, minute=1, timezone="Europe/Paris")
最终成果
已知问题
在消息中输入 @群员昵称 并不能真正让该群友收到@提示(显示推送提示),微信App里是在@群员昵称后自动加上了一个特殊的显示空白的字符u’ ′。但是经测试,加上这个符号也不行,推测是微信Web API基于防范垃圾推送,屏蔽了群提示接口。
wxpy的bot在运行一段时间后会停止工作,出现连接服务器错误,必须重新登录,推测是微信Web API的Session安全机制导致的问题。
数据清洗
一段时间后大部分群友修改了昵称,于是有了在德中国程序员职业和专业方向的数据,经清洗后,导出CSV规格如下。
数据分析
该任务所需第三方库如下:
pip3 install pandas pip3 install matplotlib pip3 install jieba pip3 install wordcloud pip3 install seaborn pip3 install palettable