1717from traceback import format_exc
1818from requests .exceptions import ConnectionError , ReadTimeout
1919import HTMLParser
20+ import xml .etree .ElementTree as ET
2021
2122UNKONWN = 'unkonwn'
2223SUCCESS = '200'
2324SCANED = '201'
2425TIMEOUT = '408'
2526
27+ def map_username_batch (user_name ):
28+ return {"UserName" : user_name , "EncryChatRoomId" : "" }
29+
2630
2731def show_image (file_path ):
2832 """
@@ -54,6 +58,14 @@ def request(self, method, url, params=None, data=None, headers=None, cookies=Non
5458 print e .message , traceback .format_exc ()
5559 continue
5660
61+ #重试3次以后再加一次,抛出异常
62+ try :
63+ return super (SafeSession , self ).request (method , url , params , data , headers , cookies , files , auth ,
64+ timeout ,
65+ allow_redirects , proxies , hooks , stream , verify , cert , json )
66+ except Exception as e :
67+ raise e
68+
5769
5870class WXBot :
5971 """WXBot功能类"""
@@ -74,6 +86,12 @@ def __init__(self):
7486 self .sync_key = []
7587 self .sync_host = ''
7688
89+
90+ self .batch_count = 50 #一次拉取50个联系人的信息
91+ self .full_user_name_list = [] #直接获取不到通讯录时,获取的username列表
92+ self .wxid_list = [] #获取到的wxid的列表
93+ self .cursor = 0 #拉取联系人信息的游标
94+ self .is_big_contact = False #通讯录人数过多,无法直接获取
7795 #文件缓存目录
7896 self .temp_pwd = os .path .join (os .getcwd (),'temp' )
7997 if os .path .exists (self .temp_pwd ) == False :
@@ -119,9 +137,17 @@ def to_unicode(string, encoding='utf-8'):
119137
120138 def get_contact (self ):
121139 """获取当前账户的所有相关账号(包括联系人、公众号、群聊、特殊账号)"""
140+ if self .is_big_contact :
141+ return False
122142 url = self .base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' \
123143 % (self .pass_ticket , self .skey , int (time .time ()))
124- r = self .session .post (url , data = '{}' )
144+
145+ #如果通讯录联系人过多,这里会直接获取失败
146+ try :
147+ r = self .session .post (url , data = '{}' )
148+ except Exception as e :
149+ self .is_big_contact = True
150+ return False
125151 r .encoding = 'utf-8'
126152 if self .DEBUG :
127153 with open (os .path .join (self .temp_pwd ,'contacts.json' ), 'w' ) as f :
@@ -184,6 +210,100 @@ def get_contact(self):
184210 f .write (json .dumps (self .account_info ))
185211 return True
186212
213+
214+ def get_big_contact (self ):
215+ total_len = len (self .full_user_name_list )
216+ user_info_list = []
217+
218+ #一次拉取50个联系人的信息,包括所有的群聊,公众号,好友
219+ while self .cursor < total_len :
220+ cur_batch = self .full_user_name_list [self .cursor :(self .cursor + self .batch_count )]
221+ self .cursor += self .batch_count
222+ cur_batch = map (map_username_batch , cur_batch )
223+ user_info_list += self .batch_get_contact (cur_batch )
224+ print "[INFO] Get batch contacts"
225+
226+ self .member_list = user_info_list
227+ special_users = ['newsapp' , 'filehelper' , 'weibo' , 'qqmail' ,
228+ 'fmessage' , 'tmessage' , 'qmessage' , 'qqsync' , 'floatbottle' ,
229+ 'lbsapp' , 'shakeapp' , 'medianote' , 'qqfriend' , 'readerapp' ,
230+ 'blogapp' , 'facebookapp' , 'masssendapp' , 'meishiapp' ,
231+ 'feedsapp' , 'voip' , 'blogappweixin' , 'weixin' , 'brandsessionholder' ,
232+ 'weixinreminder' , 'wxid_novlwrv3lqwv11' ,
233+ 'officialaccounts' ,
234+ 'gh_22b87fa7cb3c' , 'wxitil' , 'userexperience_alarm' , 'notification_messages' , 'notifymessage' ]
235+
236+ self .contact_list = []
237+ self .public_list = []
238+ self .special_list = []
239+ self .group_list = []
240+ for i , contact in enumerate (self .member_list ):
241+ if contact ['VerifyFlag' ] & 8 != 0 : # 公众号
242+ self .public_list .append (contact )
243+ self .account_info ['normal_member' ][contact ['UserName' ]] = {'type' : 'public' , 'info' : contact }
244+ elif contact ['UserName' ] in special_users or self .wxid_list [i ] in special_users : # 特殊账户
245+ self .special_list .append (contact )
246+ self .account_info ['normal_member' ][contact ['UserName' ]] = {'type' : 'special' , 'info' : contact }
247+ elif contact ['UserName' ].find ('@@' ) != - 1 : # 群聊
248+ self .group_list .append (contact )
249+ self .account_info ['normal_member' ][contact ['UserName' ]] = {'type' : 'group' , 'info' : contact }
250+ elif contact ['UserName' ] == self .my_account ['UserName' ]: # 自己
251+ self .account_info ['normal_member' ][contact ['UserName' ]] = {'type' : 'self' , 'info' : contact }
252+ else :
253+ self .contact_list .append (contact )
254+ self .account_info ['normal_member' ][contact ['UserName' ]] = {'type' : 'contact' , 'info' : contact }
255+ group_members = {}
256+ encry_chat_room_id = {}
257+ for group in self .group_list :
258+ gid = group ['UserName' ]
259+ members = group ['MemberList' ]
260+ group_members [gid ] = members
261+ encry_chat_room_id [gid ] = group ['EncryChatRoomId' ]
262+ self .group_members = group_members
263+ self .encry_chat_room_id_list = encry_chat_room_id
264+
265+ for group in self .group_members :
266+ for member in self .group_members [group ]:
267+ if member ['UserName' ] not in self .account_info :
268+ self .account_info ['group_member' ][member ['UserName' ]] = \
269+ {'type' : 'group_member' , 'info' : member , 'group' : group }
270+
271+ if self .DEBUG :
272+ with open (os .path .join (self .temp_pwd ,'contact_list.json' ), 'w' ) as f :
273+ f .write (json .dumps (self .contact_list ))
274+ with open (os .path .join (self .temp_pwd ,'special_list.json' ), 'w' ) as f :
275+ f .write (json .dumps (self .special_list ))
276+ with open (os .path .join (self .temp_pwd ,'group_list.json' ), 'w' ) as f :
277+ f .write (json .dumps (self .group_list ))
278+ with open (os .path .join (self .temp_pwd ,'public_list.json' ), 'w' ) as f :
279+ f .write (json .dumps (self .public_list ))
280+ with open (os .path .join (self .temp_pwd ,'member_list.json' ), 'w' ) as f :
281+ f .write (json .dumps (self .member_list ))
282+ with open (os .path .join (self .temp_pwd ,'group_users.json' ), 'w' ) as f :
283+ f .write (json .dumps (self .group_members ))
284+ with open (os .path .join (self .temp_pwd ,'account_info.json' ), 'w' ) as f :
285+ f .write (json .dumps (self .account_info ))
286+ print '[INFO] Get %d contacts' % len (self .contact_list )
287+ print '[INFO] Start to process messages .'
288+ return True
289+
290+
291+
292+ def batch_get_contact (self , cur_batch ):
293+ """批量获取成员信息"""
294+ url = self .base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int (time .time ()), self .pass_ticket )
295+ params = {
296+ 'BaseRequest' : self .base_request ,
297+ "Count" : len (cur_batch ),
298+ "List" : cur_batch
299+ }
300+ r = self .session .post (url , data = json .dumps (params ))
301+ r .encoding = 'utf-8'
302+ dic = json .loads (r .text )
303+ #print dic['ContactList']
304+ return dic ['ContactList' ]
305+
306+
187307 def batch_get_group_members (self ):
188308 """批量获取所有群聊成员信息"""
189309 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):
548668 if msg ['MsgType' ] == 51 : # init message
549669 msg_type_id = 0
550670 user ['name' ] = 'system'
671+ #会获取所有联系人的username 和 wxid,但是会收到3次这个消息,只取第一次
672+ if self .is_big_contact and len (self .full_user_name_list ) == 0 :
673+ self .full_user_name_list = msg ['StatusNotifyUserName' ].split ("," )
674+ wxid_str = msg ["Content" ]
675+ tmp = wxid_str .replace ("<" , "<" )
676+ wxid_str = tmp .replace (">" , ">" )
677+ tree = ET .fromstring (wxid_str )
678+ self .wxid_list = tree [1 ][1 ].text .split ("," )
679+ with open (os .path .join (self .temp_pwd ,'UserName.txt' ), 'w' ) as f :
680+ f .write (msg ['StatusNotifyUserName' ])
681+ with open (os .path .join (self .temp_pwd ,'wxid.txt' ), 'w' ) as f :
682+ f .write (wxid_str )
683+ print "[INFO] Contact list is too big. Now start to fetch member list ."
684+ self .get_big_contact ()
685+
551686 elif msg ['MsgType' ] == 37 : # friend request
552687 msg_type_id = 37
553688 pass
@@ -987,9 +1122,9 @@ def run(self):
9871122 print '[INFO] Web WeChat init failed'
9881123 return
9891124 self .status_notify ()
990- self .get_contact ()
991- print '[INFO] Get %d contacts' % len (self .contact_list )
992- print '[INFO] Start to process messages .'
1125+ if self .get_contact ():
1126+ print '[INFO] Get %d contacts' % len (self .contact_list )
1127+ print '[INFO] Start to process messages .'
9931128 self .proc_msg ()
9941129
9951130 def get_uuid (self ):
0 commit comments