From 8fff2d64a842a6a8ea2b7e5d462451702255fd7d Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Wed, 17 Aug 2016 10:11:58 +0800 Subject: [PATCH 01/33] add QQ group --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0bb6a6b..0d30a0b 100644 --- a/README.md +++ b/README.md @@ -264,3 +264,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** From a95b4d056d56fc23f121b84e3484211dd341084e Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Wed, 17 Aug 2016 18:50:14 +0800 Subject: [PATCH 02/33] reformat code --- wxbot.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/wxbot.py b/wxbot.py index c394bb3..3ed0cc8 100644 --- a/wxbot.py +++ b/wxbot.py @@ -542,17 +542,16 @@ def handle_msg(self, r): if msg['MsgType'] == 51: # 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 + elif msg['MsgType'] == 37: # friend request + 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' From 88c815fa4430a89647977b4dbc331663608dde65 Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Wed, 17 Aug 2016 19:20:46 +0800 Subject: [PATCH 03/33] fix null pointer --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index 3ed0cc8..5ffeae7 100644 --- a/wxbot.py +++ b/wxbot.py @@ -493,7 +493,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: From addc6162624e474049e35f5fc9efa247d677a8bd Mon Sep 17 00:00:00 2001 From: vivre Date: Fri, 26 Aug 2016 22:15:10 +0800 Subject: [PATCH 04/33] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在hand_all_msg中调用即可 #自动通过好友申请 if msg['msg_type_id'] == 37: print json.dumps(msg,indent=4) print self.apply_useradd_requests(msg['content']['data']) #发送消息吧。。。。 --- wxbot.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/wxbot.py b/wxbot.py index 5ffeae7..35dbe5f 100644 --- a/wxbot.py +++ b/wxbot.py @@ -448,6 +448,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'] @@ -543,6 +548,7 @@ def handle_msg(self, r): msg_type_id = 0 user['name'] = 'system' elif msg['MsgType'] == 37: # friend request + msg_type_id = 37 pass # content = msg['Content'] # username = content[content.index('fromusername='): content.index('encryptusername')] @@ -639,6 +645,33 @@ 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 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('.', '') @@ -667,8 +700,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.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' + url_2 = 'https://file2.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' flen = str(os.path.getsize(fpath)) ftype = mimetypes.guess_type(fpath)[0] or 'application/octet-stream' files = { From ea1bda0becb9731ad020d0fd655d79731f19ca2c Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Fri, 26 Aug 2016 22:28:53 +0800 Subject: [PATCH 05/33] change url of upload_media --- wxbot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wxbot.py b/wxbot.py index 35dbe5f..8d71dd7 100644 --- a/wxbot.py +++ b/wxbot.py @@ -700,8 +700,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.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' - url_2 = 'https://file2.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' + 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' flen = str(os.path.getsize(fpath)) ftype = mimetypes.guess_type(fpath)[0] or 'application/octet-stream' files = { From fae4cf1ec6818665c1317bf1abda736b688c425a Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Sat, 27 Aug 2016 00:18:56 +0800 Subject: [PATCH 06/33] change path for send file and img msg --- wxbot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wxbot.py b/wxbot.py index 8d71dd7..0658155 100644 --- a/wxbot.py +++ b/wxbot.py @@ -721,7 +721,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: @@ -815,7 +815,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', '') From faf1a3039f16ce46f6c7146ac80741c22eda0833 Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Sat, 27 Aug 2016 13:15:09 +0800 Subject: [PATCH 07/33] exception fix --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index 0658155..69e0ea4 100644 --- a/wxbot.py +++ b/wxbot.py @@ -584,7 +584,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'], From d7f99d15e5a6f665746c802b1744ca3539c7b36d Mon Sep 17 00:00:00 2001 From: vivre Date: Sat, 27 Aug 2016 15:23:30 +0800 Subject: [PATCH 08/33] =?UTF-8?q?=E6=96=B0=E5=A2=9E3=E4=B8=AA=E7=BE=A4?= =?UTF-8?q?=E7=AE=A1=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、主动向群内好友打招呼添加好友 2、将好友加入到群聊中 3、将群用户从群中剔除,只有群管理员有权限 --- wxbot.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/wxbot.py b/wxbot.py index 69e0ea4..be3a15c 100644 --- a/wxbot.py +++ b/wxbot.py @@ -620,6 +620,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: @@ -672,6 +676,98 @@ def apply_useradd_requests(self,RecommendInfo): 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 + #通过群id判断uid是否在群中 + for user in self.group_members[gid]: + if user['UserName'] == uid: + #已经在群里面了,不用加了 + return True + url = self.base_uri + '/webwxupdatechatroom?fun=addmember&pass_ticket=%s' % self.pass_ticket + params ={ + "AddMemberList": 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 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('.', '') From e954cafb832f42067466aa4c447df779f4a573e9 Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Thu, 29 Sep 2016 17:21:11 +0800 Subject: [PATCH 09/33] update README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d30a0b..d846902 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** 库。 使用之前需要所依赖的库: From c949109325b6811843523b23b47918b352d4b4a7 Mon Sep 17 00:00:00 2001 From: liuwangsheng01 Date: Tue, 11 Oct 2016 11:57:59 +0800 Subject: [PATCH 10/33] change push url and file upload url --- wxbot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wxbot.py b/wxbot.py index be3a15c..ee3128f 100644 --- a/wxbot.py +++ b/wxbot.py @@ -796,8 +796,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.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' + url_2 = 'https://file2.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' flen = str(os.path.getsize(fpath)) ftype = mimetypes.guess_type(fpath)[0] or 'application/octet-stream' files = { @@ -1133,7 +1133,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 + '.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params) try: r = self.session.get(url, timeout=60) r.encoding = 'utf-8' From 7db6de7b9efcec059c5fdb7476eca8ab9e7f8227 Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 15:08:59 +0800 Subject: [PATCH 11/33] =?UTF-8?q?=E4=BF=AE=E5=A4=8D1102=EF=BC=8C0=E5=92=8C?= =?UTF-8?q?-1=EF=BC=8C-1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_sync_check函数完善,将4个服务器地址均放进去检测,得到合适的心跳服务器地址 --- wxbot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wxbot.py b/wxbot.py index ee3128f..abea7fe 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1116,7 +1116,7 @@ def status_notify(self): return dic['BaseResponse']['Ret'] == 0 def test_sync_check(self): - for host in ['webpush', 'webpush2']: + for host in ['webpush.wx', 'webpush2.wx','webpush.weixin', 'webpush2.weixin2',]: self.sync_host = host retcode = self.sync_check()[0] if retcode == '0': @@ -1133,7 +1133,7 @@ def sync_check(self): 'synckey': self.sync_key_str, '_': int(time.time()), } - url = 'https://' + self.sync_host + '.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params) + url = 'https://' + self.sync_host + '.qq.com/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params) try: r = self.session.get(url, timeout=60) r.encoding = 'utf-8' From 5c74dbd47566a238f555b7a48a792a58b555d0e9 Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 15:15:52 +0800 Subject: [PATCH 12/33] fix bug fix bug --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index abea7fe..c813dae 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1116,7 +1116,7 @@ def status_notify(self): return dic['BaseResponse']['Ret'] == 0 def test_sync_check(self): - for host in ['webpush.wx', 'webpush2.wx','webpush.weixin', 'webpush2.weixin2',]: + for host in ['webpush.wx', 'webpush2.wx','webpush.weixin', 'webpush2.weixin2']: self.sync_host = host retcode = self.sync_check()[0] if retcode == '0': From b60084e4c978fa6df3ac9bc666ae57e23fa4947d Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 15:54:24 +0800 Subject: [PATCH 13/33] fix bugs fix bugs --- wxbot.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/wxbot.py b/wxbot.py index c813dae..b4d6a61 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1116,9 +1116,12 @@ def status_notify(self): return dic['BaseResponse']['Ret'] == 0 def test_sync_check(self): - for host in ['webpush.wx', 'webpush2.wx','webpush.weixin', 'webpush2.weixin2']: + for host in ['webpush.wx', 'webpush2.wx2','webpush.weixin', 'webpush2.weixin2']: self.sync_host = host - retcode = self.sync_check()[0] + try: + retcode = self.sync_check()[0] + except: + retcode == -1 if retcode == '0': return True return False From d8907994ae1f25d650576a5323612db2259161fb Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 17:57:38 +0800 Subject: [PATCH 14/33] =?UTF-8?q?test=5Fsync=5Fcheck=20=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_sync_check 兼容 --- wxbot.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/wxbot.py b/wxbot.py index b4d6a61..c7413be 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1116,14 +1116,15 @@ def status_notify(self): return dic['BaseResponse']['Ret'] == 0 def test_sync_check(self): - for host in ['webpush.wx', 'webpush2.wx2','webpush.weixin', 'webpush2.weixin2']: - self.sync_host = host - try: - retcode = self.sync_check()[0] - except: - retcode == -1 - if retcode == '0': - return True + for host1 in ['webpush', 'webpush2']: + for host2 in ['weixin','weixin2','wx','wx2']: + self.sync_host = host1+host2 + try: + retcode = self.sync_check()[0] + except: + retcode == -1 + if retcode == '0': + return True return False def sync_check(self): From 51c37e02b3127a91e9e789cec5a6961c5ba1568b Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 18:01:19 +0800 Subject: [PATCH 15/33] =?UTF-8?q?=E5=85=BC=E5=AE=B9sync=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B31102=EF=BC=8C0=E5=92=8C-1=EF=BC=8C-1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 兼容sync,解决1102,0和-1,-1问题 --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index c7413be..66ca144 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1116,7 +1116,7 @@ def status_notify(self): return dic['BaseResponse']['Ret'] == 0 def test_sync_check(self): - for host1 in ['webpush', 'webpush2']: + for host1 in ['webpush.', 'webpush2.']: for host2 in ['weixin','weixin2','wx','wx2']: self.sync_host = host1+host2 try: From b09cd76e2ce7b09b06d8eedf74d2ed98995e0177 Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 12 Oct 2016 19:01:20 +0800 Subject: [PATCH 16/33] fix --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index 66ca144..50f724a 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1118,7 +1118,7 @@ def status_notify(self): def test_sync_check(self): for host1 in ['webpush.', 'webpush2.']: for host2 in ['weixin','weixin2','wx','wx2']: - self.sync_host = host1+host2 + self.sync_host = host1+host2 try: retcode = self.sync_check()[0] except: From 95729f4bff041674fcb627444724f2665469477f Mon Sep 17 00:00:00 2001 From: vivre Date: Thu, 13 Oct 2016 22:22:13 +0800 Subject: [PATCH 17/33] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E7=BE=A4=E8=81=8A=E5=90=8D=E7=A7=B0=E6=8E=A5=E5=8F=A3--set=5Fg?= =?UTF-8?q?roup=5Fname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增设置群聊名称接口--set_group_name --- wxbot.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/wxbot.py b/wxbot.py index 50f724a..7b72256 100644 --- a/wxbot.py +++ b/wxbot.py @@ -767,6 +767,24 @@ def delete_user_from_group(self,uname,gid): 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 From efe2c02b77ae08dd3990947eece553070bc60f2b Mon Sep 17 00:00:00 2001 From: xuncl Date: Fri, 14 Oct 2016 17:58:37 +0800 Subject: [PATCH 18/33] fix a syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复之前vivre90更新的一点小疏忽 --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index 7b72256..1e6c99b 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1140,7 +1140,7 @@ def test_sync_check(self): try: retcode = self.sync_check()[0] except: - retcode == -1 + retcode = -1 if retcode == '0': return True return False From bb44aca2062afef9d643e5b666fcc2d7fba5b9d8 Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 26 Oct 2016 00:20:52 +0800 Subject: [PATCH 19/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BF=83=E8=B7=B3?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E5=90=8C=E6=97=B6=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E4=B8=8A=E4=BC=A0=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=81=20=E5=9C=A8=E8=BF=94=E5=9B=9E-1,-1?= =?UTF-8?q?=E6=97=B6=E9=99=8D=E4=BD=8E=E5=BF=83=E8=B7=B3=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=A2=91=E7=8E=87=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、更新心跳服务器获取方式,新增base_host 2、文件上传同步base_host 3、-1,-1错误的时候降低心跳获取频率 --- wxbot.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/wxbot.py b/wxbot.py index 1e6c99b..dc6a523 100644 --- a/wxbot.py +++ b/wxbot.py @@ -62,6 +62,7 @@ def __init__(self): self.DEBUG = False self.uuid = '' self.base_uri = '' + self.base_host = '' self.redirect_uri = '' self.uin = '' self.sid = '' @@ -641,6 +642,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' @@ -814,8 +816,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.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json' - url_2 = 'https://file2.wx2.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 = { @@ -1057,6 +1059,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[:host2.find("/")] return code elif code == TIMEOUT: print '[ERROR] WeChat login timeout. retry in %s secs later...' % (try_later_secs,) @@ -1135,14 +1139,13 @@ def status_notify(self): def test_sync_check(self): for host1 in ['webpush.', 'webpush2.']: - for host2 in ['weixin','weixin2','wx','wx2']: - self.sync_host = host1+host2 - try: - retcode = self.sync_check()[0] - except: - retcode = -1 - if retcode == '0': - return True + self.sync_host = host1+self.base_host + try: + retcode = self.sync_check()[0] + except: + retcode = -1 + if retcode == '0': + return True return False def sync_check(self): @@ -1155,7 +1158,7 @@ def sync_check(self): 'synckey': self.sync_key_str, '_': int(time.time()), } - url = 'https://' + self.sync_host + '.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' From efef7ee17215a41e67778d35f46bca315200e1b6 Mon Sep 17 00:00:00 2001 From: vivre Date: Wed, 26 Oct 2016 17:11:36 +0800 Subject: [PATCH 20/33] fix bugs~~~ fix bugs~~~ --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index dc6a523..e2c9173 100644 --- a/wxbot.py +++ b/wxbot.py @@ -1060,7 +1060,7 @@ def wait4login(self): self.redirect_uri = redirect_uri self.base_uri = redirect_uri[:redirect_uri.rfind('/')] temp_host = self.base_uri[8:] - self.base_host = temp_host[:host2.find("/")] + 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,) From 047bfe6d6ae1b54449248f9023aed57edf50a3d6 Mon Sep 17 00:00:00 2001 From: mzy Date: Sat, 29 Oct 2016 17:00:30 +0800 Subject: [PATCH 21/33] Add invite function for add user to group Signed-off-by: mzy --- wxbot.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/wxbot.py b/wxbot.py index e2c9173..e31899d 100644 --- a/wxbot.py +++ b/wxbot.py @@ -724,17 +724,28 @@ def add_friend_to_group(self,uid,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 - url = self.base_uri + '/webwxupdatechatroom?fun=addmember&pass_ticket=%s' % self.pass_ticket - params ={ - "AddMemberList": uid, - "ChatRoomName": gid, - "BaseRequest": self.base_request - } + 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: From 596f5bb034355856f00c1f19bcf2916a8d55b41a Mon Sep 17 00:00:00 2001 From: yjz <273638544@qq.com> Date: Wed, 7 Dec 2016 17:14:09 +0800 Subject: [PATCH 22/33] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E5=BD=95=E8=BF=87=E5=A4=A7=EF=BC=8C=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E9=80=9A=E8=AE=AF=E5=BD=95=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test.py | 1 + wxbot.py | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 4 deletions(-) diff --git a/test.py b/test.py index c81a60f..a815690 100644 --- a/test.py +++ b/test.py @@ -21,6 +21,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 e2c9173..836d36d 100644 --- a/wxbot.py +++ b/wxbot.py @@ -17,12 +17,16 @@ from traceback import format_exc from requests.exceptions import ConnectionError, ReadTimeout import HTMLParser +import xml.etree.ElementTree as ET UNKONWN = 'unkonwn' SUCCESS = '200' SCANED = '201' TIMEOUT = '408' +def map_username_batch(user_name): + return {"UserName": user_name, "EncryChatRoomId": ""} + def show_image(file_path): """ @@ -54,6 +58,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功能类""" @@ -74,6 +86,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: @@ -119,9 +137,17 @@ 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: @@ -184,6 +210,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) @@ -548,6 +668,21 @@ def handle_msg(self, r): if msg['MsgType'] == 51: # init message msg_type_id = 0 user['name'] = 'system' + #会获取所有联系人的username 和 wxid,但是会收到3次这个消息,只取第一次 + if self.is_big_contact and len(self.full_user_name_list) == 0: + self.full_user_name_list = msg['StatusNotifyUserName'].split(",") + wxid_str = msg["Content"] + tmp = wxid_str.replace("<", "<") + wxid_str = tmp.replace(">", ">") + tree = ET.fromstring(wxid_str) + self.wxid_list = tree[1][1].text.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(wxid_str) + 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 @@ -987,9 +1122,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): From 2fff37136bc22cd74749af11d91a6d666833a123 Mon Sep 17 00:00:00 2001 From: yjz <273638544@qq.com> Date: Thu, 8 Dec 2016 15:02:29 +0800 Subject: [PATCH 23/33] =?UTF-8?q?=E5=A2=9E=E5=8A=A0StatusNotifyCode?= =?UTF-8?q?=E7=9A=84=E5=88=A4=E6=96=AD=EF=BC=8C=E6=9B=B4=E6=94=B9replace?= =?UTF-8?q?=E4=B8=BA=E6=AD=A3=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wxbot.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/wxbot.py b/wxbot.py index 836d36d..de93f36 100644 --- a/wxbot.py +++ b/wxbot.py @@ -17,7 +17,6 @@ from traceback import format_exc from requests.exceptions import ConnectionError, ReadTimeout import HTMLParser -import xml.etree.ElementTree as ET UNKONWN = 'unkonwn' SUCCESS = '200' @@ -665,17 +664,13 @@ 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' #会获取所有联系人的username 和 wxid,但是会收到3次这个消息,只取第一次 if self.is_big_contact and len(self.full_user_name_list) == 0: self.full_user_name_list = msg['StatusNotifyUserName'].split(",") - wxid_str = msg["Content"] - tmp = wxid_str.replace("<", "<") - wxid_str = tmp.replace(">", ">") - tree = ET.fromstring(wxid_str) - self.wxid_list = tree[1][1].text.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: From f13ef3f79527918f4eab52cec5dbdd97ff504c4d Mon Sep 17 00:00:00 2001 From: yjz <273638544@qq.com> Date: Wed, 21 Dec 2016 14:46:17 +0800 Subject: [PATCH 24/33] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BF=9D=E5=AD=98wxid?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=90=8D=E5=86=99=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wxbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxbot.py b/wxbot.py index de93f36..3f7ab70 100644 --- a/wxbot.py +++ b/wxbot.py @@ -674,7 +674,7 @@ def handle_msg(self, r): 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(wxid_str) + f.write(json.dumps(self.wxid_list)) print "[INFO] Contact list is too big. Now start to fetch member list ." self.get_big_contact() From 072abe099515d410a58bae9ed9a263e521cd9b9b Mon Sep 17 00:00:00 2001 From: Archspider Date: Sat, 24 Dec 2016 17:38:31 +0800 Subject: [PATCH 25/33] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2'\xa0'=E2=80=98=E5=AD=97=E7=AC=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot.py b/bot.py index fe755c1..f97a231 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 = respond['text'].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 = respond['text'].replace(u'\xa0', u' ') print ' ROBOT:', result return result From 87123183cbb71bea85295f7860c46632f3120bb0 Mon Sep 17 00:00:00 2001 From: archspider Date: Wed, 28 Dec 2016 21:36:29 +0800 Subject: [PATCH 26/33] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2'\xa0'=E2=80=98=E5=AD=97=E7=AC=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复无法转换'\xa0'‘字符问题 --- bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index f97a231..e87a3d5 100644 --- a/bot.py +++ b/bot.py @@ -31,7 +31,7 @@ def tuling_auto_reply(self, uid, msg): result = '' if respond['code'] == 100000: result = respond['text'].replace('
', ' ') - result = respond['text'].replace(u'\xa0', u' ') + result = result.replace(u'\xa0', u' ') elif respond['code'] == 200000: result = respond['url'] elif respond['code'] == 302000: @@ -40,7 +40,7 @@ def tuling_auto_reply(self, uid, msg): k['article'] + "\t" + k['detailurl'] + "\n" else: result = respond['text'].replace('
', ' ') - result = respond['text'].replace(u'\xa0', u' ') + result = result.replace(u'\xa0', u' ') print ' ROBOT:', result return result From 50d3b3be839aa2a4310a190b064b3e0466bcdaea Mon Sep 17 00:00:00 2001 From: chong Date: Mon, 2 Jan 2017 17:55:42 +0800 Subject: [PATCH 27/33] =?UTF-8?q?=E5=A2=9E=E5=8A=A0invite=5Ffriend=5Fto=5F?= =?UTF-8?q?group=E6=96=B9=E6=B3=95=EF=BC=8C=E5=BA=94=E5=AF=B9=E7=94=B1?= =?UTF-8?q?=E4=BA=8E=E7=BE=A4=E4=BA=BA=E6=95=B0=E8=BF=87=E5=A4=9A=E9=80=A0?= =?UTF-8?q?=E6=88=90=E7=9A=84=E6=97=A0=E6=B3=95=E7=9B=B4=E6=8E=A5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=94=A8=E6=88=B7=E5=88=B0=E7=BE=A4=E3=80=81add=5Ffri?= =?UTF-8?q?end=5Fto=5Fgroup=E6=96=B9=E6=B3=95=E8=B0=83=E7=94=A8=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E7=9A=84=E6=83=85=E5=86=B5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wxbot.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/wxbot.py b/wxbot.py index 3f7ab70..74ea073 100644 --- a/wxbot.py +++ b/wxbot.py @@ -874,6 +874,38 @@ def add_friend_to_group(self,uid,group_name): 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): """ 将群用户从群中剔除,只有群管理员有权限 From c2f0ce6659184238133573d1b74e8331a06551f2 Mon Sep 17 00:00:00 2001 From: derrickcyt <422780332@qq.com> Date: Mon, 9 Jan 2017 17:17:54 +0800 Subject: [PATCH 28/33] fix bug that it can't find group personal name. --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index e87a3d5..bd56c88 100644 --- a/bot.py +++ b/bot.py @@ -71,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']: From fad69e3f5c7efc8c13f23df1a66d5612723db79c Mon Sep 17 00:00:00 2001 From: zhgcao Date: Wed, 11 Jan 2017 22:20:27 +0800 Subject: [PATCH 29/33] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E5=B0=8F?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ wxbot.py | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/README.md b/README.md index d846902..c823bfe 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ python test.py | 10 | 撤回消息 | 不可用 | | 11 | 空内容 | 空字符串 | | 12 | 红包 | 不可用 | +| 13 | 小视频 | 字符串,视频数据的url,HTTP POST请求此url可以得到mp4文件格式的数据 | | 99 | 未知类型 | 不可用 | ### 4.4 群文本消息 @@ -202,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微信数据) | diff --git a/wxbot.py b/wxbot.py index 74ea073..07acfbb 100644 --- a/wxbot.py +++ b/wxbot.py @@ -641,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 @@ -1416,6 +1421,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) From 1e9e63994743f50eae317ff3f0aa98e7ae720adb Mon Sep 17 00:00:00 2001 From: "k.liu" Date: Wed, 8 Mar 2017 14:48:23 +0800 Subject: [PATCH 30/33] pull request --- test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test.py b/test.py index c81a60f..184a0b3 100644 --- a/test.py +++ b/test.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # coding: utf-8 +# from wxbot import * From a6637df1381e599262c4d88cd613c81979c149d2 Mon Sep 17 00:00:00 2001 From: vivre Date: Sat, 11 Mar 2017 23:06:39 +0800 Subject: [PATCH 31/33] =?UTF-8?q?=E4=B8=BA=E4=BA=86=E5=85=BC=E5=AE=B9Wxbot?= =?UTF-8?q?Manage=EF=BC=8C=E6=96=B0=E5=A2=9E=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=92=8C=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将WxbotManage中维护的属性和方法同步到Wxbot中,Wxbot以后只需要更新lib目录中的wxbot就能和官方同步 --- wxbot.py | 75 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/wxbot.py b/wxbot.py index 4d117eb..fadaed1 100644 --- a/wxbot.py +++ b/wxbot.py @@ -85,6 +85,8 @@ def __init__(self): self.sync_key = [] self.sync_host = '' + status = 'wait4login' #表示机器人状态,供WEBAPI读取,WxbotManage使用 + bot_conf = {} #机器人配置,在webapi初始化的时候传入,后续也可修改,WxbotManage使用 self.batch_count = 50 #一次拉取50个联系人的信息 self.full_user_name_list = [] #直接获取不到通讯录时,获取的username列表 @@ -119,6 +121,21 @@ def __init__(self): self.file_index = 0 + #在未传入bot_conf的情况下尝试载入本地配置文件,WxbotManage使用 + def load_conf(self,bot_conf): + try: + if bot_conf == {}: + with open(os.path.join(self.temp_pwd,'bot_conf.json')) as f: + self.bot_conf= json.loads(f.read()) + except: + self.bot_conf = {} + + #保存配置文件,WxbotManage使用 + def save_conf(self): + with open(os.path.join(self.temp_pwd,'bot_conf.json'), 'w') as f: + f.write(json.dumps(self.bot_conf)) + + @staticmethod def to_unicode(string, encoding='utf-8'): """ @@ -738,7 +755,10 @@ def schedule(self): def proc_msg(self): self.test_sync_check() + self.status = 'loginsuccess' #WxbotManage使用 while True: + if self.status == 'wait4loginout': #WxbotManage使用 + return check_time = time.time() try: [retcode, selector] = self.sync_check() @@ -1144,31 +1164,40 @@ def search_content(key, content, fmat='attr'): return 'unknown' def run(self): - self.get_uuid() - self.gen_qr_code(os.path.join(self.temp_pwd,'wxqr.png')) - print '[INFO] Please use WeChat to scan the QR code .' - - result = self.wait4login() - if result != SUCCESS: - print '[ERROR] Web WeChat login failed. failed code=%s' % (result,) - return + try: + self.get_uuid() + self.gen_qr_code(os.path.join(self.temp_pwd,'wxqr.png')) + print '[INFO] Please use WeChat to scan the QR code .' + + result = self.wait4login() + if result != SUCCESS: + print '[ERROR] Web WeChat login failed. failed code=%s' % (result,) + self.status = 'loginout' + return + + if self.login(): + print '[INFO] Web WeChat login succeed .' + else: + print '[ERROR] Web WeChat login failed .' + self.status = 'loginout' + return - if self.login(): - print '[INFO] Web WeChat login succeed .' - else: - print '[ERROR] Web WeChat login failed .' - return + if self.init(): + print '[INFO] Web WeChat init succeed .' + else: + print '[INFO] Web WeChat init failed' + self.status = 'loginout' + return + self.status_notify() + if self.get_contact(): + print '[INFO] Get %d contacts' % len(self.contact_list) + print '[INFO] Start to process messages .' + self.proc_msg() + self.status = 'loginout' + except Exception,e: + print '[ERROR] Web WeChat run failed --> %s'%(e) + self.status = 'loginout' - if self.init(): - print '[INFO] Web WeChat init succeed .' - else: - print '[INFO] Web WeChat init failed' - return - self.status_notify() - 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): url = 'https://login.weixin.qq.com/jslogin' From eab8fcf475578fac59911f5fa2d08a136baf518f Mon Sep 17 00:00:00 2001 From: vivre Date: Sat, 11 Mar 2017 23:12:44 +0800 Subject: [PATCH 32/33] =?UTF-8?q?=E6=96=B0=E5=A2=9EWxbotManage=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=BB=8B=E7=BB=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增WxbotManage系统介绍 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c823bfe..7228695 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,10 @@ python test.py [zixia/wechaty](https://github.com/zixia/wechaty) Wechaty is wechat for bot in Javascript(ES6). It's a Personal Account Robot Framework/Library. -## 7 交流讨论 +## 7 基于Wxbot延伸的一些项目 +[WxbotManage](https://coding.net/u/vivre/p/WxbotManage/git) 基于Wxbot的微信多开管理和Webapi系统 + +## 8 交流讨论 问题可以直接开 **issue** From f3e347a864396ebd79401fd1ad24cbff5785a78d Mon Sep 17 00:00:00 2001 From: Jerry Yu Date: Wed, 15 Mar 2017 14:34:26 +0800 Subject: [PATCH 33/33] =?UTF-8?q?=E6=94=B6=E5=88=B01102=20retcode=20?= =?UTF-8?q?=E5=90=8E=E6=9B=B4=E6=96=B0=E5=BE=AE=E4=BF=A1=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E8=BD=AE=E8=AF=A2=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wxbot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wxbot.py b/wxbot.py index fadaed1..f5389fa 100644 --- a/wxbot.py +++ b/wxbot.py @@ -767,6 +767,8 @@ def proc_msg(self): break elif retcode == '1101': # 从其它设备上登了网页微信 break + elif retcode == '1102': # 服务器轮回地址发生变化,更新源服务 + self.test_sync_check() elif retcode == '0': if selector == '2': # 有新消息 r = self.sync()