LearnPython/python_wechat.py

336 lines
13 KiB
Python
Raw Normal View History

2017-04-05 15:38:25 +08:00
# _*_ coding: utf-8 _*_
"""
python_wechat.py by xianhu
主要包括如下功能
(1) 自动提醒群红包
2017-06-25 11:23:36 +08:00
(2) 自动监测被撤回消息
(3) 群统计发言人数发言次数等
(4) 群关键字提醒群被@提醒
2017-04-05 15:38:25 +08:00
"""
import time
import itchat
import logging
from itchat.content import *
# 初始化
2017-04-08 21:56:15 +08:00
my = itchat.new_instance()
2017-06-25 11:23:36 +08:00
my.auto_login(hotReload=False, enableCmdQR=-2)
2017-06-23 15:44:04 +08:00
my.global_keys = ["创业", "算法", "人工智能", "机器学习"]
2017-04-08 21:56:15 +08:00
my.to_user_name = "filehelper"
2017-04-05 15:38:25 +08:00
2017-04-08 21:56:15 +08:00
# my还包括的以下属性注意用点.查看:
# (1) alive 是否还活着isLogging 是否已登陆
2017-05-16 12:03:28 +08:00
# (2) loginInfo 登陆信息其中的User属性为自己的信息User字典类包括UserName, NickName, RemarkName, Sex(1 or 2), Signature, Province, City等
# (3) memberList 通讯录列表每一项为一个User字典类包括UserName, NickName, RemarkName, Sex(1 or 2), Signature, Province, City等
# (4) chatroomList 群聊列表每一项为一个Chatroom字典类包括UserName, NickName, RemarkName, MemberCount, MemberList, Self等
2017-04-08 21:56:15 +08:00
# (5) mpList 订阅号列表每一项为一个MassivePlatform字典类包括UserName, NickName等
2017-04-05 15:38:25 +08:00
2017-04-08 21:56:15 +08:00
# 获取并更新通讯录: {UserName: UserInstance}
my.friends = {user["UserName"]: user for user in my.get_friends(update=True)}
2017-04-05 15:38:25 +08:00
# 消息存储队列
2017-04-08 21:56:15 +08:00
my.msg_store = {}
2017-04-05 15:38:25 +08:00
# 消息提取函数
def get_msg_list(msg):
"""
2017-05-03 17:53:44 +08:00
提取消息内容消息来源分类
1来自好友的消息
2017-06-25 11:23:36 +08:00
2来自群的消息
2017-05-16 12:03:28 +08:00
3来自文件传输助手的消息
2017-05-03 17:53:44 +08:00
提取消息内容消息类型分类
2017-06-25 11:23:36 +08:00
1文字2图片3语音4视频5地址6名片7提醒8分享9附件
2017-04-05 15:38:25 +08:00
"""
2017-06-25 11:23:36 +08:00
# logging.warning("message: %s", msg)
2017-04-08 17:15:24 +08:00
msg_id = msg["MsgId"] # 消息ID
2017-06-25 11:23:36 +08:00
from_user_name = msg["FromUserName"] # 消息发送者ID如果为群消息则为群ID
to_user_name = msg["ToUserName"] # 消息接受者ID如果为群消息则为群ID
2017-04-08 17:15:24 +08:00
msg_type = msg["MsgType"] # 消息类型
msg_content = msg["Content"] # 消息内容
2017-06-25 15:13:36 +08:00
msg_time = msg["CreateTime"] # 消息发送时间
2017-04-05 15:38:25 +08:00
2017-04-08 17:15:24 +08:00
msg_file = msg["FileName"] # 消息中所带文件的名称
2017-05-16 12:03:28 +08:00
msg_file_length = msg["FileSize"] # 消息中所带文件的大小
msg_voice_length = msg["VoiceLength"] # 消息中所带语音的长度(毫秒)
msg_play_length = msg["PlayLength"] # 消息中所带视频的长度(秒)
msg_url = msg["Url"] # 消息中所带链接的地址
2017-04-08 17:15:24 +08:00
2017-04-08 21:56:15 +08:00
wind_name = msg["User"]["RemarkName"] if msg["User"].get("RemarkName") else (
msg["User"]["NickName"] if msg["User"].get("NickName") else to_user_name
2017-06-25 11:23:36 +08:00
) # 窗口名称:群名或好友名
2017-04-08 21:56:15 +08:00
2017-06-25 11:23:36 +08:00
msg_user_name = from_user_name # 消息发送者的ID
msg_nick_name = wind_name # 消息发送者的昵称
if from_user_name.startswith("@@") or to_user_name.startswith("@@"):
msg_user_name = msg["ActualUserName"]
msg_nick_name = msg["ActualNickName"] if (msg_user_name not in my.friends) or (not my.friends[msg_user_name]["RemarkName"]) else my.friends[msg_user_name]["RemarkName"]
2017-04-08 17:15:24 +08:00
2017-06-25 11:23:36 +08:00
is_at = msg.get("IsAt", None) # 是否在群内被@
2017-04-08 17:15:24 +08:00
we_type = msg["Type"] # 消息类型
we_text = msg["Text"] # 消息内容
2017-04-05 15:38:25 +08:00
2017-06-25 15:13:36 +08:00
logging.warning("show: msg_nick_name=%s, wind_name=%s, we_type=%s, we_text=%s", msg_nick_name, wind_name, we_type, we_text)
2017-06-25 11:23:36 +08:00
return msg_id, from_user_name, to_user_name, msg_type, msg_content, msg_time, msg_file, msg_file_length, msg_voice_length, msg_play_length, msg_url, \
wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text
2017-04-05 15:38:25 +08:00
# 消息注册,主要处理群消息
2017-06-25 11:23:36 +08:00
@my.msg_register([TEXT, PICTURE, RECORDING, VIDEO, MAP, CARD, NOTE, SHARING, ATTACHMENT], isFriendChat=True, isGroupChat=True)
2017-04-05 15:38:25 +08:00
def text_reply(msg):
"""
2017-06-25 15:13:36 +08:00
消息自动接收, 接受全部的消息自己发送的消息除外
2017-04-05 15:38:25 +08:00
"""
2017-06-25 15:13:36 +08:00
# 跳过来自自己的消息
if msg["FromUserName"] == my.loginInfo["User"]["UserName"]:
return
2017-04-05 15:38:25 +08:00
# 消息提取
2017-06-25 15:13:36 +08:00
msg_list = get_msg_list(msg)
2017-06-25 11:23:36 +08:00
msg_id, from_user_name, to_user_name, msg_type, msg_content, msg_time, msg_file, msg_file_length, msg_voice_length, msg_play_length, msg_url, \
2017-06-25 15:13:36 +08:00
wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text = msg_list
2017-04-05 15:38:25 +08:00
2017-06-25 15:13:36 +08:00
# 消息过滤, 只监测文字、图片、语音、名片、注解、分享等
if we_type not in ["Text", "Picture", "Recording", "Card", "Note", "Sharing"]:
2017-05-03 17:53:44 +08:00
logging.warning("message type isn't included, ignored")
2017-04-08 17:15:24 +08:00
return
2017-04-08 21:56:15 +08:00
# 消息存储,删除过期消息
2017-06-25 15:13:36 +08:00
my.msg_store[msg_id] = msg_list
for _id in [_id for _id in my.msg_store if time.time() - my.msg_store[_id][5] > 120]:
2017-04-08 21:56:15 +08:00
my.msg_store.pop(_id)
2017-04-05 15:38:25 +08:00
2017-06-25 11:23:36 +08:00
# 保存消息中的内容(图片、语音等),不保存动态图片
if (we_type in ["Picture", "Recording"]) and (not msg_file.endswith(".gif")):
try:
we_text(".Cache/" + msg_file)
logging.warning("downloading %s to .Cache/", msg_file)
except Exception as excep:
logging.error("downloading %s to .Cache/ error: %s", msg_file, excep)
2017-06-25 15:13:36 +08:00
# ==== 处理群消息 ====
2017-04-05 15:38:25 +08:00
if from_user_name.startswith("@@"):
2017-06-25 11:23:36 +08:00
# ==== 处理红包消息 ====
2017-04-08 17:15:24 +08:00
if we_type == "Note" and we_text.find("收到红包,请在手机上查看") >= 0:
2017-06-25 15:13:36 +08:00
my.send("%s】中有人发红包啦,快抢!" % wind_name, toUserName=my.to_user_name)
2017-06-25 11:23:36 +08:00
# ==== 处理关键词消息 ====
for key in my.global_keys:
if we_type == "Text" and we_text.find(key) >= 0:
2017-06-25 15:13:36 +08:00
my.send("%s】中【%s】提及了关键字:%s" % (wind_name, msg_nick_name, key), toUserName=my.to_user_name)
2017-06-25 11:23:36 +08:00
my.send(we_text, toUserName=my.to_user_name)
break
# ==== 群内是否被@ ====
if we_type == "Text" and is_at:
my.send("%s】中【%s】@了你" % (wind_name, msg_nick_name), toUserName=my.to_user_name)
my.send(we_text, toUserName=my.to_user_name)
2017-04-05 15:38:25 +08:00
2017-06-25 11:23:36 +08:00
# ==== 撤回消息处理(必须为最后一步) ====
2017-04-08 17:15:24 +08:00
if we_type == "Note" and we_text.find("撤回了一条消息") >= 0:
2017-06-25 15:13:36 +08:00
msg_list = my.msg_store.get(msg_content[msg_content.find("<msgid>")+7: msg_content.find("</msgid>")])
if not msg_list:
2017-05-03 17:53:44 +08:00
logging.warning("not message id in my.msg_store")
2017-04-08 17:15:24 +08:00
return
2017-06-25 11:23:36 +08:00
msg_id, from_user_name, to_user_name, msg_type, msg_content, msg_time, msg_file, msg_file_length, msg_voice_length, msg_play_length, msg_url, \
2017-06-25 15:13:36 +08:00
wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text = msg_list
2017-06-25 11:23:36 +08:00
my.send("%s】中【%s】撤回了自己发送的消息:\nType: %s\nTime: %s\n%s" % (wind_name, msg_nick_name, we_type, msg_time, msg_file), toUserName=my.to_user_name)
2017-05-16 12:03:28 +08:00
2017-06-25 15:13:36 +08:00
if we_type in ["Text", "Card"]:
my.send(str(we_text), toUserName=my.to_user_name)
2017-04-08 17:15:24 +08:00
elif we_type == "Sharing":
2017-06-25 11:23:36 +08:00
my.send(we_text + "\n" + msg_url, toUserName=my.to_user_name)
2017-06-25 15:13:36 +08:00
elif we_type == "Recording":
my.send_file(".Cache/" + msg_file, toUserName=my.to_user_name)
elif we_type == "Picture" and (not msg_file.endswith(".gif")):
2017-06-25 11:23:36 +08:00
my.send_image(".Cache/" + msg_file, toUserName=my.to_user_name)
2017-04-05 15:38:25 +08:00
return
# 运行程序
2017-06-25 11:23:36 +08:00
my.run(debug=False)
"""
好友消息
{
'MsgId': '5254859004542036569',
'FromUserName': '@f3b7fdc54717ea8dc22cb3edef59688e82ef34874e3236801537b94f6cd73e1e',
'ToUserName': '@e79dde912b8f817514c01f399ca9ba12',
'MsgType': 1,
'Content': '[微笑]己改',
'Status': 3,
'ImgStatus': 1,
'CreateTime': 1498448860,
'VoiceLength': 0,
'PlayLength': 0,
'FileName': '',
'FileSize': '',
'MediaId': '',
'Url': '',
'AppMsgType': 0,
'StatusNotifyCode': 0,
'StatusNotifyUserName': '',
'HasProductId': 0,
'Ticket': '',
'ImgHeight': 0,
'ImgWidth': 0,
'SubMsgType': 0,
'NewMsgId': 5254859004542036569,
'OriContent': '',
'User': <User: {
'MemberList': <ContactList: []>,
'Uin': 0,
'UserName': '@f3b7fdc54717ea8dc22cb3edef59688e82ef34874e3236801537b94f6cd73e1e',
'NickName': '付贵吉祥',
'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=688475226&username=@f3b7fdc54717ea8dc22cb3edef59688e82ef34874e3236801537b94f6cd73e1e&skey=@',
'ContactFlag': 3,
'MemberCount': 0,
'RemarkName': '付贵吉祥@中建5号楼',
'HideInputBarFlag': 0,
'Sex': 1,
'Signature': '漫漫人生路...',
'VerifyFlag': 0,
'OwnerUin': 0,
'PYInitial': 'FGJX',
'PYQuanPin': 'fuguijixiang',
'RemarkPYInitial': 'FGJXZJ5HL',
'RemarkPYQuanPin': 'fuguijixiangzhongjian5haolou',
'StarFriend': 0,
'AppAccountFlag': 0,
'Statues': 0,
'AttrStatus': 135205,
'Province': '山东',
'City': '',
'Alias': '',
'SnsFlag': 17,
'UniFriend': 0,
'DisplayName': '',
'ChatRoomId': 0,
'KeyWord': '',
'EncryChatRoomId': '',
'IsOwner': 0
}>,
'Type': 'Text',
'Text': '[微笑]己改'
}
"""
"""
2017-06-25 15:13:36 +08:00
群消息
2017-06-25 11:23:36 +08:00
{
'MsgId': '7844877618948840992',
'FromUserName': '@@8dc5df044444d1fb8e3972e755b47adf9d07f5a032cae90a4d822b74ee1e4880',
'ToUserName': '@e79dde912b8f817514c01f399ca9ba12',
'MsgType': 1,
'Content': '就是那个,那个协议我们手上有吗',
'Status': 3,
'ImgStatus': 1,
'CreateTime': 1498448972,
'VoiceLength': 0,
'PlayLength': 0,
'FileName': '',
'FileSize': '',
'MediaId': '',
'Url': '',
'AppMsgType': 0,
'StatusNotifyCode': 0,
'StatusNotifyUserName': '',
'HasProductId': 0,
'Ticket': '',
'ImgHeight': 0,
'ImgWidth': 0,
'SubMsgType': 0,
'NewMsgId': 7844877618948840992,
'OriContent': '',
'ActualNickName': '5-1-1003',
'IsAt': False,
'ActualUserName': '@a0922f18795e4c3b6d7d09c492ace233',
'User': <Chatroom: {
'MemberList': <ContactList: [
<ChatroomMember: {
'MemberList': <ContactList: []>,
'Uin': 0,
'UserName': '@e79dde912b8f817514c01f399ca9ba12',
'NickName': '齐现虎',
'AttrStatus': 2147600869,
'PYInitial': '',
'PYQuanPin': '',
'RemarkPYInitial': '',
'RemarkPYQuanPin': '',
'MemberStatus': 0,
'DisplayName': '5-1-1601',
'KeyWord': 'qix'
}>,
<ChatroomMember: {
'MemberList': <ContactList: []>,
'Uin': 0,
'UserName': '@a9620e3d4b82eab2521ccdbb985afc37',
'NickName': 'A高佳祥15069179911',
'AttrStatus': 102503,
'PYInitial': '',
'PYQuanPin': '',
'RemarkPYInitial': '',
'RemarkPYQuanPin': '',
'MemberStatus': 0,
'DisplayName': '5-2-220315069179911',
'KeyWord': 'gao'
}>,
.......
]>,
'Uin': 0,
'UserName': '@@8dc5df044444d1fb8e3972e755b47adf9d07f5a032cae90a4d822b74ee1e4880',
'NickName': '中建锦绣澜庭二期5#楼',
'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=0&username=@@8dc5df044444d1fb8e3972e755b47adf9d07f5a032cae90a4d822b74ee1e4880&skey=@',
'ContactFlag': 3,
'MemberCount': 106,
'RemarkName': '',
'HideInputBarFlag': 0,
'Sex': 0,
'Signature': '',
'VerifyFlag': 0,
'OwnerUin': 0,
'PYInitial': 'ZJJXLTEJ5L',
'PYQuanPin': 'zhongjianjinxiulantingerji5lou',
'RemarkPYInitial': '',
'RemarkPYQuanPin': '',
'StarFriend': 0,
'AppAccountFlag': 0,
'Statues': 0,
'AttrStatus': 0,
'Province': '',
'City': '',
'Alias': '',
'SnsFlag': 0,
'UniFriend': 0,
'DisplayName': '',
'ChatRoomId': 0,
'KeyWord': '',
'EncryChatRoomId': '@d1e510bc8cbd192468e9c85c6f5a9d81',
'IsOwner': 1,
'IsAdmin': None,
'Self': <ChatroomMember: {
'MemberList': <ContactList: []>,
'Uin': 0,
'UserName': '@e79dde912b8f817514c01f399ca9ba12',
'NickName': '齐现虎',
'AttrStatus': 2147600869,
'PYInitial': '',
'PYQuanPin': '',
'RemarkPYInitial': '',
'RemarkPYQuanPin': '',
'MemberStatus': 0,
'DisplayName': '5-1-1601',
'KeyWord': 'qix'
}>,
'HeadImgUpdateFlag': 1,
'ContactType': 0,
'ChatRoomOwner': '@e79dde912b8f817514c01f399ca9ba12'
}>,
'Type': 'Text',
'Text': '就是那个,那个协议我们手上有吗'
}
"""