wxbot.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. import pyqrcode
  4. import requests
  5. import json
  6. import xml.dom.minidom
  7. import urllib
  8. import time
  9. import re
  10. import random
  11. class WXBot:
  12. def __init__(self):
  13. self.DEBUG = False
  14. self.uuid = ''
  15. self.base_uri = ''
  16. self.redirect_uri = ''
  17. self.uin = ''
  18. self.sid = ''
  19. self.skey = ''
  20. self.pass_ticket = ''
  21. self.device_id = 'e' + repr(random.random())[2:17]
  22. self.base_request = {}
  23. self.sync_key_str = ''
  24. self.sync_key = []
  25. self.user = {}
  26. self.account_info = {}
  27. self.member_list = [] # all kind of accounts: contacts, public accounts, groups, special accounts
  28. self.contact_list = [] # contact list
  29. self.public_list = [] # public account list
  30. self.group_list = [] # group chat list
  31. self.special_list = [] # special list account
  32. self.group_members = {} # members of all groups
  33. self.sync_host = ''
  34. self.session = requests.Session()
  35. self.session.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5'})
  36. self.conf = {'qr': 'png'}
  37. # Get information of all contacts of current account.
  38. def get_contact(self):
  39. url = self.base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' \
  40. % (self.pass_ticket, self.skey, int(time.time()))
  41. r = self.session.post(url, data='{}')
  42. r.encoding = 'utf-8'
  43. if self.DEBUG:
  44. with open('contacts.json', 'w') as f:
  45. f.write(r.text.encode('utf-8'))
  46. dic = json.loads(r.text)
  47. self.member_list = dic['MemberList']
  48. special_users = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail',
  49. 'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle',
  50. 'lbsapp', 'shakeapp', 'medianote', 'qqfriend', 'readerapp',
  51. 'blogapp', 'facebookapp', 'masssendapp', 'meishiapp',
  52. 'feedsapp', 'voip', 'blogappweixin', 'weixin', 'brandsessionholder',
  53. 'weixinreminder', 'wxid_novlwrv3lqwv11', 'gh_22b87fa7cb3c',
  54. 'officialaccounts', 'notification_messages', 'wxid_novlwrv3lqwv11',
  55. 'gh_22b87fa7cb3c', 'wxitil', 'userexperience_alarm', 'notification_messages']
  56. self.contact_list = []
  57. self.public_list = []
  58. self.special_list = []
  59. self.group_list = []
  60. for contact in self.member_list:
  61. if contact['VerifyFlag'] & 8 != 0: # public account
  62. self.public_list.append(contact)
  63. self.account_info[contact['UserName']] = {'type': 'public', 'info': contact}
  64. elif contact['UserName'] in special_users: # special account
  65. self.special_list.append(contact)
  66. self.account_info[contact['UserName']] = {'type': 'special', 'info': contact}
  67. elif contact['UserName'].find('@@') != -1: # group
  68. self.group_list.append(contact)
  69. self.account_info[contact['UserName']] = {'type': 'group', 'info': contact}
  70. elif contact['UserName'] == self.user['UserName']: # self
  71. self.account_info[contact['UserName']] = {'type': 'self', 'info': contact}
  72. pass
  73. else:
  74. self.contact_list.append(contact)
  75. self.group_members = self.batch_get_group_members()
  76. for group in self.group_members:
  77. for member in self.group_members[group]:
  78. if member['UserName'] not in self.account_info:
  79. self.account_info[member['UserName']] = {'type': 'group_member', 'info': member, 'group': group}
  80. if self.DEBUG:
  81. with open('contact_list.json', 'w') as f:
  82. f.write(json.dumps(self.contact_list))
  83. with open('special_list.json', 'w') as f:
  84. f.write(json.dumps(self.special_list))
  85. with open('group_list.json', 'w') as f:
  86. f.write(json.dumps(self.group_list))
  87. with open('public_list.json', 'w') as f:
  88. f.write(json.dumps(self.public_list))
  89. with open('member_list.json', 'w') as f:
  90. f.write(json.dumps(self.member_list))
  91. with open('group_users.json', 'w') as f:
  92. f.write(json.dumps(self.group_members))
  93. with open('account_info.json', 'w') as f:
  94. f.write(json.dumps(self.account_info))
  95. return True
  96. # Get information of accounts in all groups at once.
  97. def batch_get_group_members(self):
  98. url = self.base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int(time.time()), self.pass_ticket)
  99. params = {
  100. 'BaseRequest': self.base_request,
  101. "Count": len(self.group_list),
  102. "List": [{"UserName": group['UserName'], "EncryChatRoomId":""} for group in self.group_list]
  103. }
  104. r = self.session.post(url, data=json.dumps(params))
  105. r.encoding = 'utf-8'
  106. dic = json.loads(r.text)
  107. group_members = {}
  108. for group in dic['ContactList']:
  109. gid = group['UserName']
  110. members = group['MemberList']
  111. group_members[gid] = members
  112. return group_members
  113. def get_account_info(self, uid):
  114. if uid in self.account_info:
  115. return self.account_info[uid]
  116. else:
  117. return None
  118. def get_account_name(self, uid):
  119. info = self.get_account_info(uid)
  120. if info is None:
  121. return 'unknown'
  122. info = info['info']
  123. name = {}
  124. if 'RemarkName' in info and info['RemarkName']:
  125. name['remark_name'] = info['RemarkName']
  126. if 'NickName' in info and info['NickName']:
  127. name['nickname'] = info['NickName']
  128. if 'DisplayName' in info and info['DisplayName']:
  129. name['display_name'] = info['DisplayName']
  130. return name
  131. @staticmethod
  132. def get_prefer_name(name):
  133. if 'remark_name' in name:
  134. return name['remark_name']
  135. if 'display_name' in name:
  136. return name['display_name']
  137. if 'nickname' in name:
  138. return name['nickname']
  139. return 'unknown'
  140. # Get the relationship of a account and current user.
  141. def get_user_type(self, wx_user_id):
  142. for account in self.contact_list:
  143. if wx_user_id == account['UserName']:
  144. return 'contact'
  145. for account in self.public_list:
  146. if wx_user_id == account['UserName']:
  147. return 'public'
  148. for account in self.special_list:
  149. if wx_user_id == account['UserName']:
  150. return 'special'
  151. for account in self.group_list:
  152. if wx_user_id == account['UserName']:
  153. return 'group'
  154. for group in self.group_members:
  155. for member in self.group_members[group]:
  156. if member['UserName'] == wx_user_id:
  157. return 'group_member'
  158. return 'unknown'
  159. def is_contact(self, uid):
  160. for account in self.contact_list:
  161. if uid == account['UserName']:
  162. return True
  163. return False
  164. def is_public(self, uid):
  165. for account in self.public_list:
  166. if uid == account['UserName']:
  167. return True
  168. return False
  169. def is_special(self, uid):
  170. for account in self.special_list:
  171. if uid == account['UserName']:
  172. return True
  173. return False
  174. '''
  175. msg:
  176. msg_id
  177. msg_type_id
  178. user
  179. content
  180. '''
  181. def handle_msg_all(self, msg):
  182. pass
  183. '''
  184. content_type_id:
  185. 0 -> Text
  186. 1 -> Location
  187. 3 -> Image
  188. 4 -> Voice
  189. 5 -> Recommend
  190. 6 -> Animation
  191. 7 -> Share
  192. 8 -> Video
  193. 9 -> VideoCall
  194. 10 -> Redraw
  195. 11 -> Empty
  196. 99 -> Unknown
  197. '''
  198. def extract_msg_content(self, msg_type_id, msg):
  199. mtype = msg['MsgType']
  200. content = msg['Content'].replace('&lt;', '<').replace('&gt;', '>')
  201. msg_id = msg['MsgId']
  202. msg_content = {}
  203. if msg_type_id == 0:
  204. return {'type': 11, 'data': ''}
  205. elif msg_type_id == 2: # File Helper
  206. return {'type': 0, 'data': content.replace('<br/>', '\n')}
  207. elif msg_type_id == 3: # Group
  208. sp = content.find('<br/>')
  209. uid = content[:sp]
  210. content = content[sp:]
  211. content = content.replace('<br/>', '')
  212. uid = uid[:-1]
  213. msg_content['user'] = {'id': uid, 'name': self.get_prefer_name(self.get_account_name(uid))}
  214. if self.DEBUG:
  215. print msg_content['user']['name']
  216. else: # Self, Contact, Special, Public, Unknown
  217. pass
  218. if mtype == 1:
  219. if content.find('http://weixin.qq.com/cgi-bin/redirectforward?args=') != -1:
  220. r = self.session.get(content)
  221. r.encoding = 'gbk'
  222. data = r.text
  223. pos = self.search_content('title', data, 'xml')
  224. msg_content['type'] = 1
  225. msg_content['data'] = pos
  226. msg_content['detail'] = data
  227. if self.DEBUG:
  228. print ' [Location] I am at %s ' % pos
  229. else:
  230. msg_content['type'] = 0
  231. msg_content['data'] = content.replace(u'\u2005', '')
  232. if self.DEBUG:
  233. print ' [Text] %s' % msg_content['data']
  234. elif mtype == 3:
  235. msg_content['type'] = 3
  236. msg_content['data'] = self.get_msg_img_url(msg_id)
  237. if self.DEBUG:
  238. image = self.get_msg_img(msg_id)
  239. print ' [Image] %s' % image
  240. elif mtype == 34:
  241. msg_content['type'] = 4
  242. msg_content['data'] = self.get_voice_url(msg_id)
  243. if self.DEBUG:
  244. voice = self.get_voice(msg_id)
  245. print ' [Voice] %s' % voice
  246. elif mtype == 42:
  247. msg_content['type'] = 5
  248. info = msg['RecommendInfo']
  249. msg_content['data'] = {'nickname': info['NickName'],
  250. 'alias': info['Alias'],
  251. 'province': info['Province'],
  252. 'city': info['City'],
  253. 'gender': ['unknown', 'male', 'female'][info['Sex']]}
  254. if self.DEBUG:
  255. print ' [Recommend]'
  256. print ' -----------------------------'
  257. print ' | NickName: %s' % info['NickName']
  258. print ' | Alias: %s' % info['Alias']
  259. print ' | Local: %s %s' % (info['Province'], info['City'])
  260. print ' | Gender: %s' % ['unknown', 'male', 'female'][info['Sex']]
  261. print ' -----------------------------'
  262. elif mtype == 47:
  263. msg_content['type'] = 6
  264. msg_content['data'] = self.search_content('cdnurl', content)
  265. if self.DEBUG:
  266. print ' [Animation] %s' % msg_content['data']
  267. elif mtype == 49:
  268. msg_content['type'] = 7
  269. app_msg_type = ''
  270. if msg['AppMsgType'] == 3:
  271. app_msg_type = 'music'
  272. elif msg['AppMsgType'] == 5:
  273. app_msg_type = 'link'
  274. elif msg['AppMsgType'] == 7:
  275. app_msg_type = 'weibo'
  276. else:
  277. app_msg_type = 'unknown'
  278. msg_content['data'] = {'type': app_msg_type,
  279. 'title': msg['FileName'],
  280. 'desc': self.search_content('des', content, 'xml'),
  281. 'url': msg['Url'],
  282. 'from': self.search_content('appname', content, 'xml')}
  283. if self.DEBUG:
  284. print ' [Share] %s' % app_msg_type
  285. print ' --------------------------'
  286. print ' | title: %s' % msg['FileName']
  287. print ' | desc: %s' % self.search_content('des', content, 'xml')
  288. print ' | link: %s' % msg['Url']
  289. print ' | from: %s' % self.search_content('appname', content, 'xml')
  290. print ' --------------------------'
  291. elif mtype == 62:
  292. msg_content['type'] = 8
  293. msg_content['data'] = content
  294. if self.DEBUG:
  295. print ' [Video] Please check on mobiles'
  296. elif mtype == 53:
  297. msg_content['type'] = 9
  298. msg_content['data'] = content
  299. if self.DEBUG:
  300. print ' [Video Call]'
  301. elif mtype == 10002:
  302. msg_content['type'] = 10
  303. msg_content['data'] = content
  304. if self.DEBUG:
  305. print ' [Redraw]'
  306. else:
  307. msg_content['type'] = 99
  308. msg_content['data'] = content
  309. if self.DEBUG:
  310. print ' [Unknown]'
  311. return msg_content
  312. '''
  313. msg_type_id:
  314. 0 -> Init
  315. 1 -> Self
  316. 2 -> FileHelper
  317. 3 -> Group
  318. 4 -> Contact
  319. 5 -> Public
  320. 6 -> Special
  321. 99 -> Unknown
  322. '''
  323. def handle_msg(self, r):
  324. for msg in r['AddMsgList']:
  325. msg_type_id = 99
  326. user = {'id': msg['FromUserName']}
  327. if msg['MsgType'] == 51: # init message
  328. msg_type_id = 0
  329. elif msg['FromUserName'] == self.user['UserName']: # Self
  330. msg_type_id = 1
  331. user['name'] = 'self'
  332. elif msg['ToUserName'] == 'filehelper': # File Helper
  333. msg_type_id = 2
  334. user['name'] = 'file_helper'
  335. elif msg['FromUserName'][:2] == '@@': # Group
  336. msg_type_id = 3
  337. user['name'] = self.get_prefer_name(self.get_account_name(user['id']))
  338. elif self.is_contact(msg['FromUserName']): # Contact
  339. msg_type_id = 4
  340. user['name'] = self.get_prefer_name(self.get_account_name(user['id']))
  341. elif self.is_public(msg['FromUserName']): # Public
  342. msg_type_id = 5
  343. user['name'] = self.get_prefer_name(self.get_account_name(user['id']))
  344. elif self.is_special(msg['FromUserName']): # Special
  345. msg_type_id = 6
  346. user['name'] = self.get_prefer_name(self.get_account_name(user['id']))
  347. if self.DEBUG and msg_type_id != 0:
  348. print '[MSG] %s:' % user['name']
  349. content = self.extract_msg_content(msg_type_id, msg)
  350. message = {'msg_type_id': msg_type_id,
  351. 'msg_id': msg['MsgId'],
  352. 'content': content,
  353. 'user': user}
  354. self.handle_msg_all(message)
  355. def schedule(self):
  356. pass
  357. def proc_msg(self):
  358. self.test_sync_check()
  359. while True:
  360. [retcode, selector] = self.sync_check()
  361. if retcode == '1100': # User have login on mobile
  362. pass
  363. elif retcode == '0':
  364. if selector == '2':
  365. r = self.sync()
  366. if r is not None:
  367. self.handle_msg(r)
  368. elif selector == '7': # Play WeChat on mobile
  369. r = self.sync()
  370. if r is not None:
  371. self.handle_msg(r)
  372. elif selector == '0':
  373. time.sleep(1)
  374. self.schedule()
  375. def send_msg_by_uid(self, word, dst='filehelper'):
  376. url = self.base_uri + '/webwxsendmsg?pass_ticket=%s' % self.pass_ticket
  377. msg_id = str(int(time.time()*1000)) + str(random.random())[:5].replace('.', '')
  378. if type(word) == 'str':
  379. word = word.decode('utf-8')
  380. params = {
  381. 'BaseRequest': self.base_request,
  382. 'Msg': {
  383. "Type": 1,
  384. "Content": word,
  385. "FromUserName": self.user['UserName'],
  386. "ToUserName": dst,
  387. "LocalID": msg_id,
  388. "ClientMsgId": msg_id
  389. }
  390. }
  391. headers = {'content-type': 'application/json; charset=UTF-8'}
  392. data = json.dumps(params, ensure_ascii=False).encode('utf8')
  393. r = self.session.post(url, data=data, headers=headers)
  394. dic = r.json()
  395. return dic['BaseResponse']['Ret'] == 0
  396. def send_msg(self, name, word, isfile=False):
  397. uid = self.get_user_id(name)
  398. if uid:
  399. if isfile:
  400. with open(word, 'r') as f:
  401. result = True
  402. for line in f.readlines():
  403. line = line.replace('\n', '')
  404. print '-> '+name+': '+line
  405. if self.send_msg_by_uid(line, uid):
  406. pass
  407. else:
  408. result = False
  409. time.sleep(1)
  410. return result
  411. else:
  412. if self.send_msg_by_uid(word, uid):
  413. return True
  414. else:
  415. return False
  416. else:
  417. if self.DEBUG:
  418. print '[ERROR] This user does not exist .'
  419. return True
  420. @staticmethod
  421. def search_content(key, content, fmat='attr'):
  422. if fmat == 'attr':
  423. pm = re.search(key+'\s?=\s?"([^"<]+)"', content)
  424. if pm:
  425. return pm.group(1)
  426. elif fmat == 'xml':
  427. pm = re.search('<{0}>([^<]+)</{0}>'.format(key), content)
  428. if pm:
  429. return pm.group(1)
  430. return 'unknown'
  431. def run(self):
  432. self.get_uuid()
  433. self.gen_qr_code('qr.png')
  434. print '[INFO] Please use WeCaht to scan the QR code .'
  435. self.wait4login(1)
  436. print '[INFO] Please confirm to login .'
  437. self.wait4login(0)
  438. if self.login():
  439. print '[INFO] Web WeChat login succeed .'
  440. else:
  441. print '[ERROR] Web WeChat login failed .'
  442. return
  443. if self.init():
  444. print '[INFO] Web WeChat init succeed .'
  445. else:
  446. print '[INFO] Web WeChat init failed'
  447. return
  448. self.status_notify()
  449. self.get_contact()
  450. print '[INFO] Get %d contacts' % len(self.contact_list)
  451. print '[INFO] Start to process messages .'
  452. self.proc_msg()
  453. def get_uuid(self):
  454. url = 'https://login.weixin.qq.com/jslogin'
  455. params = {
  456. 'appid': 'wx782c26e4c19acffb',
  457. 'fun': 'new',
  458. 'lang': 'zh_CN',
  459. '_': int(time.time())*1000 + random.randint(1, 999),
  460. }
  461. r = self.session.get(url, params=params)
  462. r.encoding = 'utf-8'
  463. data = r.text
  464. regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'
  465. pm = re.search(regx, data)
  466. if pm:
  467. code = pm.group(1)
  468. self.uuid = pm.group(2)
  469. return code == '200'
  470. return False
  471. def gen_qr_code(self, qr_file_path):
  472. string = 'https://login.weixin.qq.com/l/' + self.uuid
  473. qr = pyqrcode.create(string)
  474. if self.conf['qr'] == 'png':
  475. qr.png(qr_file_path)
  476. elif self.conf['qr'] == 'tty':
  477. print(qr.terminal(quiet_zone=1))
  478. def wait4login(self, tip):
  479. time.sleep(tip)
  480. url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' \
  481. % (tip, self.uuid, int(time.time()))
  482. r = self.session.get(url)
  483. r.encoding = 'utf-8'
  484. data = r.text
  485. param = re.search(r'window.code=(\d+);', data)
  486. code = param.group(1)
  487. if code == '201':
  488. return True
  489. elif code == '200':
  490. param = re.search(r'window.redirect_uri="(\S+?)";', data)
  491. redirect_uri = param.group(1) + '&fun=new'
  492. self.redirect_uri = redirect_uri
  493. self.base_uri = redirect_uri[:redirect_uri.rfind('/')]
  494. return True
  495. elif code == '408':
  496. print '[ERROR] WeChat login timeout .'
  497. else:
  498. print '[ERROR] WeChat login exception .'
  499. return False
  500. def login(self):
  501. r = self.session.get(self.redirect_uri)
  502. r.encoding = 'utf-8'
  503. data = r.text
  504. doc = xml.dom.minidom.parseString(data)
  505. root = doc.documentElement
  506. for node in root.childNodes:
  507. if node.nodeName == 'skey':
  508. self.skey = node.childNodes[0].data
  509. elif node.nodeName == 'wxsid':
  510. self.sid = node.childNodes[0].data
  511. elif node.nodeName == 'wxuin':
  512. self.uin = node.childNodes[0].data
  513. elif node.nodeName == 'pass_ticket':
  514. self.pass_ticket = node.childNodes[0].data
  515. if '' in (self.skey, self.sid, self.uin, self.pass_ticket):
  516. return False
  517. self.base_request = {
  518. 'Uin': self.uin,
  519. 'Sid': self.sid,
  520. 'Skey': self.skey,
  521. 'DeviceID': self.device_id,
  522. }
  523. return True
  524. def init(self):
  525. url = self.base_uri + '/webwxinit?r=%i&lang=en_US&pass_ticket=%s' % (int(time.time()), self.pass_ticket)
  526. params = {
  527. 'BaseRequest': self.base_request
  528. }
  529. r = self.session.post(url, data=json.dumps(params))
  530. r.encoding = 'utf-8'
  531. dic = json.loads(r.text)
  532. self.sync_key = dic['SyncKey']
  533. self.user = dic['User']
  534. self.sync_key_str = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val'])
  535. for keyVal in self.sync_key['List']])
  536. return dic['BaseResponse']['Ret'] == 0
  537. def status_notify(self):
  538. url = self.base_uri + '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s' % self.pass_ticket
  539. self.base_request['Uin'] = int(self.base_request['Uin'])
  540. params = {
  541. 'BaseRequest': self.base_request,
  542. "Code": 3,
  543. "FromUserName": self.user['UserName'],
  544. "ToUserName": self.user['UserName'],
  545. "ClientMsgId": int(time.time())
  546. }
  547. r = self.session.post(url, data=json.dumps(params))
  548. r.encoding = 'utf-8'
  549. dic = json.loads(r.text)
  550. return dic['BaseResponse']['Ret'] == 0
  551. def test_sync_check(self):
  552. for host in ['webpush', 'webpush2']:
  553. self.sync_host = host
  554. retcode = self.sync_check()[0]
  555. if retcode == '0':
  556. return True
  557. return False
  558. def sync_check(self):
  559. params = {
  560. 'r': int(time.time()),
  561. 'sid': self.sid,
  562. 'uin': self.uin,
  563. 'skey': self.skey,
  564. 'deviceid': self.device_id,
  565. 'synckey': self.sync_key_str,
  566. '_': int(time.time()),
  567. }
  568. url = 'https://' + self.sync_host + '.weixin.qq.com/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params)
  569. r = self.session.get(url)
  570. r.encoding = 'utf-8'
  571. data = r.text
  572. pm = re.search(r'window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}', data)
  573. retcode = pm.group(1)
  574. selector = pm.group(2)
  575. return [retcode, selector]
  576. def sync(self):
  577. url = self.base_uri + '/webwxsync?sid=%s&skey=%s&lang=en_US&pass_ticket=%s' \
  578. % (self.sid, self.skey, self.pass_ticket)
  579. params = {
  580. 'BaseRequest': self.base_request,
  581. 'SyncKey': self.sync_key,
  582. 'rr': ~int(time.time())
  583. }
  584. r = self.session.post(url, data=json.dumps(params))
  585. r.encoding = 'utf-8'
  586. dic = json.loads(r.text)
  587. if dic['BaseResponse']['Ret'] == 0:
  588. self.sync_key = dic['SyncKey']
  589. self.sync_key_str = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val'])
  590. for keyVal in self.sync_key['List']])
  591. return dic
  592. def get_icon(self, uid):
  593. url = self.base_uri + '/webwxgeticon?username=%s&skey=%s' % (uid, self.skey)
  594. r = self.session.get(url)
  595. data = r.content
  596. fn = 'img_'+uid+'.jpg'
  597. with open(fn, 'wb') as f:
  598. f.write(data)
  599. return fn
  600. def get_head_img(self, uid):
  601. url = self.base_uri + '/webwxgetheadimg?username=%s&skey=%s' % (uid, self.skey)
  602. r = self.session.get(url)
  603. data = r.content
  604. fn = 'img_'+uid+'.jpg'
  605. with open(fn, 'wb') as f:
  606. f.write(data)
  607. return fn
  608. def get_msg_img_url(self, msgid):
  609. return self.base_uri + '/webwxgetmsgimg?MsgID=%s&skey=%s' % (msgid, self.skey)
  610. def get_msg_img(self, msgid):
  611. url = self.base_uri + '/webwxgetmsgimg?MsgID=%s&skey=%s' % (msgid, self.skey)
  612. r = self.session.get(url)
  613. data = r.content
  614. fn = 'img_'+msgid+'.jpg'
  615. with open(fn, 'wb') as f:
  616. f.write(data)
  617. return fn
  618. def get_voice_url(self, msgid):
  619. return self.base_uri + '/webwxgetvoice?msgid=%s&skey=%s' % (msgid, self.skey)
  620. def get_voice(self, msgid):
  621. url = self.base_uri + '/webwxgetvoice?msgid=%s&skey=%s' % (msgid, self.skey)
  622. r = self.session.get(url)
  623. data = r.content
  624. fn = 'voice_'+msgid+'.mp3'
  625. with open(fn, 'wb') as f:
  626. f.write(data)
  627. return fn