|
17 | 17 | # 初始化 |
18 | 18 | my = itchat.new_instance() |
19 | 19 | my.auto_login(hotReload=False, enableCmdQR=-2) |
20 | | -my.global_keys = ["创业", "算法", "人工智能", "机器学习"] |
21 | | -my.to_user_name = "filehelper" |
22 | | - |
23 | 20 | # my还包括的以下属性,注意用点.查看: |
24 | 21 | # (1) alive 是否还活着,isLogging 是否已登陆 |
25 | 22 | # (2) loginInfo 登陆信息,其中的User属性为自己的信息User字典类,包括UserName, NickName, RemarkName, Sex(1 or 2), Signature, Province, City等 |
26 | 23 | # (3) memberList 通讯录列表,每一项为一个User字典类,包括UserName, NickName, RemarkName, Sex(1 or 2), Signature, Province, City等 |
27 | 24 | # (4) chatroomList 群聊列表,每一项为一个Chatroom字典类,包括UserName, NickName, RemarkName, MemberCount, MemberList, Self等 |
28 | 25 | # (5) mpList 订阅号列表,每一项为一个MassivePlatform字典类,包括UserName, NickName等 |
29 | 26 |
|
30 | | -# 获取并更新通讯录: {UserName: UserInstance} |
31 | | -my.friends = {user["UserName"]: user for user in my.get_friends(update=True)} |
| 27 | +my.global_keys = ["创业", "算法", "人工智能", "机器学习"] |
| 28 | +my.to_user_name = "filehelper" # 消息接受者 |
| 29 | +my.update_time = time.time() # 信息更新时间 |
| 30 | +my.msg_store = {} # 消息存储队列 |
| 31 | +my.friends = {} # 好友字典列表 |
| 32 | +my.groups = {} # 群聊字典列表 |
32 | 33 |
|
33 | | -# 消息存储队列 |
34 | | -my.msg_store = {} |
| 34 | + |
| 35 | +def update_my_infos(): |
| 36 | + """ |
| 37 | + 更新信息 |
| 38 | + """ |
| 39 | + # 获取并更新通讯录: {UserName: UserInstance} |
| 40 | + my.friends = {user["UserName"]: user for user in my.get_friends(update=True)} |
| 41 | + # 获取并更新群列表: {UserName: UserInstance} |
| 42 | + my.groups = {group["UserName"]: group for group in my.get_chatrooms(update=True)} |
| 43 | + return |
| 44 | +update_my_infos() |
35 | 45 |
|
36 | 46 |
|
37 | | -# 消息提取函数 |
38 | | -def get_msg_list(msg): |
| 47 | +class Message(object): |
39 | 48 | """ |
40 | | - 提取消息内容,消息来源分类: |
41 | | - (1)来自好友的消息 |
42 | | - (2)来自群的消息 |
43 | | - (3)来自文件传输助手的消息 |
44 | | - 提取消息内容,消息类型分类: |
45 | | - (1)文字(2)图片(3)语音(4)视频(5)地址(6)名片(7)提醒(8)分享(9)附件 |
| 49 | + 消息类 |
46 | 50 | """ |
47 | | - # logging.warning("message: %s", msg) |
48 | | - msg_id = msg["MsgId"] # 消息ID |
49 | | - from_user_name = msg["FromUserName"] # 消息发送者ID,如果为群消息,则为群ID |
50 | | - to_user_name = msg["ToUserName"] # 消息接受者ID,如果为群消息,则为群ID |
| 51 | + def __init__(self, msg): |
| 52 | + """ |
| 53 | + 构造函数:提取消息内容 |
| 54 | + 消息来源分类: |
| 55 | + (1)来自好友的消息 |
| 56 | + (2)来自群的消息 |
| 57 | + 提取消息内容,消息类型分类: |
| 58 | + (1)文字(2)图片(3)语音(4)视频(5)地址(6)名片(7)提醒(8)分享(9)附件 |
| 59 | + """ |
| 60 | + # 更新信息,十分钟更新一次 |
| 61 | + # logging.warning("message: %s", msg) |
| 62 | + if time.time() - my.update_time > 600: |
| 63 | + update_my_infos() |
| 64 | + my.update_time = time.time() |
51 | 65 |
|
52 | | - msg_type = msg["MsgType"] # 消息类型 |
53 | | - msg_content = msg["Content"] # 消息内容 |
54 | | - msg_time = msg["CreateTime"] # 消息发送时间 |
| 66 | + self.msg_id = msg["MsgId"] # 消息ID |
| 67 | + self.from_user_name = msg["FromUserName"] # 消息发送者ID,如果为群消息,则为群ID |
55 | 68 |
|
56 | | - msg_file = msg["FileName"] # 消息中所带文件的名称 |
57 | | - msg_file_length = msg["FileSize"] # 消息中所带文件的大小 |
58 | | - msg_voice_length = msg["VoiceLength"] # 消息中所带语音的长度(毫秒) |
59 | | - msg_play_length = msg["PlayLength"] # 消息中所带视频的长度(秒) |
60 | | - msg_url = msg["Url"] # 消息中所带链接的地址 |
| 69 | + self.msg_type = msg["MsgType"] # 消息类型,这里参考下边的we_type |
| 70 | + self.msg_content = msg["Content"] # 消息内容,这里参考下边的we_text |
| 71 | + self.msg_time = msg["CreateTime"] # 消息发送时间,时间戳格式 |
61 | 72 |
|
62 | | - wind_name = msg["User"]["RemarkName"] if msg["User"].get("RemarkName") else ( |
63 | | - msg["User"]["NickName"] if msg["User"].get("NickName") else to_user_name |
64 | | - ) # 窗口名称:群名或好友名 |
| 73 | + self.msg_file = msg["FileName"] # 消息中所带文件的名称 |
| 74 | + self.msg_file_length = msg["FileSize"] # 消息中所带文件的大小,字符串类型 |
| 75 | + self.msg_voice_length = msg["VoiceLength"] # 消息中所带语音的长度(毫秒) |
| 76 | + self.msg_play_length = msg["PlayLength"] # 消息中所带视频的长度(秒) |
| 77 | + self.msg_url = msg["Url"] # 消息中所带链接的地址 |
65 | 78 |
|
66 | | - msg_user_name = from_user_name # 消息发送者的ID |
67 | | - msg_nick_name = wind_name # 消息发送者的昵称 |
68 | | - if from_user_name.startswith("@@") or to_user_name.startswith("@@"): |
69 | | - msg_user_name = msg["ActualUserName"] |
70 | | - 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"] |
| 79 | + self.user_user_name = msg["User"].get("UserName", "") # 消息发送者ID,如果为群消息,则为群ID |
| 80 | + self.user_nick_name = msg["User"].get("NickName", "") # 消息发送者昵称,如果为群消息,则为群名 |
| 81 | + self.user_remark_name = msg["User"].get("RemarkName", "") # 消息发送者备注名称,如果为群消息,则为群备注名称 |
| 82 | + self.wind_name = self.user_remark_name if self.user_remark_name else ( |
| 83 | + self.user_nick_name if self.user_nick_name else ( |
| 84 | + my.friends[self.user_user_name]["NickName"] if self.user_user_name in my.friends else ( |
| 85 | + my.groups[self.user_user_name]["NickName"] if self.user_user_name in my.groups else "未知窗口" |
| 86 | + ) |
| 87 | + ) |
| 88 | + ) |
71 | 89 |
|
72 | | - is_at = msg.get("IsAt", None) # 是否在群内被@ |
73 | | - we_type = msg["Type"] # 消息类型 |
74 | | - we_text = msg["Text"] # 消息内容 |
| 90 | + self.actual_user_name = msg.get("ActualUserName", "") # 群消息中,消息发送者的ID |
| 91 | + self.actual_nick_name = msg.get("ActualNickName", "") # 群消息中,消息发送者的群昵称 |
| 92 | + self.actual_remark_name = self.actual_nick_name \ |
| 93 | + if (self.actual_user_name not in my.friends) or (not my.friends[self.actual_user_name]["RemarkName"]) \ |
| 94 | + else my.friends[self.actual_user_name]["RemarkName"] |
75 | 95 |
|
76 | | - 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) |
77 | | - 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, \ |
78 | | - wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text |
| 96 | + self.is_at = msg.get("IsAt", None) # 是否在群内被@ |
| 97 | + self.we_type = msg["Type"] # 消息类型 |
| 98 | + self.we_text = msg["Text"] # 消息内容 |
79 | 99 |
|
| 100 | + logging.warning("show: wind_name=%s, actual_send_name=%s, we_type=%s, we_text=%s", self.wind_name, self.actual_remark_name, self.we_type, self.we_text) |
| 101 | + return |
80 | 102 |
|
81 | | -# 消息注册,主要处理群消息 |
82 | | -@my.msg_register([TEXT, PICTURE, RECORDING, VIDEO, MAP, CARD, NOTE, SHARING, ATTACHMENT], isFriendChat=True, isGroupChat=True) |
83 | | -def text_reply(msg): |
| 103 | + |
| 104 | +def process_message_group(msg): |
84 | 105 | """ |
85 | | - 消息自动接收, 接受全部的消息(自己发送的消息除外) |
| 106 | + 处理群消息 |
86 | 107 | """ |
87 | | - # 跳过来自自己的消息 |
88 | | - if msg["FromUserName"] == my.loginInfo["User"]["UserName"]: |
| 108 | + # 消息过滤, 只监测文字、图片、语音、名片、注解、分享等 |
| 109 | + if msg.we_type not in ["Text", "Picture", "Recording", "Card", "Note", "Sharing"]: |
| 110 | + logging.warning("process_message_group: message type isn't included, ignored") |
89 | 111 | return |
90 | 112 |
|
91 | | - # 消息提取 |
92 | | - msg_list = get_msg_list(msg) |
93 | | - 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, \ |
94 | | - wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text = msg_list |
| 113 | + # ==== 处理红包消息 ==== |
| 114 | + if msg.we_type == "Note" and msg.we_text.find("收到红包,请在手机上查看") >= 0: |
| 115 | + my.send("【%s】中有人发红包啦,快抢!" % msg.wind_name, toUserName=my.to_user_name) |
| 116 | + |
| 117 | + # ==== 处理关键词消息 ==== |
| 118 | + for key in my.global_keys: |
| 119 | + if msg.we_type == "Text" and msg.we_text.find(key) >= 0: |
| 120 | + my.send("【%s】中【%s】提及了关键字:%s" % (msg.wind_name, msg.actual_remark_name, key), toUserName=my.to_user_name) |
| 121 | + my.send(msg.we_text, toUserName=my.to_user_name) |
| 122 | + break |
| 123 | + |
| 124 | + # ==== 群内是否被@ ==== |
| 125 | + if msg.we_type == "Text" and msg.is_at: |
| 126 | + my.send("【%s】中【%s】@了你" % (msg.wind_name, msg.actual_remark_name), toUserName=my.to_user_name) |
| 127 | + my.send(msg.we_text, toUserName=my.to_user_name) |
| 128 | + return |
| 129 | + |
95 | 130 |
|
| 131 | +def process_message_revoke(msg): |
| 132 | + """ |
| 133 | + 处理撤回消息 |
| 134 | + """ |
96 | 135 | # 消息过滤, 只监测文字、图片、语音、名片、注解、分享等 |
97 | | - if we_type not in ["Text", "Picture", "Recording", "Card", "Note", "Sharing"]: |
98 | | - logging.warning("message type isn't included, ignored") |
| 136 | + if msg.we_type not in ["Text", "Picture", "Recording", "Card", "Note", "Sharing"]: |
| 137 | + logging.warning("process_message_revoke: message type isn't included, ignored") |
99 | 138 | return |
100 | 139 |
|
101 | 140 | # 消息存储,删除过期消息 |
102 | | - my.msg_store[msg_id] = msg_list |
103 | | - for _id in [_id for _id in my.msg_store if time.time() - my.msg_store[_id][5] > 120]: |
| 141 | + my.msg_store[msg.msg_id] = msg |
| 142 | + for _id in [_id for _id in my.msg_store if time.time() - my.msg_store[_id].msg_time > 120]: |
104 | 143 | my.msg_store.pop(_id) |
105 | 144 |
|
106 | | - # 保存消息中的内容(图片、语音等),不保存动态图片 |
107 | | - if (we_type in ["Picture", "Recording"]) and (not msg_file.endswith(".gif")): |
| 145 | + # 保存消息中的内容(图片、语音等) |
| 146 | + if msg.we_type in ["Picture", "Recording"]: |
108 | 147 | try: |
109 | | - we_text(".Cache/" + msg_file) |
110 | | - logging.warning("downloading %s to .Cache/", msg_file) |
| 148 | + msg.we_text(".Cache/" + msg.msg_file) |
| 149 | + logging.warning("process_message_revoke: download %s to .Cache/", msg.msg_file) |
111 | 150 | except Exception as excep: |
112 | | - logging.error("downloading %s to .Cache/ error: %s", msg_file, excep) |
113 | | - |
114 | | - # ==== 处理群消息 ==== |
115 | | - if from_user_name.startswith("@@"): |
116 | | - # ==== 处理红包消息 ==== |
117 | | - if we_type == "Note" and we_text.find("收到红包,请在手机上查看") >= 0: |
118 | | - my.send("【%s】中有人发红包啦,快抢!" % wind_name, toUserName=my.to_user_name) |
119 | | - # ==== 处理关键词消息 ==== |
120 | | - for key in my.global_keys: |
121 | | - if we_type == "Text" and we_text.find(key) >= 0: |
122 | | - my.send("【%s】中【%s】提及了关键字:%s" % (wind_name, msg_nick_name, key), toUserName=my.to_user_name) |
123 | | - my.send(we_text, toUserName=my.to_user_name) |
124 | | - break |
125 | | - # ==== 群内是否被@ ==== |
126 | | - if we_type == "Text" and is_at: |
127 | | - my.send("【%s】中【%s】@了你" % (wind_name, msg_nick_name), toUserName=my.to_user_name) |
128 | | - my.send(we_text, toUserName=my.to_user_name) |
| 151 | + logging.error("process_message_revoke: download %s to .Cache/ error: %s", msg.msg_file, excep) |
129 | 152 |
|
130 | 153 | # ==== 撤回消息处理(必须为最后一步) ==== |
131 | | - if we_type == "Note" and we_text.find("撤回了一条消息") >= 0: |
132 | | - msg_list = my.msg_store.get(msg_content[msg_content.find("<msgid>")+7: msg_content.find("</msgid>")]) |
133 | | - if not msg_list: |
134 | | - logging.warning("not message id in my.msg_store") |
| 154 | + if msg.we_type == "Note" and msg.we_text.find("撤回了一条消息") >= 0: |
| 155 | + old_msg = my.msg_store.get(msg.msg_content[msg.msg_content.find("<msgid>")+7: msg.msg_content.find("</msgid>")]) |
| 156 | + if not old_msg: |
| 157 | + logging.warning("process_message_revoke: no message id in my.msg_store") |
135 | 158 | return |
136 | 159 |
|
137 | | - 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, \ |
138 | | - wind_name, msg_user_name, msg_nick_name, is_at, we_type, we_text = msg_list |
139 | | - 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) |
| 160 | + if old_msg.from_user_name.startswith("@@"): |
| 161 | + my.send("【%s】中【%s】撤回了自己发送的消息:\nType: %s\nTime: %s\n%s" % |
| 162 | + (old_msg.wind_name, old_msg.actual_remark_name, old_msg.we_type, old_msg.msg_time, old_msg.msg_file), toUserName=my.to_user_name) |
| 163 | + else: |
| 164 | + my.send("【%s】撤回了自己发送的消息:\nType: %s\nTime: %s\n%s" % |
| 165 | + (old_msg.wind_name, old_msg.we_type, old_msg.msg_time, old_msg.msg_file), toUserName=my.to_user_name) |
| 166 | + |
| 167 | + if old_msg.we_type in ["Text", "Card"]: |
| 168 | + my.send(str(old_msg.we_text), toUserName=my.to_user_name) |
| 169 | + elif old_msg.we_type == "Sharing": |
| 170 | + my.send(old_msg.we_text + "\n" + old_msg.msg_url, toUserName=my.to_user_name) |
| 171 | + elif old_msg.we_type == "Picture": |
| 172 | + my.send_image(".Cache/" + old_msg.msg_file, toUserName=my.to_user_name) |
| 173 | + elif old_msg.we_type == "Recording": |
| 174 | + my.send_file(".Cache/" + old_msg.msg_file, toUserName=my.to_user_name) |
| 175 | + return |
| 176 | + |
| 177 | + |
| 178 | +@my.msg_register([TEXT, PICTURE, RECORDING, VIDEO, MAP, CARD, NOTE, SHARING, ATTACHMENT], isFriendChat=True, isGroupChat=True) |
| 179 | +def text_reply(msg): |
| 180 | + """ |
| 181 | + 消息自动接收, 接受全部的消息(自己发送的消息除外) |
| 182 | + """ |
| 183 | + # 跳过来自自己的消息 |
| 184 | + if msg["FromUserName"] == my.loginInfo["User"]["UserName"]: |
| 185 | + return |
| 186 | + |
| 187 | + # 消息提取 |
| 188 | + msg = Message(msg) |
140 | 189 |
|
141 | | - if we_type in ["Text", "Card"]: |
142 | | - my.send(str(we_text), toUserName=my.to_user_name) |
143 | | - elif we_type == "Sharing": |
144 | | - my.send(we_text + "\n" + msg_url, toUserName=my.to_user_name) |
145 | | - elif we_type == "Recording": |
146 | | - my.send_file(".Cache/" + msg_file, toUserName=my.to_user_name) |
147 | | - elif we_type == "Picture" and (not msg_file.endswith(".gif")): |
148 | | - my.send_image(".Cache/" + msg_file, toUserName=my.to_user_name) |
| 190 | + # 处理群消息 |
| 191 | + if msg.from_user_name.startswith("@@"): |
| 192 | + process_message_group(msg) |
149 | 193 |
|
| 194 | + # 处理撤回消息 |
| 195 | + process_message_revoke(msg) |
150 | 196 | return |
151 | 197 |
|
152 | 198 |
|
@@ -332,4 +378,75 @@ def text_reply(msg): |
332 | 378 | 'Type': 'Text', |
333 | 379 | 'Text': '就是那个,那个协议我们手上有吗' |
334 | 380 | } |
| 381 | +
|
| 382 | +警示消息:好友类 |
| 383 | +{ |
| 384 | + 'MsgId': '1529895072288746571', |
| 385 | + 'FromUserName': '@4076708be2e09ef83f249f168553d0dd55b4f734aee7d276e92ddbe98625476a', |
| 386 | + 'ToUserName': '@f97583d8ffbaee6189854116897c677f', |
| 387 | + 'MsgType': 10000, |
| 388 | + 'Content': '你已添加了呼啸而过的小青春,现在可以开始聊天了。', |
| 389 | + 'Status': 4, |
| 390 | + 'ImgStatus': 1, |
| 391 | + 'CreateTime': 1498533407, |
| 392 | + 'VoiceLength': 0, |
| 393 | + 'PlayLength': 0, |
| 394 | + 'FileName': '', |
| 395 | + 'FileSize': '', |
| 396 | + 'MediaId': '', |
| 397 | + 'Url': '', |
| 398 | + 'AppMsgType': 0, |
| 399 | + 'StatusNotifyCode': 0, |
| 400 | + 'StatusNotifyUserName': '', |
| 401 | + 'HasProductId': 0, |
| 402 | + 'Ticket': '', |
| 403 | + 'ImgHeight': 0, |
| 404 | + 'ImgWidth': 0, |
| 405 | + 'SubMsgType': 0, |
| 406 | + 'NewMsgId': 1529895072288746571, |
| 407 | + 'OriContent': '', |
| 408 | + 'User': <User: { |
| 409 | + 'userName': '@4076708be2e09ef83f249f168553d0dd55b4f734aee7d276e92ddbe98625476a', |
| 410 | + 'MemberList': <ContactList: []> |
| 411 | + }>, |
| 412 | + 'Type': 'Note', |
| 413 | + 'Text': '你已添加了呼啸而过的小青春,现在可以开始聊天了。' |
| 414 | +} |
| 415 | +
|
| 416 | +警示消息:群类 |
| 417 | +{ |
| 418 | + 'MsgId': '1049646282086057263', |
| 419 | + 'FromUserName': '@@300f57b68ca7ef593ae3221eef7dba5377466c86122aaa15a8ffc1031310e210', |
| 420 | + 'ToUserName': '@006f63e8086ab07fcbe3771dc824c4a6', |
| 421 | + 'MsgType': 10000, |
| 422 | + 'Content': '你邀请"大姐"加入了群聊', |
| 423 | + 'Status': 3, |
| 424 | + 'ImgStatus': 1, |
| 425 | + 'CreateTime': 1498533901, |
| 426 | + 'VoiceLength': 0, |
| 427 | + 'PlayLength': 0, |
| 428 | + 'FileName': '', |
| 429 | + 'FileSize': '', |
| 430 | + 'MediaId': '', |
| 431 | + 'Url': '', |
| 432 | + 'AppMsgType': 0, |
| 433 | + 'StatusNotifyCode': 0, |
| 434 | + 'StatusNotifyUserName': '', |
| 435 | + 'HasProductId': 0, |
| 436 | + 'Ticket': '', |
| 437 | + 'ImgHeight': 0, |
| 438 | + 'ImgWidth': 0, |
| 439 | + 'SubMsgType': 0, |
| 440 | + 'NewMsgId': 1049646282086057263, |
| 441 | + 'OriContent': '', |
| 442 | + 'ActualUserName': '@006f63e8086ab07fcbe3771dc824c4a6', |
| 443 | + 'ActualNickName': '某某某', |
| 444 | + 'IsAt': False, |
| 445 | + 'User': <Chatroom: { |
| 446 | + 'UserName': '@@300f57b68ca7ef593ae3221eef7dba5377466c86122aaa15a8ffc1031310e210', |
| 447 | + 'MemberList': <ContactList: []> |
| 448 | + }>, |
| 449 | + 'Type': 'Note', |
| 450 | + 'Text': '你邀请"大姐"加入了群聊' |
| 451 | +} |
335 | 452 | """ |
0 commit comments