├── .idea ├── .name ├── encodings.xml ├── vcs.xml ├── modules.xml ├── wechat2instapaper.iml ├── misc.xml └── workspace.xml ├── __init__.py ├── CHANGES.md ├── README.md ├── wechat2instapaper.py ├── instapaper.py └── weixin.py /.idea/.name: -------------------------------------------------------------------------------- 1 | wechat2instapaper -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/wechat2instapaper.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## Wechat2instapaper 2 | 3 | ## Version 0.2 4 | 5 | ## Update: 6 | * 1.Change the frame into [WeixinBot][1]. 7 | * 2.Add a new command “#insta” to push all your link to your instapaper. 8 | * 3.Add a new command “#list” whose function is able to show all your list. 9 | * 4.Add a new command “#del”+number whose function is able to delect the item of your list. 10 | * 5.Add a new command “#num” whose function is able to show the number of your list. 11 | 12 | 13 | [1]: https://github.com/Urinx/WeixinBot -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wechat2instapaper 2 | 3 | ## Version 0.2 4 | ## Update: 5 | * 1.Change the frame into [WeixinBot][1]. 6 | * 2.Add a new command “#insta” to push all your links to your instapaper. 7 | * 3.Add a new command “#list” whose function is able to show all your items. 8 | * 4.Add a new command “#del”+number whose function is able to delect the item of your list. 9 | * 5.Add a new command “#num” whose function is able to show the number of your list. 10 | 11 | 12 | ## 1.Based on 13 | 14 | [WeixinBot][2] 15 | 16 | [instapaper ][3] 17 | 18 | ## 2.Environment 19 | 20 | Python 2.7 21 | 22 | 23 | 24 | ## 3.Install 25 | 26 | ``` 27 | $ git clone git://github.com/chanjh/wechat2instapaper 28 | $ cd wechat2instapaper 29 | ``` 30 | 31 | ## 4.Code & conf 32 | 33 | You should change 34 | ``` 35 | ipaper = instapaper.Instapaper('Your Consumer Key', 'Your Secret') 36 | ipaper.login('Username', 'Password') 37 | ``` 38 | in wechat2instapaper.py into your own Instapaper data. 39 | You can get consumer key in: [Register New OAuth Application][4] 40 | 41 | ## 5.Run 42 | 43 | `$ python wechat2instapaper.py` 44 | 45 | Use WeChat to scan the QR code and confirm to login. 46 | 47 | ## 6.Share 48 | 49 | Use another WeChat account to send a link to the logined account.And then when you send "#insta" to it,it would save all your links to Instapaper. 50 | 51 | ## 7.More 52 | 53 | Learn more about me and my project on my blog: [ChanTalk][5] 54 | 55 | 56 | [1]: https://github.com/Urinx/WeixinBot 57 | [2]: https://github.com/Urinx/WeixinBot 58 | [3]: https://github.com/rsgalloway/instapaper 59 | [4]: https://www.instapaper.com/main/request_oauth_consumer_token 60 | [5]: http://chanjh.com/ -------------------------------------------------------------------------------- /wechat2instapaper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # from wxbot import * 5 | from weixin import * 6 | import instapaper 7 | import types 8 | 9 | __author__= "Chanjh " 10 | 11 | __dic__=""" 12 | This is a python wrapper for adding new url to the Instapaper. 13 | 14 | http://github.com/chanjh 15 | """ 16 | 17 | class MyWXBot(WebWeixin): 18 | 19 | def messageHandle(self, msgBox, content, name): 20 | if content == "#insta": 21 | self.sendAll2insta(msgBox, name) 22 | self.sendMsg(name, "Success!") 23 | elif content == "#list": 24 | self.showAllTheContent(msgBox, name) 25 | elif content[0:4] == "#del": 26 | try: 27 | word = msgBox[ int(content[4:]) ] 28 | del msgBox[ int(content[4:]) ] 29 | self.sendMsg(name, content[4:] + ".已删除") 30 | except: 31 | self.sendMsg(name, "You cannot delect songthing out of range!") 32 | elif content == "#num": 33 | self.sendMsg(name, "一共保存【" + str(len(msgBox)-1) + "】条目") 34 | else: 35 | self.storeTheTextInBox(content, msgBox) 36 | return msgBox 37 | 38 | def showAllTheContent(self, msgBox, name): 39 | for i in range(1,len(msgBox)): 40 | if type(msgBox[i]) is types.StringType or types.DictType: 41 | if type(msgBox[i]) is types.StringType: 42 | word = str(i) + "." + msgBox[i] 43 | else: 44 | word = str(i) + "." + "【标题】:\n" + msgBox[i]['fileName'] + "\n【链接】:\n" + msgBox[i]['url'] 45 | self.sendMsg(name, word) 46 | else: 47 | pass 48 | 49 | 50 | 51 | def storeTheTextInBox(self, content, msgBox): 52 | msgBox.append(content) 53 | return msgBox 54 | 55 | def storeTheSharingInBox(self, fileName, url, msgBox): 56 | urlDetail = {'fileName':fileName, 'url':url} 57 | msgBox.append(urlDetail) 58 | return msgBox 59 | 60 | 61 | def sendAll2insta(self, msgBox, name): 62 | 63 | dics = [] 64 | i = 0 65 | 66 | while True: 67 | if i > len(msgBox)-1: 68 | break 69 | elif type(msgBox[i]) is types.DictType: 70 | dics.append(msgBox[i]) 71 | del msgBox[i] 72 | else: 73 | i+=1 74 | 75 | for i in range(0, len(dics)): 76 | ipaper = instapaper.Instapaper('Your Consumer Key', 'Your Secret') 77 | try: 78 | ipaper.login('Username', 'Password') 79 | result = ipaper.add(dics[i]['url']) 80 | if result == 0: 81 | word = "《" + dics[i]['fileName'] + "》\n【保存失败】" 82 | elif result == 1: 83 | word = "《" + dics[i]['fileName'] + "》\n【保存成功】" 84 | except: 85 | word = u"保存失败,账号密码错误或 API 出错,请检查" 86 | 87 | self.sendMsg(name, word) 88 | print word,msgBox 89 | 90 | def main(): 91 | webwx = MyWXBot() 92 | 93 | logger = logging.getLogger(__name__) 94 | import coloredlogs 95 | coloredlogs.install(level='DEBUG') 96 | 97 | 98 | webwx.start() 99 | 100 | 101 | if sys.stdout.encoding == 'cp936': 102 | sys.stdout = UnicodeStreamFilter(sys.stdout) 103 | 104 | 105 | if __name__ == '__main__': 106 | main() 107 | -------------------------------------------------------------------------------- /instapaper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import urllib2 4 | import urlparse 5 | import simplejson as json 6 | import oauth2 as oauth 7 | from lxml import etree 8 | from urllib import urlencode 9 | 10 | from HTMLParser import HTMLParser 11 | from re import sub 12 | from sys import stderr 13 | from traceback import print_exc 14 | 15 | __author__= "Chanjh " 16 | 17 | __changeFrom__ = "https://github.com/nickbarnwell/Instapaper-py" 18 | 19 | __doc__ = """ 20 | A Python wrapper for adding new url to the Instapaper. 21 | 22 | http://www.instapaper.com/api/full 23 | """ 24 | 25 | 26 | _BASE_ = "https://www.instapaper.com" 27 | _API_VERSION_ = "api/1" 28 | _ACCESS_TOKEN_ = "oauth/access_token" 29 | _BOOKMARKS_ADD_ = "bookmarks/add" 30 | 31 | 32 | class Bookmark(object): 33 | def __init__(self, parent, params): 34 | self.parent = parent 35 | self.__text = None 36 | self.__html = None 37 | self.__dict__.update(params) 38 | 39 | @property 40 | def html(self): 41 | if self.__html is None: 42 | response, html = self.parent.http.request( 43 | "/".join([_BASE_, _API_VERSION_, _BOOKMARKS_TEXT_]), 44 | method='POST', 45 | body=urlencode({ 46 | 'bookmark_id': self.bookmark_id, 47 | })) 48 | if response.get("status") == "200": 49 | self.__html = html 50 | return self.__html 51 | 52 | @property 53 | def text(self): 54 | if self.__text is None: 55 | self.__text = dehtml(self.html) 56 | return self.__text 57 | 58 | def star(self): 59 | raise NotImplementedError 60 | 61 | def delete(self): 62 | raise NotImplementedError 63 | 64 | def save(self): 65 | raise NotImplementedError 66 | 67 | 68 | class Instapaper(object): 69 | def __init__(self, oauthkey, oauthsec): 70 | self.consumer = oauth.Consumer(oauthkey, oauthsec) 71 | self.client = oauth.Client(self.consumer) 72 | self.token = None 73 | self.http = None 74 | 75 | def login(self, username, password): 76 | response, content = self.client.request( 77 | "/".join([_BASE_, _API_VERSION_, _ACCESS_TOKEN_]), 78 | "POST", urlencode({ 79 | 'x_auth_mode': 'client_auth', 80 | 'x_auth_username': username, 81 | 'x_auth_password': password})) 82 | _oauth = dict(urlparse.parse_qsl(content)) 83 | self.token = oauth.Token(_oauth['oauth_token'], 84 | _oauth['oauth_token_secret']) 85 | self.http = oauth.Client(self.consumer, self.token) 86 | 87 | def add(self, url): 88 | response, data = self.http.request( 89 | "/".join([_BASE_, _API_VERSION_, _BOOKMARKS_ADD_]), 90 | method='POST', 91 | body=urlencode({ 92 | 'url':url 93 | })) 94 | marks = [] 95 | items = json.loads(data) 96 | for item in items: 97 | if item.get("type") == "error": 98 | raise Exception(item.get("message")) 99 | return 0 100 | elif item.get("type") == "bookmark": 101 | marks.append(Bookmark(self, item)) 102 | return 1 103 | 104 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | true 69 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 113 | 114 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 1458316081686 136 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 169 | 172 | 173 | 174 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /weixin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | import qrcode 4 | import urllib, urllib2 5 | import cookielib 6 | import requests 7 | import xml.dom.minidom 8 | import json 9 | import time, re, sys, os, random 10 | import multiprocessing 11 | import platform 12 | import logging 13 | from collections import defaultdict 14 | from urlparse import urlparse 15 | from lxml import html 16 | 17 | msgBox = [] 18 | 19 | def catchKeyboardInterrupt(fn): 20 | def wrapper(*args): 21 | try: 22 | return fn(*args) 23 | except KeyboardInterrupt: 24 | print '\n[*] 强制退出程序' 25 | logging.debug('[*] 强制退出程序') 26 | return wrapper 27 | 28 | def _decode_list(data): 29 | rv = [] 30 | for item in data: 31 | if isinstance(item, unicode): 32 | item = item.encode('utf-8') 33 | elif isinstance(item, list): 34 | item = _decode_list(item) 35 | elif isinstance(item, dict): 36 | item = _decode_dict(item) 37 | rv.append(item) 38 | return rv 39 | 40 | def _decode_dict(data): 41 | rv = {} 42 | for key, value in data.iteritems(): 43 | if isinstance(key, unicode): 44 | key = key.encode('utf-8') 45 | if isinstance(value, unicode): 46 | value = value.encode('utf-8') 47 | elif isinstance(value, list): 48 | value = _decode_list(value) 49 | elif isinstance(value, dict): 50 | value = _decode_dict(value) 51 | rv[key] = value 52 | return rv 53 | 54 | class WebWeixin(object): 55 | def __str__(self): 56 | description = \ 57 | "=========================\n" + \ 58 | "[#] Web Weixin\n" + \ 59 | "[#] Debug Mode: " + str(self.DEBUG) + "\n" + \ 60 | "[#] Uuid: " + self.uuid + "\n" + \ 61 | "[#] Uin: " + str(self.uin) + "\n" + \ 62 | "[#] Sid: " + self.sid + "\n" + \ 63 | "[#] Skey: " + self.skey + "\n" + \ 64 | "[#] DeviceId: " + self.deviceId + "\n" + \ 65 | "[#] PassTicket: " + self.pass_ticket + "\n" + \ 66 | "=========================" 67 | return description 68 | 69 | # def instapaper(self, url): 70 | # ipaper = instapaper.Instapaper('4316ef7422ec43b6b9a991247eafc4f1', '79fd8bb7888d43f489279d98ab830db4') 71 | # ipaper.login('705676521@qq.com', 'wode0408.') 72 | # result,marks = ipaper.add(url) 73 | 74 | def __init__(self): 75 | self.DEBUG = False 76 | self.uuid = '' 77 | self.base_uri = '' 78 | self.redirect_uri= '' 79 | self.uin = '' 80 | self.sid = '' 81 | self.skey = '' 82 | self.pass_ticket = '' 83 | self.deviceId = 'e' + repr(random.random())[2:17] 84 | self.BaseRequest = {} 85 | self.synckey = '' 86 | self.SyncKey = [] 87 | self.User = [] 88 | self.MemberList = [] 89 | self.ContactList = [] # 好友 90 | self.GroupList = [] # 群 91 | self.GroupMemeberList = [] # 群友 92 | self.PublicUsersList = [] # 公众号/服务号 93 | self.SpecialUsersList = [] # 特殊账号 94 | self.autoReplyMode = False 95 | self.syncHost = '' 96 | self.user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36' 97 | self.interactive = False 98 | self.autoOpen = False 99 | self.saveFolder = os.path.join(os.getcwd(), 'saved') 100 | self.saveSubFolders = {'webwxgeticon': 'icons', 'webwxgetheadimg': 'headimgs', 'webwxgetmsgimg': 'msgimgs', 'webwxgetvideo': 'videos', 'webwxgetvoice': 'voices', '_showQRCodeImg': 'qrcodes'} 101 | self.appid = 'wx782c26e4c19acffb' 102 | self.lang = 'zh_CN' 103 | self.lastCheckTs = time.time() 104 | self.memberCount = 0 105 | self.SpecialUsers = ['newsapp', 'fmessage', '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', 'gh_22b87fa7cb3c', 'officialaccounts', 'notification_messages', 'wxid_novlwrv3lqwv11', 'gh_22b87fa7cb3c', 'wxitil', 'userexperience_alarm', 'notification_messages'] 106 | self.TimeOut = 20 # 同步最短时间间隔(单位:秒) 107 | 108 | 109 | opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookielib.CookieJar())) 110 | opener.addheaders = [('User-agent', self.user_agent)] 111 | urllib2.install_opener(opener) 112 | 113 | def loadConfig(self, config): 114 | if config['DEBUG']: self.DEBUG = config['DEBUG'] 115 | if config['autoReplyMode']: self.autoReplyMode = config['autoReplyMode'] 116 | if config['user_agent']: self.user_agent = config['user_agent'] 117 | if config['interactive']: self.interactive = config['interactive'] 118 | if config['autoOpen']: self.autoOpen = config['autoOpen'] 119 | 120 | def getUUID(self): 121 | url = 'https://login.weixin.qq.com/jslogin' 122 | params = { 123 | 'appid': self.appid, 124 | 'fun': 'new', 125 | 'lang': self.lang, 126 | '_': int(time.time()), 127 | } 128 | data = self._post(url, params, False) 129 | regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"' 130 | pm = re.search(regx, data) 131 | if pm: 132 | code = pm.group(1) 133 | self.uuid = pm.group(2) 134 | return code == '200' 135 | return False 136 | 137 | def genQRCode(self): 138 | if sys.platform.startswith('win'): 139 | self._showQRCodeImg() 140 | else: 141 | self._str2qr('https://login.weixin.qq.com/l/' + self.uuid) 142 | 143 | def _showQRCodeImg(self): 144 | url = 'https://login.weixin.qq.com/qrcode/' + self.uuid 145 | params = { 146 | 't' : 'webwx', 147 | '_' : int(time.time()) 148 | } 149 | 150 | data = self._post(url, params, False) 151 | QRCODE_PATH = self._saveFile('qrcode.jpg', data, '_showQRCodeImg') 152 | os.startfile(QRCODE_PATH) 153 | 154 | def waitForLogin(self, tip = 1): 155 | time.sleep(tip) 156 | url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (tip, self.uuid, int(time.time())) 157 | data = self._get(url) 158 | pm = re.search(r'window.code=(\d+);', data) 159 | code = pm.group(1) 160 | 161 | if code == '201': return True 162 | elif code == '200': 163 | pm = re.search(r'window.redirect_uri="(\S+?)";', data) 164 | r_uri = pm.group(1) + '&fun=new' 165 | self.redirect_uri = r_uri 166 | self.base_uri = r_uri[:r_uri.rfind('/')] 167 | return True 168 | elif code == '408': 169 | self._echo('[登陆超时] \n') 170 | else: 171 | self._echo('[登陆异常] \n') 172 | return False 173 | 174 | def login(self): 175 | data = self._get(self.redirect_uri) 176 | doc = xml.dom.minidom.parseString(data) 177 | root = doc.documentElement 178 | 179 | for node in root.childNodes: 180 | if node.nodeName == 'skey': 181 | self.skey = node.childNodes[0].data 182 | elif node.nodeName == 'wxsid': 183 | self.sid = node.childNodes[0].data 184 | elif node.nodeName == 'wxuin': 185 | self.uin = node.childNodes[0].data 186 | elif node.nodeName == 'pass_ticket': 187 | self.pass_ticket = node.childNodes[0].data 188 | 189 | if '' in (self.skey, self.sid, self.uin, self.pass_ticket): 190 | return False 191 | 192 | self.BaseRequest = { 193 | 'Uin': int(self.uin), 194 | 'Sid': self.sid, 195 | 'Skey': self.skey, 196 | 'DeviceID': self.deviceId, 197 | } 198 | return True 199 | 200 | def webwxinit(self): 201 | url = self.base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (self.pass_ticket, self.skey, int(time.time())) 202 | params = { 203 | 'BaseRequest': self.BaseRequest 204 | } 205 | dic = self._post(url, params) 206 | self.SyncKey = dic['SyncKey'] 207 | self.User = dic['User'] 208 | # synckey for synccheck 209 | self.synckey = '|'.join([ str(keyVal['Key']) + '_' + str(keyVal['Val']) for keyVal in self.SyncKey['List'] ]) 210 | 211 | return dic['BaseResponse']['Ret'] == 0 212 | 213 | def webwxstatusnotify(self): 214 | url = self.base_uri + '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s' % (self.pass_ticket) 215 | params = { 216 | 'BaseRequest': self.BaseRequest, 217 | "Code": 3, 218 | "FromUserName": self.User['UserName'], 219 | "ToUserName": self.User['UserName'], 220 | "ClientMsgId": int(time.time()) 221 | } 222 | dic = self._post(url, params) 223 | 224 | return dic['BaseResponse']['Ret'] == 0 225 | 226 | def webwxgetcontact(self): 227 | SpecialUsers = self.SpecialUsers 228 | url = self.base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % (self.pass_ticket, self.skey, int(time.time())) 229 | dic = self._post(url, {}) 230 | 231 | self.MemberCount = dic['MemberCount'] 232 | self.MemberList = dic['MemberList'] 233 | ContactList = self.MemberList[:] 234 | GroupList = self.GroupList[:] 235 | PublicUsersList = self.PublicUsersList[:] 236 | SpecialUsersList = self.SpecialUsersList[:] 237 | 238 | for i in xrange(len(ContactList) - 1, -1, -1): 239 | Contact = ContactList[i] 240 | if Contact['VerifyFlag'] & 8 != 0: # 公众号/服务号 241 | ContactList.remove(Contact) 242 | self.PublicUsersList.append(Contact) 243 | elif Contact['UserName'] in SpecialUsers: # 特殊账号 244 | ContactList.remove(Contact) 245 | self.SpecialUsersList.append(Contact) 246 | elif Contact['UserName'].find('@@') != -1: # 群聊 247 | ContactList.remove(Contact) 248 | self.GroupList.append(Contact) 249 | elif Contact['UserName'] == self.User['UserName']: # 自己 250 | ContactList.remove(Contact) 251 | self.ContactList = ContactList 252 | 253 | return True 254 | 255 | def webwxbatchgetcontact(self): 256 | url = self.base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int(time.time()), self.pass_ticket) 257 | params = { 258 | 'BaseRequest': self.BaseRequest, 259 | "Count": len(self.GroupList), 260 | "List": [ {"UserName": g['UserName'], "EncryChatRoomId":""} for g in self.GroupList ] 261 | } 262 | dic = self._post(url, params) 263 | 264 | # blabla ... 265 | ContactList = dic['ContactList'] 266 | ContactCount = dic['Count'] 267 | self.GroupList = ContactList 268 | 269 | for i in xrange(len(ContactList) - 1, -1, -1): 270 | Contact = ContactList[i] 271 | MemberList = Contact['MemberList'] 272 | for member in MemberList: 273 | self.GroupMemeberList.append(member) 274 | return True 275 | 276 | def getNameById(self, id): 277 | url = self.base_uri + '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s' % (int(time.time()), self.pass_ticket) 278 | params = { 279 | 'BaseRequest': self.BaseRequest, 280 | "Count": 1, 281 | "List": [ {"UserName": id, "EncryChatRoomId":""} ] 282 | } 283 | dic = self._post(url, params) 284 | 285 | # blabla ... 286 | return dic['ContactList'] 287 | 288 | def testsynccheck(self): 289 | SyncHost = [ 290 | 'webpush.weixin.qq.com', 291 | 'webpush2.weixin.qq.com', 292 | 'webpush.wechat.com', 293 | 'webpush1.wechat.com', 294 | 'webpush2.wechat.com', 295 | 'webpush1.wechatapp.com', 296 | # 'webpush.wechatapp.com' 297 | ] 298 | for host in SyncHost: 299 | self.syncHost = host 300 | [retcode, selector] = self.synccheck() 301 | if retcode == '0': return True 302 | return False 303 | 304 | def synccheck(self): 305 | params = { 306 | 'r': int(time.time()), 307 | 'sid': self.sid, 308 | 'uin': self.uin, 309 | 'skey': self.skey, 310 | 'deviceid': self.deviceId, 311 | 'synckey': self.synckey, 312 | '_': int(time.time()), 313 | } 314 | url = 'https://' + self.syncHost + '/cgi-bin/mmwebwx-bin/synccheck?' + urllib.urlencode(params) 315 | data = self._get(url) 316 | pm = re.search(r'window.synccheck={retcode:"(\d+)",selector:"(\d+)"}', data) 317 | retcode = pm.group(1) 318 | selector = pm.group(2) 319 | return [retcode, selector] 320 | 321 | def webwxsync(self): 322 | url = self.base_uri + '/webwxsync?sid=%s&skey=%s&pass_ticket=%s' % (self.sid, self.skey, self.pass_ticket) 323 | params = { 324 | 'BaseRequest': self.BaseRequest, 325 | 'SyncKey': self.SyncKey, 326 | 'rr': ~int(time.time()) 327 | } 328 | dic = self._post(url, params) 329 | if self.DEBUG: 330 | print json.dumps(dic, indent=4) 331 | logging.debug(json.dumps(dic, indent=4)) 332 | 333 | if dic['BaseResponse']['Ret'] == 0: 334 | self.SyncKey = dic['SyncKey'] 335 | self.synckey = '|'.join([ str(keyVal['Key']) + '_' + str(keyVal['Val']) for keyVal in self.SyncKey['List'] ]) 336 | return dic 337 | 338 | def webwxsendmsg(self, word, to = 'filehelper'): 339 | url = self.base_uri + '/webwxsendmsg?pass_ticket=%s' % (self.pass_ticket) 340 | clientMsgId = str(int(time.time()*1000)) + str(random.random())[:5].replace('.','') 341 | params = { 342 | 'BaseRequest': self.BaseRequest, 343 | 'Msg': { 344 | "Type": 1, 345 | "Content": self._transcoding(word), 346 | "FromUserName": self.User['UserName'], 347 | "ToUserName": to, 348 | "LocalID": clientMsgId, 349 | "ClientMsgId": clientMsgId 350 | } 351 | } 352 | headers = {'content-type': 'application/json; charset=UTF-8'} 353 | data = json.dumps(params, ensure_ascii=False).encode('utf8') 354 | r = requests.post(url, data = data, headers = headers) 355 | dic = r.json() 356 | return dic['BaseResponse']['Ret'] == 0 357 | 358 | def _saveFile(self, filename, data, api=None): 359 | fn = filename 360 | if self.saveSubFolders[api]: 361 | dirName = os.path.join(self.saveFolder, self.saveSubFolders[api]) 362 | if not os.path.exists(dirName): 363 | os.makedirs(dirName) 364 | fn = os.path.join(dirName, filename) 365 | logging.debug('Saved file: %s' % fn) 366 | with open(fn, 'wb') as f: f.write(data); f.close() 367 | return fn 368 | 369 | def webwxgeticon(self, id): 370 | url = self.base_uri + '/webwxgeticon?username=%s&skey=%s' % (id, self.skey) 371 | data = self._get(url) 372 | fn = 'img_'+id+'.jpg' 373 | return self._saveFile(fn, data, 'webwxgeticon') 374 | 375 | def webwxgetheadimg(self, id): 376 | url = self.base_uri + '/webwxgetheadimg?username=%s&skey=%s' % (id, self.skey) 377 | data = self._get(url) 378 | fn = 'img_'+id+'.jpg' 379 | return self._saveFile(fn, data, 'webwxgetheadimg') 380 | 381 | def webwxgetmsgimg(self, msgid): 382 | url = self.base_uri + '/webwxgetmsgimg?MsgID=%s&skey=%s' % (msgid, self.skey) 383 | data = self._get(url) 384 | fn = 'img_'+msgid+'.jpg' 385 | return self._saveFile(fn, data, 'webwxgetmsgimg') 386 | 387 | # Not work now for weixin haven't support this API 388 | def webwxgetvideo(self, msgid): 389 | url = self.base_uri + '/webwxgetvideo?msgid=%s&skey=%s' % (msgid, self.skey) 390 | data = self._get(url, api='webwxgetvideo') 391 | fn = 'video_'+msgid+'.mp4' 392 | return self._saveFile(fn, data, 'webwxgetvideo') 393 | 394 | def webwxgetvoice(self, msgid): 395 | url = self.base_uri + '/webwxgetvoice?msgid=%s&skey=%s' % (msgid, self.skey) 396 | data = self._get(url) 397 | fn = 'voice_'+msgid+'.mp3' 398 | return self._saveFile(fn, data, 'webwxgetvoice') 399 | 400 | def getGroupName(self, id): 401 | name = '未知群' 402 | for member in self.GroupList: 403 | if member['UserName'] == id: 404 | name = member['NickName'] 405 | if name == '未知群': 406 | # 现有群里面查不到 407 | GroupList = self.getNameById(id) 408 | for group in GroupList: 409 | self.GroupList.append(group) 410 | if group['UserName'] == id: 411 | name = group['NickName'] 412 | MemberList = group['MemberList'] 413 | for member in MemberList: 414 | self.GroupMemeberList.append(member) 415 | return name 416 | 417 | def getUserRemarkName(self, id): 418 | name = '未知群' if id[:2] == '@@' else '陌生人' 419 | if id == self.User['UserName']: return self.User['NickName'] # 自己 420 | 421 | if id[:2] == '@@': 422 | # 群 423 | name = self.getGroupName(id) 424 | else: 425 | # 特殊账号 426 | for member in self.SpecialUsersList: 427 | if member['UserName'] == id: 428 | name = member['RemarkName'] if member['RemarkName'] else member['NickName'] 429 | 430 | # 公众号或服务号 431 | for member in self.PublicUsersList: 432 | if member['UserName'] == id: 433 | name = member['RemarkName'] if member['RemarkName'] else member['NickName'] 434 | 435 | # 直接联系人 436 | for member in self.ContactList: 437 | if member['UserName'] == id: 438 | name = member['RemarkName'] if member['RemarkName'] else member['NickName'] 439 | # 群友 440 | for member in self.GroupMemeberList: 441 | if member['UserName'] == id: 442 | name = member['DisplayName'] if member['DisplayName'] else member['NickName'] 443 | 444 | if name == '未知群' or name == '陌生人': logging.debug(id) 445 | return name 446 | 447 | def getUSerID(self, name): 448 | for member in self.MemberList: 449 | if name == member['RemarkName'] or name == member['NickName']: 450 | return member['UserName'] 451 | return None 452 | 453 | def _showMsg(self, message): 454 | 455 | srcName = None 456 | dstName = None 457 | groupName = None 458 | content = None 459 | 460 | msg = message 461 | logging.debug(msg) 462 | 463 | if msg['raw_msg']: 464 | srcName = self.getUserRemarkName(msg['raw_msg']['FromUserName']) 465 | dstName = self.getUserRemarkName(msg['raw_msg']['ToUserName']) 466 | content = msg['raw_msg']['Content'].replace('<','<').replace('>','>') 467 | message_id = msg['raw_msg']['MsgId'] 468 | 469 | if content.find('http://weixin.qq.com/cgi-bin/redirectforward?args=') != -1: 470 | # 地理位置消息 471 | data = self._get(content).decode('gbk').encode('utf-8') 472 | pos = self._searchContent('title', data, 'xml') 473 | tree = html.fromstring(self._get(content)) 474 | url = tree.xpath('//html/body/div/img')[0].attrib['src'] 475 | 476 | for item in urlparse(url).query.split('&'): 477 | if item.split('=')[0] == 'center': loc = item.split('=')[-1:] 478 | 479 | content = '%s 发送了一个 位置消息 - 我在 [%s](%s) @ %s]' % (srcName, pos, url, loc) 480 | 481 | if msg['raw_msg']['ToUserName'] == 'filehelper': 482 | # 文件传输助手 483 | dstName = '文件传输助手' 484 | 485 | if msg['raw_msg']['FromUserName'][:2] == '@@': 486 | # 接收到来自群的消息 487 | if re.search(":
", content, re.IGNORECASE): 488 | [people, content] = content.split(':
') 489 | groupName = srcName 490 | srcName = self.getUserRemarkName(people) 491 | dstName = 'GROUP' 492 | else: 493 | groupName = srcName 494 | srcName = 'SYSTEM' 495 | elif msg['raw_msg']['ToUserName'][:2] == '@@': 496 | # 自己发给群的消息 497 | groupName = dstName 498 | dstName = 'GROUP' 499 | 500 | # 收到了红包 501 | if content == '收到红包,请在手机上查看': msg['message'] = content 502 | 503 | # 指定了消息内容 504 | if 'message' in msg.keys(): content = msg['message'] 505 | 506 | 507 | if groupName != None: 508 | print '%s |%s| %s -> %s: %s' % (message_id, groupName.strip(), srcName.strip(), dstName.strip(), content.replace('
','\n')) 509 | logging.info('%s |%s| %s -> %s: %s' % (message_id, groupName.strip(), srcName.strip(), dstName.strip(), content.replace('
','\n'))) 510 | else: 511 | print '%s %s -> %s: %s' % (message_id, srcName.strip(), dstName.strip(), content.replace('
','\n')) 512 | logging.info('%s %s -> %s: %s' % (message_id, srcName.strip(), dstName.strip(), content.replace('
','\n'))) 513 | 514 | def handleMsg(self, r): 515 | for msg in r['AddMsgList']: 516 | print '[*] 你有新的消息,请注意查收' 517 | logging.debug('[*] 你有新的消息,请注意查收') 518 | 519 | if self.DEBUG: 520 | fn = 'msg' + str(int(random.random() * 1000)) + '.json' 521 | with open(fn, 'w') as f: f.write(json.dumps(msg)) 522 | print '[*] 该消息已储存到文件: ' + fn 523 | logging.debug('[*] 该消息已储存到文件: %s' % (fn)) 524 | 525 | msgType = msg['MsgType'] 526 | name = self.getUserRemarkName(msg['FromUserName']) 527 | content = msg['Content'].replace('<','<').replace('>','>') 528 | msgid = msg['MsgId'] 529 | 530 | if msgType == 1: 531 | raw_msg = { 'raw_msg': msg } 532 | 533 | self.messageHandle(msgBox, content, name) 534 | 535 | self._showMsg(raw_msg) 536 | if self.autoReplyMode: 537 | ans = self._xiaodoubi(content)+'\n[微信机器人自动回复]' 538 | if self.webwxsendmsg(ans, msg['FromUserName']): 539 | print '自动回复: '+ans 540 | logging.info('自动回复: '+ans) 541 | else: 542 | print '自动回复失败' 543 | logging.info('自动回复失败') 544 | elif msgType == 3: 545 | image = self.webwxgetmsgimg(msgid) 546 | raw_msg = { 'raw_msg': msg, 'message': '%s 发送了一张图片: %s' % (name, image) } 547 | self._showMsg(raw_msg) 548 | self._safe_open(image) 549 | elif msgType == 34: 550 | voice = self.webwxgetvoice(msgid) 551 | raw_msg = { 'raw_msg': msg, 'message': '%s 发了一段语音: %s' % (name, voice) } 552 | self._showMsg(raw_msg) 553 | self._safe_open(voice) 554 | elif msgType == 42: 555 | info = msg['RecommendInfo'] 556 | print '%s 发送了一张名片:' % name 557 | print '=========================' 558 | print '= 昵称: %s' % info['NickName'] 559 | print '= 微信号: %s' % info['Alias'] 560 | print '= 地区: %s %s' % (info['Province'], info['City']) 561 | print '= 性别: %s' % ['未知', '男', '女'][info['Sex']] 562 | print '=========================' 563 | raw_msg = { 'raw_msg': msg, 'message': '%s 发送了一张名片: %s' % (name.strip(), json.dumps(info)) } 564 | self._showMsg(raw_msg) 565 | elif msgType == 47: 566 | url = self._searchContent('cdnurl', content) 567 | raw_msg = { 'raw_msg': msg, 'message': '%s 发了一个动画表情,点击下面链接查看: %s' % (name, url) } 568 | self._showMsg(raw_msg) 569 | self._safe_open(url) 570 | elif msgType == 49: 571 | appMsgType = defaultdict(lambda : "") 572 | appMsgType.update({5:'链接', 3:'音乐', 7:'微博'}) 573 | # if appMsgType[msg['AppMsgType']] == 5: 574 | self.storeTheSharingInBox(msg['FileName'], msg['Url'], msgBox) 575 | 576 | # else: 577 | # pass 578 | print '%s 分享了一个%s:' % (name, appMsgType[msg['AppMsgType']]) 579 | print '=========================' 580 | print '= 标题: %s' % msg['FileName'] 581 | print '= 描述: %s' % self._searchContent('des', content, 'xml') 582 | print '= 链接: %s' % msg['Url'] 583 | print '= 来自: %s' % self._searchContent('appname', content, 'xml') 584 | print '=========================' 585 | card = { 586 | 'title': msg['FileName'], 587 | 'description': self._searchContent('des', content, 'xml'), 588 | 'url': msg['Url'], 589 | 'appname': self._searchContent('appname', content, 'xml') 590 | } 591 | raw_msg = { 'raw_msg': msg, 'message': '%s 分享了一个%s: %s' % (name, appMsgType[msg['AppMsgType']], json.dumps(card)) } 592 | self._showMsg(raw_msg) 593 | elif msgType == 51: 594 | raw_msg = { 'raw_msg': msg, 'message': '[*] 成功获取联系人信息' } 595 | self._showMsg(raw_msg) 596 | elif msgType == 62: 597 | video = self.webwxgetvideo(msgid) 598 | raw_msg = { 'raw_msg': msg, 'message': '%s 发了一段小视频: %s' % (name, video) } 599 | self._showMsg(raw_msg) 600 | self._safe_open(video) 601 | elif msgType == 10002: 602 | raw_msg = { 'raw_msg': msg, 'message': '%s 撤回了一条消息' % name } 603 | self._showMsg(raw_msg) 604 | else: 605 | logging.debug('[*] 该消息类型为: %d,可能是表情,图片, 链接或红包: %s' % (msg['MsgType'], json.dumps(msg))) 606 | raw_msg = { 'raw_msg': msg, 'message': '[*] 该消息类型为: %d,可能是表情,图片, 链接或红包' % msg['MsgType'] } 607 | self._showMsg(raw_msg) 608 | 609 | def listenMsgMode(self): 610 | print '[*] 进入消息监听模式 ... 成功' 611 | logging.debug('[*] 进入消息监听模式 ... 成功') 612 | self._run('[*] 进行同步线路测试 ... ', self.testsynccheck) 613 | playWeChat = 0 614 | redEnvelope = 0 615 | while True: 616 | self.lastCheckTs = time.time() 617 | [retcode, selector] = self.synccheck() 618 | if self.DEBUG: print 'retcode: %s, selector: %s' % (retcode, selector) 619 | logging.debug('retcode: %s, selector: %s' % (retcode, selector)) 620 | if retcode == '1100': 621 | print '[*] 你在手机上登出了微信,债见' 622 | logging.debug('[*] 你在手机上登出了微信,债见') 623 | break 624 | if retcode == '1101': 625 | print '[*] 你在其他地方登录了 WEB 版微信,债见' 626 | logging.debug('[*] 你在其他地方登录了 WEB 版微信,债见') 627 | break 628 | elif retcode == '0': 629 | if selector == '2': 630 | r = self.webwxsync() 631 | if r is not None: self.handleMsg(r) 632 | elif selector == '6': 633 | # TODO 634 | redEnvelope += 1 635 | print '[*] 收到疑似红包消息 %d 次' % redEnvelope 636 | logging.debug('[*] 收到疑似红包消息 %d 次' % redEnvelope) 637 | elif selector == '7': 638 | playWeChat += 1 639 | print '[*] 你在手机上玩微信被我发现了 %d 次' % playWeChat 640 | logging.debug('[*] 你在手机上玩微信被我发现了 %d 次' % playWeChat) 641 | r = self.webwxsync() 642 | elif selector == '0': 643 | time.sleep(1) 644 | if (time.time() - self.lastCheckTs) <= 20: time.sleep(time.time() - self.lastCheckTs) 645 | 646 | def sendMsg(self, name, word, isfile = False): 647 | id = self.getUSerID(name) 648 | if id: 649 | if isfile: 650 | with open(word, 'r') as f: 651 | for line in f.readlines(): 652 | line = line.replace('\n','') 653 | self._echo('-> '+name+': '+line) 654 | if self.webwxsendmsg(line, id): 655 | print ' [成功]' 656 | else: 657 | print ' [失败]' 658 | time.sleep(1) 659 | else: 660 | if self.webwxsendmsg(word, id): 661 | print '[*] 消息发送成功' 662 | logging.debug('[*] 消息发送成功') 663 | else: 664 | print '[*] 消息发送失败' 665 | logging.debug('[*] 消息发送失败') 666 | else: 667 | print '[*] 此用户不存在' 668 | logging.debug('[*] 此用户不存在') 669 | 670 | def sendMsgToAll(self, word): 671 | for contact in self.ContactList: 672 | name = contact['RemarkName'] if contact['RemarkName'] else contact['NickName'] 673 | id = contact['UserName'] 674 | self._echo('-> '+name+': '+word) 675 | if self.webwxsendmsg(word, id): 676 | print ' [成功]' 677 | else: 678 | print ' [失败]' 679 | time.sleep(1) 680 | 681 | @catchKeyboardInterrupt 682 | def start(self): 683 | self._echo('[*] 微信网页版 ... 开动'); print; logging.debug('[*] 微信网页版 ... 开动') 684 | while True: 685 | self._run('[*] 正在获取 uuid ... ', self.getUUID) 686 | self._echo('[*] 正在获取二维码 ... 成功'); print; logging.debug('[*] 微信网页版 ... 开动'); self.genQRCode() 687 | print '[*] 请使用微信扫描二维码以登录 ... ' 688 | if not self.waitForLogin(): 689 | continue 690 | print '[*] 请在手机上点击确认以登录 ... ' 691 | if not self.waitForLogin(0): 692 | continue 693 | break 694 | 695 | self._run('[*] 正在登录 ... ', self.login) 696 | self._run('[*] 微信初始化 ... ', self.webwxinit) 697 | self._run('[*] 开启状态通知 ... ', self.webwxstatusnotify) 698 | self._run('[*] 获取联系人 ... ', self.webwxgetcontact) 699 | self._echo('[*] 应有 %s 个联系人,读取到联系人 %d 个' % (self.MemberCount, len(self.MemberList))); print 700 | self._echo('[*] 共有 %d 个群 | %d 个直接联系人 | %d 个特殊账号 | %d 公众号或服务号' % (len(self.GroupList), len(self.ContactList), len(self.SpecialUsersList), len(self.PublicUsersList) )); print 701 | self._run('[*] 获取群 ... ', self.webwxbatchgetcontact) 702 | logging.debug('[*] 微信网页版 ... 开动') 703 | if self.DEBUG: print self 704 | logging.debug(self) 705 | 706 | if self.interactive and raw_input('[*] 是否开启自动回复模式(y/n): ') == 'y': 707 | self.autoReplyMode = True 708 | print '[*] 自动回复模式 ... 开启' 709 | logging.debug('[*] 自动回复模式 ... 开启') 710 | else: 711 | print '[*] 自动回复模式 ... 关闭' 712 | logging.debug('[*] 自动回复模式 ... 关闭') 713 | 714 | listenProcess = multiprocessing.Process(target=self.listenMsgMode) 715 | listenProcess.start() 716 | 717 | while True: 718 | text = raw_input('') 719 | if text == 'quit': 720 | listenProcess.terminate() 721 | print('[*] 退出微信') 722 | logging.debug('[*] 退出微信') 723 | exit() 724 | elif text[:2] == '->': 725 | [name, word] = text[2:].split(':') 726 | if name == 'all': self.sendMsgToAll(word) 727 | else: self.sendMsg(name, word) 728 | elif text[:3] == 'm->': 729 | [name, file] = text[3:].split(':') 730 | self.sendMsg(name, file, True) 731 | elif text[:3] == 'f->': 732 | print '发送文件' 733 | logging.debug('发送文件') 734 | elif text[:3] == 'i->': 735 | print '发送图片' 736 | logging.debug('发送图片') 737 | 738 | def _safe_open(self, path): 739 | if self.autoOpen: 740 | if platform.system() == "Linux": 741 | os.system("xdg-open %s &" % path) 742 | else: 743 | os.system('open %s &' % path) 744 | 745 | def _run(self, str, func, *args): 746 | self._echo(str) 747 | if func(*args): print '成功'; logging.debug('%s... 成功' % (str)) 748 | else: print('失败\n[*] 退出程序'); logging.debug('%s... 失败' % (str)); logging.debug('[*] 退出程序'); exit() 749 | 750 | def _echo(self, str): 751 | sys.stdout.write(str) 752 | sys.stdout.flush() 753 | 754 | def _printQR(self, mat): 755 | for i in mat: 756 | BLACK = '\033[40m \033[0m' 757 | WHITE = '\033[47m \033[0m' 758 | print ''.join([BLACK if j else WHITE for j in i]) 759 | 760 | def _str2qr(self, str): 761 | qr = qrcode.QRCode() 762 | qr.border = 1 763 | qr.add_data(str) 764 | mat = qr.get_matrix() 765 | self._printQR(mat) # qr.print_tty() or qr.print_ascii() 766 | 767 | def _transcoding(self, data): 768 | if not data: return data 769 | result = None 770 | if type(data) == unicode: 771 | result = data 772 | elif type(data) == str: 773 | result = data.decode('utf-8') 774 | return result 775 | 776 | def _get(self, url, api=None): 777 | request = urllib2.Request(url = url) 778 | request.add_header('Referer', 'https://wx.qq.com/') 779 | if api == 'webwxgetvoice': request.add_header('Range', 'bytes=0-') 780 | if api == 'webwxgetvideo': request.add_header('Range', 'bytes=0-') 781 | response = urllib2.urlopen(request) 782 | data = response.read() 783 | logging.debug(url) 784 | return data 785 | 786 | def _post(self, url, params, jsonfmt = True): 787 | if jsonfmt: 788 | request = urllib2.Request(url = url, data = json.dumps(params)) 789 | request.add_header('ContentType', 'application/json; charset=UTF-8') 790 | else: 791 | request = urllib2.Request(url = url, data = urllib.urlencode(params)) 792 | response = urllib2.urlopen(request) 793 | data = response.read() 794 | if jsonfmt: return json.loads(data, object_hook=_decode_dict) 795 | return data 796 | 797 | def _xiaodoubi(self, word): 798 | url = 'http://www.xiaodoubi.com/bot/chat.php' 799 | try: 800 | r = requests.post(url, data = {'chat': word}) 801 | return r.content 802 | except: 803 | return "让我一个人静静 T_T..." 804 | 805 | def _simsimi(self, word): 806 | key = '' 807 | url = 'http://sandbox.api.simsimi.com/request.p?key=%s&lc=ch&ft=0.0&text=%s' % (key, word) 808 | r = requests.get(url) 809 | ans = r.json() 810 | if ans['result'] == '100': return ans['response'] 811 | else: return '你在说什么,风太大听不清列' 812 | 813 | def _searchContent(self, key, content, fmat = 'attr'): 814 | if fmat == 'attr': 815 | pm = re.search(key+'\s?=\s?"([^"<]+)"', content) 816 | if pm: return pm.group(1) 817 | elif fmat == 'xml': 818 | pm = re.search('<{0}>([^<]+)'.format(key),content) 819 | if not pm: pm = re.search('<{0}><\!\[CDATA\[(.*?)\]\]>'.format(key),content) 820 | if pm: return pm.group(1) 821 | return '未知' 822 | 823 | class UnicodeStreamFilter: 824 | def __init__(self, target): 825 | self.target = target 826 | self.encoding = 'utf-8' 827 | self.errors = 'replace' 828 | self.encode_to = self.target.encoding 829 | 830 | def write(self, s): 831 | if type(s) == str: 832 | s = s.decode('utf-8') 833 | s = s.encode(self.encode_to, self.errors).decode(self.encode_to) 834 | self.target.write(s) 835 | 836 | def flush(self): 837 | self.target.flush() 838 | 839 | # if sys.stdout.encoding == 'cp936': 840 | # sys.stdout = UnicodeStreamFilter(sys.stdout) 841 | 842 | 843 | # if __name__ == '__main__': 844 | 845 | # logger = logging.getLogger(__name__) 846 | # import coloredlogs 847 | # coloredlogs.install(level='DEBUG') 848 | 849 | # webwx = WebWeixin() 850 | # webwx.start() 851 | --------------------------------------------------------------------------------