diff --git a/itchat/__init__.py b/itchat/__init__.py index 256fc721..be1e467b 100644 --- a/itchat/__init__.py +++ b/itchat/__init__.py @@ -7,8 +7,8 @@ instanceList = [] -def new_instance(): - newInstance = Core() +def new_instance(proxies=None, userAgent=None): + newInstance = Core(proxies=proxies, userAgent=userAgent) instanceList.append(newInstance) return newInstance diff --git a/itchat/components/__init__.py b/itchat/components/__init__.py index f088c173..88886396 100644 --- a/itchat/components/__init__.py +++ b/itchat/components/__init__.py @@ -3,8 +3,10 @@ from .login import load_login from .messages import load_messages from .register import load_register +from .request import load_request def load_components(core): + load_request(core) load_contact(core) load_hotreload(core) load_login(core) diff --git a/itchat/components/contact.py b/itchat/components/contact.py index 3ae3c02a..2d7e5d40 100644 --- a/itchat/components/contact.py +++ b/itchat/components/contact.py @@ -31,15 +31,14 @@ def update_chatroom(self, userName, detailedMember=False): url = '%s/webwxbatchgetcontact?type=ex&r=%s' % ( self.loginInfo['url'], int(time.time())) headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } + 'ContentType': 'application/json; charset=UTF-8', } data = { 'BaseRequest': self.loginInfo['BaseRequest'], 'Count': len(userName), 'List': [{ 'UserName': u, 'ChatRoomId': '', } for u in userName], } - chatroomList = json.loads(self.s.post(url, data=json.dumps(data), headers=headers + chatroomList = json.loads(self.post_raw(url=url, data=json.dumps(data), headers=headers ).content.decode('utf8', 'replace')).get('ContactList') if not chatroomList: return ReturnValue({'BaseResponse': { @@ -51,8 +50,7 @@ def get_detailed_member_info(encryChatroomId, memberList): url = '%s/webwxbatchgetcontact?type=ex&r=%s' % ( self.loginInfo['url'], int(time.time())) headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT, } + 'ContentType': 'application/json; charset=UTF-8', } data = { 'BaseRequest': self.loginInfo['BaseRequest'], 'Count': len(memberList), @@ -60,7 +58,7 @@ def get_detailed_member_info(encryChatroomId, memberList): 'UserName': member['UserName'], 'EncryChatRoomId': encryChatroomId} \ for member in memberList], } - return json.loads(self.s.post(url, data=json.dumps(data), headers=headers + return json.loads(self.post_raw(url=url, data=json.dumps(data), headers=headers ).content.decode('utf8', 'replace'))['ContactList'] MAX_GET_NUMBER = 50 for chatroom in chatroomList: @@ -81,15 +79,14 @@ def update_friend(self, userName): url = '%s/webwxbatchgetcontact?type=ex&r=%s' % ( self.loginInfo['url'], int(time.time())) headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } + 'ContentType': 'application/json; charset=UTF-8', } data = { 'BaseRequest': self.loginInfo['BaseRequest'], 'Count': len(userName), 'List': [{ 'UserName': u, 'EncryChatRoomId': '', } for u in userName], } - friendList = json.loads(self.s.post(url, data=json.dumps(data), headers=headers + friendList = json.loads(self.post_raw(url=url, data=json.dumps(data), headers=headers ).content.decode('utf8', 'replace')).get('ContactList') update_local_friends(self, friendList) @@ -267,11 +264,8 @@ def get_contact(self, update=False): def _get_contact(seq=0): url = '%s/webwxgetcontact?r=%s&seq=%s&skey=%s' % (self.loginInfo['url'], int(time.time()), seq, self.loginInfo['skey']) - headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT, } try: - r = self.s.get(url, headers=headers) + r = self.get_raw(url=url) except: logger.info('Failed to fetch contact, that may because of the amount of your chatrooms') for chatroom in self.get_chatrooms(): @@ -330,9 +324,7 @@ def set_alias(self, userName, alias): 'CmdId' : 2, 'RemarkName' : alias, 'BaseRequest' : self.loginInfo['BaseRequest'], } - headers = { 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, json.dumps(data, ensure_ascii=False).encode('utf8'), - headers=headers) + r = self.post_raw(url=url, data=json.dumps(data, ensure_ascii=False).encode('utf8')) r = ReturnValue(rawResponse=r) if r: oldFriendInfo['RemarkName'] = alias @@ -346,8 +338,7 @@ def set_pinned(self, userName, isPinned=True): 'CmdId' : 3, 'OP' : int(isPinned), 'BaseRequest' : self.loginInfo['BaseRequest'], } - headers = { 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, json=data, headers=headers) + r = self.post_raw(url=url, json=data) return ReturnValue(rawResponse=r) def add_friend(self, userName, status=2, verifyContent='', autoUpdate=True): @@ -369,9 +360,8 @@ def add_friend(self, userName, status=2, verifyContent='', autoUpdate=True): 'SceneList': [33], 'skey': self.loginInfo['skey'], } headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, headers=headers, + 'ContentType': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8', 'replace')) if autoUpdate: self.update_friend(userName) @@ -406,8 +396,7 @@ def get_head_img(self, userName=None, chatroomUserName=None, picDir=None): if 'EncryChatRoomId' in chatroom: params['chatroomid'] = chatroom['EncryChatRoomId'] params['chatroomid'] = params.get('chatroomid') or chatroom['UserName'] - headers = { 'User-Agent' : config.USER_AGENT } - r = self.s.get(url, params=params, stream=True, headers=headers) + r = self.get_raw(url=url, params=params, stream=True) tempStorage = io.BytesIO() for block in r.iter_content(1024): tempStorage.write(block) @@ -430,9 +419,8 @@ def create_chatroom(self, memberList, topic=''): 'MemberList': [{'UserName': member['UserName']} for member in memberList], 'Topic': topic, } headers = { - 'content-type': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, headers=headers, + 'content-type': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8', 'ignore')) return ReturnValue(rawResponse=r) @@ -444,9 +432,8 @@ def set_chatroom_name(self, chatroomUserName, name): 'ChatRoomName': chatroomUserName, 'NewTopic': name, } headers = { - 'content-type': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, headers=headers, + 'content-type': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8', 'ignore')) return ReturnValue(rawResponse=r) @@ -458,9 +445,8 @@ def delete_member_from_chatroom(self, chatroomUserName, memberList): 'ChatRoomName': chatroomUserName, 'DelMemberList': ','.join([member['UserName'] for member in memberList]), } headers = { - 'content-type': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT} - r = self.s.post(url, data=json.dumps(data),headers=headers) + 'content-type': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, data=json.dumps(data),headers=headers) return ReturnValue(rawResponse=r) def add_member_into_chatroom(self, chatroomUserName, memberList, @@ -486,7 +472,6 @@ def add_member_into_chatroom(self, chatroomUserName, memberList, 'ChatRoomName' : chatroomUserName, memberKeyName : ','.join([member['UserName'] for member in memberList]), } headers = { - 'content-type': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT} - r = self.s.post(url, data=json.dumps(params),headers=headers) + 'content-type': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, data=json.dumps(params),headers=headers) return ReturnValue(rawResponse=r) diff --git a/itchat/components/login.py b/itchat/components/login.py index c1fb0391..792cb678 100644 --- a/itchat/components/login.py +++ b/itchat/components/login.py @@ -98,8 +98,7 @@ def get_QRuuid(self): params = { 'appid' : 'wx782c26e4c19acffb', 'fun' : 'new', } - headers = { 'User-Agent' : config.USER_AGENT } - r = self.s.get(url, params=params, headers=headers) + r = self.get_raw(url=url, params=params) regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)";' data = re.search(regx, r.text) if data and data.group(1) == '200': @@ -129,8 +128,7 @@ def check_login(self, uuid=None): localTime = int(time.time()) params = 'loginicon=true&uuid=%s&tip=1&r=%s&_=%s' % ( uuid, int(-localTime / 1579), localTime) - headers = { 'User-Agent' : config.USER_AGENT } - r = self.s.get(url, params=params, headers=headers) + r = self.get_raw(url=url, params=params) regx = r'window.code=(\d+)' data = re.search(regx, r.text) if data and data.group(1) == '200': @@ -192,9 +190,8 @@ def web_init(self): 'pass_ticket': self.loginInfo['pass_ticket'], } data = { 'BaseRequest': self.loginInfo['BaseRequest'], } headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT, } - r = self.s.post(url, params=params, data=json.dumps(data), headers=headers) + 'ContentType': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, params=params, data=json.dumps(data), headers=headers) dic = json.loads(r.content.decode('utf-8', 'replace')) # deal with login info utils.emoji_formatter(dic['User'], 'NickName') @@ -234,9 +231,8 @@ def show_mobile_login(self): 'ToUserName' : self.storageClass.userName, 'ClientMsgId' : int(time.time()), } headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT, } - r = self.s.post(url, data=json.dumps(data), headers=headers) + 'ContentType': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, data=json.dumps(data), headers=headers) return ReturnValue(rawResponse=r) def start_receiving(self, exitCallback=None, getReceivingFnOnly=False): @@ -299,10 +295,9 @@ def sync_check(self): 'deviceid' : self.loginInfo['deviceid'], 'synckey' : self.loginInfo['synckey'], '_' : self.loginInfo['logintime'], } - headers = { 'User-Agent' : config.USER_AGENT } self.loginInfo['logintime'] += 1 try: - r = self.s.get(url, params=params, headers=headers, timeout=config.TIMEOUT) + r = self.get_raw(url=url, params=params, timeout=config.TIMEOUT) except requests.exceptions.ConnectionError as e: try: if not isinstance(e.args[0].args[1], BadStatusLine): @@ -331,9 +326,8 @@ def get_msg(self): 'SyncKey' : self.loginInfo['SyncKey'], 'rr' : ~int(time.time()), } headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, data=json.dumps(data), headers=headers, timeout=config.TIMEOUT) + 'ContentType': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, data=json.dumps(data), headers=headers, timeout=config.TIMEOUT) dic = json.loads(r.content.decode('utf-8', 'replace')) if dic['BaseResponse']['Ret'] != 0: return None, None self.loginInfo['SyncKey'] = dic['SyncKey'] @@ -348,8 +342,7 @@ def logout(self): 'redirect' : 1, 'type' : 1, 'skey' : self.loginInfo['skey'], } - headers = { 'User-Agent' : config.USER_AGENT } - self.s.get(url, params=params, headers=headers) + self.get_raw(url=url, params=params) self.alive = False self.isLogging = False self.s.cookies.clear() diff --git a/itchat/components/messages.py b/itchat/components/messages.py index 85c0ca2e..7d0b8328 100644 --- a/itchat/components/messages.py +++ b/itchat/components/messages.py @@ -273,8 +273,8 @@ def send_raw_msg(self, msgType, content, toUserName): 'ClientMsgId': int(time.time() * 1e4), }, 'Scene': 0, } - headers = { 'ContentType': 'application/json; charset=UTF-8', 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, headers=headers, + headers = { 'ContentType': 'application/json; charset=UTF-8' } + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8')) return ReturnValue(rawResponse=r) @@ -400,9 +400,8 @@ def send_file(self, fileDir, toUserName=None, mediaId=None, file_=None): 'ClientMsgId': int(time.time() * 1e4), }, 'Scene': 0, } headers = { - 'User-Agent': config.USER_AGENT, 'Content-Type': 'application/json;charset=UTF-8', } - r = self.s.post(url, headers=headers, + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8')) return ReturnValue(rawResponse=r) @@ -442,9 +441,8 @@ def send_image(self, fileDir=None, toUserName=None, mediaId=None, file_=None): data['Msg']['Type'] = 47 data['Msg']['EmojiFlag'] = 2 headers = { - 'User-Agent': config.USER_AGENT, 'Content-Type': 'application/json;charset=UTF-8', } - r = self.s.post(url, headers=headers, + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8')) return ReturnValue(rawResponse=r) @@ -481,9 +479,8 @@ def send_video(self, fileDir=None, toUserName=None, mediaId=None, file_=None): 'ClientMsgId' : int(time.time() * 1e4), }, 'Scene': 0, } headers = { - 'User-Agent' : config.USER_AGENT, 'Content-Type': 'application/json;charset=UTF-8', } - r = self.s.post(url, headers=headers, + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8')) return ReturnValue(rawResponse=r) @@ -521,8 +518,7 @@ def revoke(self, msgId, toUserName, localId=None): "SvrMsgId": msgId, "ToUserName": toUserName} headers = { - 'ContentType': 'application/json; charset=UTF-8', - 'User-Agent' : config.USER_AGENT } - r = self.s.post(url, headers=headers, + 'ContentType': 'application/json; charset=UTF-8', } + r = self.post_raw(url=url, headers=headers, data=json.dumps(data, ensure_ascii=False).encode('utf8')) return ReturnValue(rawResponse=r) diff --git a/itchat/components/request.py b/itchat/components/request.py new file mode 100644 index 00000000..e0eec29d --- /dev/null +++ b/itchat/components/request.py @@ -0,0 +1,11 @@ +def load_request(core): + core.post_raw = post_raw + core.get_raw = get_raw + +def post_raw(self, headers={}, **kwargs): + headers.setdefault('User-Agent', self.userAgent) + return self.s.post(headers=headers, **kwargs) + +def get_raw(self, headers={}, **kwargs): + headers.setdefault('User-Agent', self.userAgent) + return self.s.get(headers=headers, **kwargs) diff --git a/itchat/core.py b/itchat/core.py index 52d6ae4f..8402bbc8 100644 --- a/itchat/core.py +++ b/itchat/core.py @@ -2,9 +2,10 @@ from . import storage from .components import load_components +from .config import USER_AGENT class Core(object): - def __init__(self): + def __init__(self, proxies=None, userAgent=None): ''' init is the only method defined in core.py alive is value showing whether core is running - you should call logout method to change it @@ -27,6 +28,10 @@ def __init__(self): self.functionDict = {'FriendChat': {}, 'GroupChat': {}, 'MpChat': {}} self.useHotReload, self.hotReloadDir = False, 'itchat.pkl' self.receivingRetryCount = 5 + if proxies: + self.s.proxies = proxies + self.userAgent = userAgent or USER_AGENT + def login(self, enableCmdQR=False, picDir=None, qrCallback=None, loginCallback=None, exitCallback=None): ''' log in like web wechat does @@ -367,6 +372,14 @@ def send(self, msg, toUserName=None, mediaId=None): it is defined in components/messages.py ''' raise NotImplementedError() + def post_raw(self, url, data=None, json=None, headers={}): + ''' + ''' + raise NotImplementedError() + def get_raw(self, url, params=None, headers={}): + ''' + ''' + raise NotImplementedError() def revoke(self, msgId, toUserName, localId=None): ''' revoke message with its and msgId for options