diff --git a/README.md b/README.md
index 0bb6a6b..c823bfe 100644
--- a/README.md
+++ b/README.md
@@ -41,12 +41,14 @@ Web微信协议参考资料:
[qwx: WeChat Qt frontend 微信Qt前端](https://github.com/xiangzhai/qwx)
+**master-dev 分支为开发版本,用于测试新特性,欢迎使用后提出建议!**
+
## 1 环境与依赖
此版本只能运行于Python 2环境 。
-**wxBot** 用到了Python **requests** , **pypng** , **Pillow* 以及 **pyqrcode** 库。
+**wxBot** 用到了Python **requests** , **pypng** , **Pillow** 以及 **pyqrcode** 库。
使用之前需要所依赖的库:
@@ -162,6 +164,7 @@ python test.py
| 10 | 撤回消息 | 不可用 |
| 11 | 空内容 | 空字符串 |
| 12 | 红包 | 不可用 |
+| 13 | 小视频 | 字符串,视频数据的url,HTTP POST请求此url可以得到mp4文件格式的数据 |
| 99 | 未知类型 | 不可用 |
### 4.4 群文本消息
@@ -200,6 +203,7 @@ python test.py
| `get_head_img(id)` | 获取用户头像并保存到本地文件 ***img_[id].jpg*** ,`id` 为用户id(Web微信数据) |
| `get_msg_img(msgid)` | 获取图像消息并保存到本地文件 ***img_[msgid].jpg*** , `msgid` 为消息id(Web微信数据) |
| `get_voice(msgid)` | 获取语音消息并保存到本地文件 ***voice_[msgid].mp3*** , `msgid` 为消息id(Web微信数据) |
+| `get_video(msgid)` | 获取视频消息并保存到本地文件 ***video_[msgid].mp4*** , `msgid` 为消息id(Web微信数据) |
| `get_contact_name(uid)` | 获取微信id对应的名称,返回一个可能包含 `remark_name` (备注名), `nickname` (昵称), `display_name` (群名称)的字典|
| `send_msg_by_uid(word, dst)` | 向好友发送消息,`word` 为消息字符串,`dst` 为好友用户id(Web微信数据) |
| `send_img_msg_by_uid(fpath, dst)` | 向好友发送图片消息,`fpath` 为本地图片文件路径,`dst` 为好友用户id(Web微信数据) |
@@ -264,3 +268,9 @@ python test.py
[Urinx/WeixinBot](https://github.com/Urinx/WeixinBot) 网页版微信API,包含终端版微信及微信机器人
[zixia/wechaty](https://github.com/zixia/wechaty) Wechaty is wechat for bot in Javascript(ES6). It's a Personal Account Robot Framework/Library.
+
+## 7 交流讨论
+
+问题可以直接开 **issue**
+
+**QQ** 交流群: **429134510**
diff --git a/bot.py b/bot.py
index fe755c1..bd56c88 100644
--- a/bot.py
+++ b/bot.py
@@ -31,6 +31,7 @@ def tuling_auto_reply(self, uid, msg):
result = ''
if respond['code'] == 100000:
result = respond['text'].replace('
', ' ')
+ result = result.replace(u'\xa0', u' ')
elif respond['code'] == 200000:
result = respond['url']
elif respond['code'] == 302000:
@@ -39,6 +40,7 @@ def tuling_auto_reply(self, uid, msg):
k['article'] + "\t" + k['detailurl'] + "\n"
else:
result = respond['text'].replace('
', ' ')
+ result = result.replace(u'\xa0', u' ')
print ' ROBOT:', result
return result
@@ -69,7 +71,7 @@ def handle_msg_all(self, msg):
self.send_msg_by_uid(self.tuling_auto_reply(msg['user']['id'], msg['content']['data']), msg['user']['id'])
elif msg['msg_type_id'] == 3 and msg['content']['type'] == 0: # group text message
if 'detail' in msg['content']:
- my_names = self.get_group_member_name(self.my_account['UserName'], msg['user']['id'])
+ my_names = self.get_group_member_name(msg['user']['id'], self.my_account['UserName'])
if my_names is None:
my_names = {}
if 'NickName' in self.my_account and self.my_account['NickName']:
diff --git a/test.py b/test.py
index c81a60f..e89b4f1 100644
--- a/test.py
+++ b/test.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python
# coding: utf-8
+#
from wxbot import *
@@ -21,6 +22,7 @@ def main():
bot = MyWXBot()
bot.DEBUG = True
bot.conf['qr'] = 'png'
+ bot.is_big_contact = False #如果确定通讯录过大,无法获取,可以直接配置,跳过检查。假如不是过大的话,这个方法可能无法获取所有的联系人
bot.run()
diff --git a/wxbot.py b/wxbot.py
index c394bb3..bad673c 100644
--- a/wxbot.py
+++ b/wxbot.py
@@ -23,6 +23,9 @@
SCANED = '201'
TIMEOUT = '408'
+def map_username_batch(user_name):
+ return {"UserName": user_name, "EncryChatRoomId": ""}
+
def show_image(file_path):
"""
@@ -54,6 +57,14 @@ def request(self, method, url, params=None, data=None, headers=None, cookies=Non
print e.message, traceback.format_exc()
continue
+ #重试3次以后再加一次,抛出异常
+ try:
+ return super(SafeSession, self).request(method, url, params, data, headers, cookies, files, auth,
+ timeout,
+ allow_redirects, proxies, hooks, stream, verify, cert, json)
+ except Exception as e:
+ raise e
+
class WXBot:
"""WXBot功能类"""
@@ -62,6 +73,7 @@ def __init__(self):
self.DEBUG = False
self.uuid = ''
self.base_uri = ''
+ self.base_host = ''
self.redirect_uri = ''
self.uin = ''
self.sid = ''
@@ -73,6 +85,12 @@ def __init__(self):
self.sync_key = []
self.sync_host = ''
+
+ self.batch_count = 50 #一次拉取50个联系人的信息
+ self.full_user_name_list = [] #直接获取不到通讯录时,获取的username列表
+ self.wxid_list = [] #获取到的wxid的列表
+ self.cursor = 0 #拉取联系人信息的游标
+ self.is_big_contact = False #通讯录人数过多,无法直接获取
#文件缓存目录
self.temp_pwd = os.path.join(os.getcwd(),'temp')
if os.path.exists(self.temp_pwd) == False:
@@ -118,15 +136,23 @@ def to_unicode(string, encoding='utf-8'):
def get_contact(self):
"""获取当前账户的所有相关账号(包括联系人、公众号、群聊、特殊账号)"""
+ if self.is_big_contact:
+ return False
url = self.base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' \
% (self.pass_ticket, self.skey, int(time.time()))
- r = self.session.post(url, data='{}')
+
+ #如果通讯录联系人过多,这里会直接获取失败
+ try:
+ r = self.session.post(url, data='{}')
+ except Exception as e:
+ self.is_big_contact = True
+ return False
r.encoding = 'utf-8'
if self.DEBUG:
with open(os.path.join(self.temp_pwd,'contacts.json'), 'w') as f:
f.write(r.text.encode('utf-8'))
dic = json.loads(r.text)
- self.member_list = dic['MemberList']
+ self.member_list.extend(dic['MemberList'])
special_users = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail',
'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle',
@@ -183,6 +209,100 @@ def get_contact(self):
f.write(json.dumps(self.account_info))
return True
+
+ def get_big_contact(self):
+ total_len = len(self.full_user_name_list)
+ user_info_list = []
+
+ #一次拉取50个联系人的信息,包括所有的群聊,公众号,好友
+ while self.cursor < total_len:
+ cur_batch = self.full_user_name_list[self.cursor:(self.cursor+self.batch_count)]
+ self.cursor += self.batch_count
+ cur_batch = map(map_username_batch, cur_batch)
+ user_info_list += self.batch_get_contact(cur_batch)
+ print "[INFO] Get batch contacts"
+
+ self.member_list = user_info_list
+ special_users = ['newsapp', 'filehelper', 'weibo', 'qqmail',
+ 'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle',
+ 'lbsapp', 'shakeapp', 'medianote', 'qqfriend', 'readerapp',
+ 'blogapp', 'facebookapp', 'masssendapp', 'meishiapp',
+ 'feedsapp', 'voip', 'blogappweixin', 'weixin', 'brandsessionholder',
+ 'weixinreminder', 'wxid_novlwrv3lqwv11',
+ 'officialaccounts',
+ 'gh_22b87fa7cb3c', 'wxitil', 'userexperience_alarm', 'notification_messages', 'notifymessage']
+
+ self.contact_list = []
+ self.public_list = []
+ self.special_list = []
+ self.group_list = []
+ for i, contact in enumerate(self.member_list):
+ if contact['VerifyFlag'] & 8 != 0: # 公众号
+ self.public_list.append(contact)
+ self.account_info['normal_member'][contact['UserName']] = {'type': 'public', 'info': contact}
+ elif contact['UserName'] in special_users or self.wxid_list[i] in special_users: # 特殊账户
+ self.special_list.append(contact)
+ self.account_info['normal_member'][contact['UserName']] = {'type': 'special', 'info': contact}
+ elif contact['UserName'].find('@@') != -1: # 群聊
+ self.group_list.append(contact)
+ self.account_info['normal_member'][contact['UserName']] = {'type': 'group', 'info': contact}
+ elif contact['UserName'] == self.my_account['UserName']: # 自己
+ self.account_info['normal_member'][contact['UserName']] = {'type': 'self', 'info': contact}
+ else:
+ self.contact_list.append(contact)
+ self.account_info['normal_member'][contact['UserName']] = {'type': 'contact', 'info': contact}
+ group_members = {}
+ encry_chat_room_id = {}
+ for group in self.group_list:
+ gid = group['UserName']
+ members = group['MemberList']
+ group_members[gid] = members
+ encry_chat_room_id[gid] = group['EncryChatRoomId']
+ self.group_members = group_members
+ self.encry_chat_room_id_list = encry_chat_room_id
+
+ for group in self.group_members:
+ for member in self.group_members[group]:
+ if member['UserName'] not in self.account_info:
+ self.account_info['group_member'][member['UserName']] = \
+ {'type': 'group_member', 'info': member, 'group': group}
+
+ if self.DEBUG:
+ with open(os.path.join(self.temp_pwd,'contact_list.json'), 'w') as f:
+ f.write(json.dumps(self.contact_list))
+ with open(os.path.join(self.temp_pwd,'special_list.json'), 'w') as f:
+ f.write(json.dumps(self.special_list))
+ with open(os.path.join(self.temp_pwd,'group_list.json'), 'w') as f:
+ f.write(json.dumps(self.group_list))
+ with open(os.path.join(self.temp_pwd,'public_list.json'), 'w') as f:
+ f.write(json.dumps(self.public_list))
+ with open(os.path.join(self.temp_pwd,'member_list.json'), 'w') as f:
+ f.write(json.dumps(self.member_list))
+ with open(os.path.join(self.temp_pwd,'group_users.json'), 'w') as f:
+ f.write(json.dumps(self.group_members))
+ with open(os.path.join(self.temp_pwd,'account_info.json'), 'w') as f:
+ f.write(json.dumps(self.account_info))
+ print '[INFO] Get %d contacts' % len(self.contact_list)
+ print '[INFO] Start to process messages .'
+ return True
+
+
+
+ def batch_get_contact(self, cur_batch):
+ """批量获取成员信息"""
+ url = self.base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int(time.time()), self.pass_ticket)
+ params = {
+ 'BaseRequest': self.base_request,
+ "Count": len(cur_batch),
+ "List": cur_batch
+ }
+ r = self.session.post(url, data=json.dumps(params))
+ r.encoding = 'utf-8'
+ dic = json.loads(r.text)
+ #print dic['ContactList']
+ return dic['ContactList']
+
+
def batch_get_group_members(self):
"""批量获取所有群聊成员信息"""
url = self.base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int(time.time()), self.pass_ticket)
@@ -448,6 +568,11 @@ def extract_msg_content(self, msg_type_id, msg):
if self.DEBUG:
voice = self.get_voice(msg_id)
print ' %s[Voice] %s' % (msg_prefix, voice)
+ elif mtype == 37:
+ msg_content['type'] = 37
+ msg_content['data'] = msg['RecommendInfo']
+ if self.DEBUG:
+ print ' %s[useradd] %s' % (msg_prefix,msg['RecommendInfo']['NickName'])
elif mtype == 42:
msg_content['type'] = 5
info = msg['RecommendInfo']
@@ -493,7 +618,7 @@ def extract_msg_content(self, msg_type_id, msg):
print ' | desc: %s' % self.search_content('des', content, 'xml')
print ' | link: %s' % msg['Url']
print ' | from: %s' % self.search_content('appname', content, 'xml')
- print ' | content: %s' % msg.get('content')[:20]
+ print ' | content: %s' % (msg.get('content')[:20] if msg.get('content') else "unknown")
print ' --------------------------'
elif mtype == 62:
@@ -516,6 +641,11 @@ def extract_msg_content(self, msg_type_id, msg):
msg_content['data'] = msg['Content']
if self.DEBUG:
print ' [Unknown]'
+ elif mtype == 43:
+ msg_content['type'] = 13
+ msg_content['data'] = self.get_video_url(msg_id)
+ if self.DEBUG:
+ print ' %s[video] %s' % (msg_prefix, msg_content['data'])
else:
msg_content['type'] = 99
msg_content['data'] = content
@@ -539,20 +669,31 @@ def handle_msg(self, r):
"""
for msg in r['AddMsgList']:
user = {'id': msg['FromUserName'], 'name': 'unknown'}
- if msg['MsgType'] == 51: # init message
+ if msg['MsgType'] == 51 and msg['StatusNotifyCode'] == 4: # init message
msg_type_id = 0
user['name'] = 'system'
- elif msg['MsgType'] == 37:# 有人加好友 ,37为加好友信息?
- weixinhao=msg['Content']
- weixinhao=weixinhao[weixinhao.index('fromusername='):weixinhao.index('encryptusername')]
- weixinhao=weixinhao[weixinhao.index('"')+1:weixinhao.rindex('"')]
- print u'[INFO] 请求加好友!'
- print u' 昵称:' + msg['RecommendInfo']['NickName']
- #print u'ID:' + msg['RecommendInfo']['UserName']
- print u' 附加消息:'+msg['RecommendInfo']['Content']
- #print u'Ticket:'+msg['RecommendInfo']['Ticket'] # Ticket添加好友时要用
- print u' 微信号:'+weixinhao #未设置微信号的 腾讯会自动生成一段微信ID 但是无法通过搜索 搜索到此人
- return
+ #会获取所有联系人的username 和 wxid,但是会收到3次这个消息,只取第一次
+ if self.is_big_contact and len(self.full_user_name_list) == 0:
+ self.full_user_name_list.extend(msg['StatusNotifyUserName'].split(","))
+ self.wxid_list = re.search(r"username>(.*?)</username", msg["Content"]).group(1).split(",")
+ with open(os.path.join(self.temp_pwd,'UserName.txt'), 'w') as f:
+ f.write(msg['StatusNotifyUserName'])
+ with open(os.path.join(self.temp_pwd,'wxid.txt'), 'w') as f:
+ f.write(json.dumps(self.wxid_list))
+ print "[INFO] Contact list is too big. Now start to fetch member list ."
+ self.get_big_contact()
+
+ elif msg['MsgType'] == 37: # friend request
+ msg_type_id = 37
+ pass
+ # content = msg['Content']
+ # username = content[content.index('fromusername='): content.index('encryptusername')]
+ # username = username[username.index('"') + 1: username.rindex('"')]
+ # print u'[Friend Request]'
+ # print u' Nickname:' + msg['RecommendInfo']['NickName']
+ # print u' 附加消息:'+msg['RecommendInfo']['Content']
+ # # print u'Ticket:'+msg['RecommendInfo']['Ticket'] # Ticket添加好友时要用
+ # print u' 微信号:'+username #未设置微信号的 腾讯会自动生成一段微信ID 但是无法通过搜索 搜索到此人
elif msg['FromUserName'] == self.my_account['UserName']: # Self
msg_type_id = 1
user['name'] = 'self'
@@ -579,7 +720,7 @@ def handle_msg(self, r):
user['name'] = HTMLParser.HTMLParser().unescape(user['name'])
if self.DEBUG and msg_type_id != 0:
- print '[MSG] %s:' % user['name']
+ print u'[MSG] %s:' % user['name']
content = self.extract_msg_content(msg_type_id, msg)
message = {'msg_type_id': msg_type_id,
'msg_id': msg['MsgId'],
@@ -615,6 +756,10 @@ def proc_msg(self):
r = self.sync()
if r is not None:
self.handle_msg(r)
+ elif selector == '4': # 通讯录更新
+ r = self.sync()
+ if r is not None:
+ self.get_contact()
elif selector == '6': # 可能是红包
r = self.sync()
if r is not None:
@@ -632,6 +777,7 @@ def proc_msg(self):
self.handle_msg(r)
else:
print '[DEBUG] sync_check:', retcode, selector
+ time.sleep(10)
self.schedule()
except:
print '[ERROR] Except in proc_msg'
@@ -640,6 +786,186 @@ def proc_msg(self):
if check_time < 0.8:
time.sleep(1 - check_time)
+ def apply_useradd_requests(self,RecommendInfo):
+ url = self.base_uri + '/webwxverifyuser?r='+str(int(time.time()))+'&lang=zh_CN'
+ params = {
+ "BaseRequest": self.base_request,
+ "Opcode": 3,
+ "VerifyUserListSize": 1,
+ "VerifyUserList": [
+ {
+ "Value": RecommendInfo['UserName'],
+ "VerifyUserTicket": RecommendInfo['Ticket'] }
+ ],
+ "VerifyContent": "",
+ "SceneListCount": 1,
+ "SceneList": [
+ 33
+ ],
+ "skey": self.skey
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
+ def add_groupuser_to_friend_by_uid(self,uid,VerifyContent):
+ """
+ 主动向群内人员打招呼,提交添加好友请求
+ uid-群内人员得uid VerifyContent-好友招呼内容
+ 慎用此接口!封号后果自负!慎用此接口!封号后果自负!慎用此接口!封号后果自负!
+ """
+ if self.is_contact(uid):
+ return True
+ url = self.base_uri + '/webwxverifyuser?r='+str(int(time.time()))+'&lang=zh_CN'
+ params ={
+ "BaseRequest": self.base_request,
+ "Opcode": 2,
+ "VerifyUserListSize": 1,
+ "VerifyUserList": [
+ {
+ "Value": uid,
+ "VerifyUserTicket": ""
+ }
+ ],
+ "VerifyContent": VerifyContent,
+ "SceneListCount": 1,
+ "SceneList": [
+ 33
+ ],
+ "skey": self.skey
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
+ def add_friend_to_group(self,uid,group_name):
+ """
+ 将好友加入到群聊中
+ """
+ gid = ''
+ #通过群名获取群id,群没保存到通讯录中的话无法添加哦
+ for group in self.group_list:
+ if group['NickName'] == group_name:
+ gid = group['UserName']
+ if gid == '':
+ return False
+ #获取群成员数量并判断邀请方式
+ group_num=len(self.group_members[gid])
+ print '[DEBUG] group_name:%s group_num:%s' % (group_name,group_num)
+ #通过群id判断uid是否在群中
+ for user in self.group_members[gid]:
+ if user['UserName'] == uid:
+ #已经在群里面了,不用加了
+ return True
+ if group_num<=100:
+ url = self.base_uri + '/webwxupdatechatroom?fun=addmember&pass_ticket=%s' % self.pass_ticket
+ params ={
+ "AddMemberList": uid,
+ "ChatRoomName": gid,
+ "BaseRequest": self.base_request
+ }
+ else:
+ url = self.base_uri + '/webwxupdatechatroom?fun=invitemember'
+ params ={
+ "InviteMemberList": uid,
+ "ChatRoomName": gid,
+ "BaseRequest": self.base_request
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
+ def invite_friend_to_group(self,uid,group_name):
+ """
+ 将好友加入到群中。对人数多的群,需要调用此方法。
+ 拉人时,可以先尝试使用add_friend_to_group方法,当调用失败(Ret=1)时,再尝试调用此方法。
+ """
+ gid = ''
+ # 通过群名获取群id,群没保存到通讯录中的话无法添加哦
+ for group in self.group_list:
+ if group['NickName'] == group_name:
+ gid = group['UserName']
+ if gid == '':
+ return False
+ # 通过群id判断uid是否在群中
+ for user in self.group_members[gid]:
+ if user['UserName'] == uid:
+ # 已经在群里面了,不用加了
+ return True
+ url = self.base_uri + '/webwxupdatechatroom?fun=invitemember&pass_ticket=%s' % self.pass_ticket
+ params = {
+ "InviteMemberList": uid,
+ "ChatRoomName": gid,
+ "BaseRequest": self.base_request
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
+ def delete_user_from_group(self,uname,gid):
+ """
+ 将群用户从群中剔除,只有群管理员有权限
+ """
+ uid = ""
+ for user in self.group_members[gid]:
+ if user['NickName'] == uname:
+ uid = user['UserName']
+ if uid == "":
+ return False
+ url = self.base_uri + '/webwxupdatechatroom?fun=delmember&pass_ticket=%s' % self.pass_ticket
+ params ={
+ "DelMemberList": uid,
+ "ChatRoomName": gid,
+ "BaseRequest": self.base_request
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
+ def set_group_name(self,gid,gname):
+ """
+ 设置群聊名称
+ """
+ url = self.base_uri + '/webwxupdatechatroom?fun=modtopic&pass_ticket=%s' % self.pass_ticket
+ params ={
+ "NewTopic": gname,
+ "ChatRoomName": gid,
+ "BaseRequest": self.base_request
+ }
+ headers = {'content-type': 'application/json; charset=UTF-8'}
+ data = json.dumps(params, ensure_ascii=False).encode('utf8')
+ try:
+ r = self.session.post(url, data=data, headers=headers)
+ except (ConnectionError, ReadTimeout):
+ return False
+ dic = r.json()
+ return dic['BaseResponse']['Ret'] == 0
+
def send_msg_by_uid(self, word, dst='filehelper'):
url = self.base_uri + '/webwxsendmsg?pass_ticket=%s' % self.pass_ticket
msg_id = str(int(time.time() * 1000)) + str(random.random())[:5].replace('.', '')
@@ -668,8 +994,8 @@ def upload_media(self, fpath, is_img=False):
if not os.path.exists(fpath):
print '[ERROR] File not exists.'
return None
- url_1 = 'https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json'
- url_2 = 'https://file2.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json'
+ url_1 = 'https://file.'+self.base_host+'/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json'
+ url_2 = 'https://file2.'+self.base_host+'/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json'
flen = str(os.path.getsize(fpath))
ftype = mimetypes.guess_type(fpath)[0] or 'application/octet-stream'
files = {
@@ -689,7 +1015,7 @@ def upload_media(self, fpath, is_img=False):
})),
'webwx_data_ticket': (None, self.session.cookies['webwx_data_ticket']),
'pass_ticket': (None, self.pass_ticket),
- 'filename': (os.path.basename(os.path.join(self.temp_pwd,fpath)), open(os.path.join(self.temp_pwd,fpath), 'rb'),ftype.split('/')[1]),
+ 'filename': (os.path.basename(fpath), open(fpath, 'rb'),ftype.split('/')[1]),
}
self.file_index += 1
try:
@@ -783,7 +1109,7 @@ def send_msg(self, name, word, isfile=False):
uid = self.get_user_id(name)
if uid is not None:
if isfile:
- with open(os.path.join(self.temp_pwd,word), 'r') as f:
+ with open(word, 'r') as f:
result = True
for line in f.readlines():
line = line.replace('\n', '')
@@ -839,9 +1165,9 @@ def run(self):
print '[INFO] Web WeChat init failed'
return
self.status_notify()
- self.get_contact()
- print '[INFO] Get %d contacts' % len(self.contact_list)
- print '[INFO] Start to process messages .'
+ if self.get_contact():
+ print '[INFO] Get %d contacts' % len(self.contact_list)
+ print '[INFO] Start to process messages .'
self.proc_msg()
def get_uuid(self):
@@ -911,6 +1237,8 @@ def wait4login(self):
redirect_uri = param.group(1) + '&fun=new'
self.redirect_uri = redirect_uri
self.base_uri = redirect_uri[:redirect_uri.rfind('/')]
+ temp_host = self.base_uri[8:]
+ self.base_host = temp_host[:temp_host.find("/")]
return code
elif code == TIMEOUT:
print '[ERROR] WeChat login timeout. retry in %s secs later...' % (try_later_secs,)
@@ -966,6 +1294,7 @@ def init(self):
r = self.session.post(url, data=json.dumps(params))
r.encoding = 'utf-8'
dic = json.loads(r.text)
+ self.member_list.extend(dic['ContactList'])
self.sync_key = dic['SyncKey']
self.my_account = dic['User']
self.sync_key_str = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val'])
@@ -988,9 +1317,12 @@ def status_notify(self):
return dic['BaseResponse']['Ret'] == 0
def test_sync_check(self):
- for host in ['webpush', 'webpush2']:
- self.sync_host = host
- retcode = self.sync_check()[0]
+ for host1 in ['webpush.', 'webpush2.']:
+ self.sync_host = host1+self.base_host
+ try:
+ retcode = self.sync_check()[0]
+ except:
+ retcode = -1
if retcode == '0':
return True
return False
@@ -1005,7 +1337,7 @@ def sync_check(self):
'synckey': self.sync_key_str,
'_': int(time.time()),
}
- url = 'https://' + self.sync_host + '.weixin.qq.com/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params)
+ url = 'https://' + self.sync_host + '/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params)
try:
r = self.session.get(url, timeout=60)
r.encoding = 'utf-8'
@@ -1101,6 +1433,25 @@ def get_voice(self, msgid):
with open(os.path.join(self.temp_pwd,fn), 'wb') as f:
f.write(data)
return fn
+
+ def get_video_url(self, msgid):
+ return self.base_uri + '/webwxgetvideo?msgid=%s&skey=%s' % (msgid, self.skey)
+
+ def get_video(self, msgid):
+ """
+ 获取视频消息,下载视频到本地
+ :param msgid: 视频消息id
+ :return: 保存的本地视频文件路径
+ """
+ url = self.base_uri + '/webwxgetvideo?msgid=%s&skey=%s' % (msgid, self.skey)
+ headers = {'Range': 'bytes=0-'}
+ r = self.session.get(url, headers=headers)
+ data = r.content
+ fn = 'video_' + msgid + '.mp4'
+ with open(os.path.join(self.temp_pwd,fn), 'wb') as f:
+ f.write(data)
+ return fn
+
def set_remarkname(self,uid,remarkname):#设置联系人的备注名
url = self.base_uri + '/webwxoplog?lang=zh_CN&pass_ticket=%s' \
% (self.pass_ticket)