diff --git a/README.md b/README.md index 75767d9b..acb2d47b 100644 --- a/README.md +++ b/README.md @@ -1,306 +1,7 @@ -# itchat +As itchat is multi-threaded, it's impossible to catch the internet errors it has produced. So I added some callbacks. -[![Gitter][gitter-picture]][gitter] ![py27][py27] ![py35][py35] [English version][english-version] +In core.functionDict, I added the key "Error" whose value is a list, where we may store error-handling functions. -itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。 +Use '@itchat.error_register(True)' to register your error-handling function. -使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。 - -当然,该api的使用远不止一个机器人,更多的功能等着你来发现,比如[这些][tutorial2]。 - -该接口与公众号接口[itchatmp][itchatmp]共享类似的操作方式,学习一次掌握两个工具。 - -如今微信已经成为了个人社交的很大一部分,希望这个项目能够帮助你扩展你的个人的微信号、方便自己的生活。 - -## 安装 - -可以通过本命令安装itchat: - -```python -pip install itchat -``` - -## 简单入门实例 - -有了itchat,如果你想要给文件传输助手发一条信息,只需要这样: - -```python -import itchat - -itchat.auto_login() - -itchat.send('Hello, filehelper', toUserName='filehelper') -``` - -如果你想要回复发给自己的文本消息,只需要这样: - -```python -import itchat - -@itchat.msg_register(itchat.content.TEXT) -def text_reply(msg): - return msg.text - -itchat.auto_login() -itchat.run() -``` - -一些进阶应用可以在下面的开源机器人的源码和进阶应用中看到,或者你也可以阅览[文档][document]。 - -## 试一试 - -这是一个基于这一项目的[开源小机器人][robot-source-code],百闻不如一见,有兴趣可以尝试一下。 - -由于好友数量实在增长过快,自动通过好友验证的功能演示暂时关闭。 - -![QRCode][robot-qr] - -## 截屏 - -![file-autoreply][robot-demo-file] ![login-page][robot-demo-login] - -## 进阶应用 - -### 特殊的字典使用方式 - -通过打印itchat的用户以及注册消息的参数,可以发现这些值都是字典。 - -但实际上itchat精心构造了相应的消息、用户、群聊、公众号类。 - -其所有的键值都可以通过这一方式访问: - -```python -@itchat.msg_register(TEXT) -def _(msg): - # equals to print(msg['FromUserName']) - print(msg.fromUserName) -``` - -属性名为键值首字母小写后的内容。 - -```python -author = itchat.search_friends(nickName='LittleCoder')[0] -author.send('greeting, littlecoder!') -``` - -### 各类型消息的注册 - -通过如下代码,微信已经可以就日常的各种信息进行获取与回复。 - -```python -import itchat, time -from itchat.content import * - -@itchat.msg_register([TEXT, MAP, CARD, NOTE, SHARING]) -def text_reply(msg): - msg.user.send('%s: %s' % (msg.type, msg.text)) - -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - msg.download(msg.fileName) - typeSymbol = { - PICTURE: 'img', - VIDEO: 'vid', }.get(msg.type, 'fil') - return '@%s@%s' % (typeSymbol, msg.fileName) - -@itchat.msg_register(FRIENDS) -def add_friend(msg): - msg.user.verify() - msg.user.send('Nice to meet you!') - -@itchat.msg_register(TEXT, isGroupChat=True) -def text_reply(msg): - if msg.isAt: - msg.user.send(u'@%s\u2005I received: %s' % ( - msg.actualNickName, msg.text)) - -itchat.auto_login(True) -itchat.run(True) -``` - -### 命令行二维码 - -通过以下命令可以在登陆的时候使用命令行显示二维码: - -```python -itchat.auto_login(enableCmdQR=True) -``` - -部分系统可能字幅宽度有出入,可以通过将enableCmdQR赋值为特定的倍数进行调整: - -```python -# 如部分的linux系统,块字符的宽度为一个字符(正常应为两字符),故赋值为2 -itchat.auto_login(enableCmdQR=2) -``` - -默认控制台背景色为暗色(黑色),若背景色为浅色(白色),可以将enableCmdQR赋值为负值: - -```python -itchat.auto_login(enableCmdQR=-1) -``` - -### 退出程序后暂存登陆状态 - -通过如下命令登陆,即使程序关闭,一定时间内重新开启也可以不用重新扫码。 - -```python -itchat.auto_login(hotReload=True) -``` - -### 用户搜索 - -使用`search_friends`方法可以搜索用户,有四种搜索方式: -1. 仅获取自己的用户信息 -2. 获取特定`UserName`的用户信息 -3. 获取备注、微信号、昵称中的任何一项等于`name`键值的用户 -4. 获取备注、微信号、昵称分别等于相应键值的用户 - -其中三、四项可以一同使用,下面是示例程序: - -```python -# 获取自己的用户信息,返回自己的属性字典 -itchat.search_friends() -# 获取特定UserName的用户信息 -itchat.search_friends(userName='@abcdefg1234567') -# 获取任何一项等于name键值的用户 -itchat.search_friends(name='littlecodersh') -# 获取分别对应相应键值的用户 -itchat.search_friends(wechatAccount='littlecodersh') -# 三、四项功能可以一同使用 -itchat.search_friends(name='LittleCoder机器人', wechatAccount='littlecodersh') -``` - -关于公众号、群聊的获取与搜索在文档中有更加详细的介绍。 - -### 附件的下载与发送 - -itchat的附件下载方法存储在msg的Text键中。 - -发送的文件的文件名(图片给出的默认文件名)都存储在msg的FileName键中。 - -下载方法接受一个可用的位置参数(包括文件名),并将文件相应的存储。 - -```python -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - msg.download(msg.fileName) - itchat.send('@%s@%s' % ( - 'img' if msg['Type'] == 'Picture' else 'fil', msg['FileName']), - msg['FromUserName']) - return '%s received' % msg['Type'] -``` - -如果你不需要下载到本地,仅想要读取二进制串进行进一步处理可以不传入参数,方法将会返回图片的二进制串。 - -```python -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - with open(msg.fileName, 'wb') as f: - f.write(msg.download()) -``` - -### 用户多开 - -使用如下命令可以完成多开的操作: - -```python -import itchat - -newInstance = itchat.new_instance() -newInstance.auto_login(hotReload=True, statusStorageDir='newInstance.pkl') - -@newInstance.msg_register(itchat.content.TEXT) -def reply(msg): - return msg.text - -newInstance.run() -``` - -### 退出及登陆完成后调用特定方法 - -登陆完成后的方法需要赋值在`loginCallback`中。 - -而退出后的方法需要赋值在`exitCallback`中。 - -```python -import time - -import itchat - -def lc(): - print('finish login') -def ec(): - print('exit') - -itchat.auto_login(loginCallback=lc, exitCallback=ec) -time.sleep(3) -itchat.logout() -``` - -若不设置loginCallback的值,则将会自动删除二维码图片并清空命令行显示。 - -## 常见问题与解答 - -Q: 如何通过这个包将自己的微信号变为控制器? - -A: 有两种方式:发送、接受自己UserName的消息;发送接收文件传输助手(filehelper)的消息 - -Q: 为什么我发送信息的时候部分信息没有成功发出来? - -A: 有些账号是天生无法给自己的账号发送信息的,建议使用`filehelper`代替。 - -## 作者 - -[LittleCoder][littlecodersh]: 构架及维护Python2 Python3版本。 - -[tempdban][tempdban]: 协议、构架及日常维护。 - -[Chyroc][Chyroc]: 完成第一版本的Python3构架。 - -## 类似项目 - -[youfou/wxpy][youfou-wxpy]: 优秀的api包装和配套插件,微信机器人/优雅的微信个人号API - -[liuwons/wxBot][liuwons-wxBot]: 类似的基于Python的微信机器人 - -[zixia/wechaty][zixia-wechaty]: 基于Javascript(ES6)的微信个人账号机器人NodeJS框架/库 - -[sjdy521/Mojo-Weixin][Mojo-Weixin]: 使用Perl语言编写的微信客户端框架,可通过插件提供基于HTTP协议的api接口供其他语言调用 - -[HanSon/vbot][HanSon-vbot]: 基于PHP7的微信个人号机器人,通过实现匿名函数可以方便地实现各种自定义的功能 - -[yaphone/itchat4j][yaphone-itchat4j]: 用Java扩展个人微信号的能力 - -[kanjielu/jeeves][kanjielu-jeeves]: 使用springboot开发的微信机器人 - -## 问题和建议 - -如果有什么问题或者建议都可以在这个[Issue][issue#1]和我讨论 - -或者也可以在gitter上交流:[![Gitter][gitter-picture]][gitter] - -当然也可以加入我们新建的QQ群讨论:549762872, 205872856 - -[gitter-picture]: https://badges.gitter.im/littlecodersh/ItChat.svg -[gitter]: https://gitter.im/littlecodersh/ItChat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge -[py27]: https://img.shields.io/badge/python-2.7-ff69b4.svg -[py35]: https://img.shields.io/badge/python-3.5-red.svg -[english-version]: https://github.com/littlecodersh/ItChat/blob/master/README_EN.md -[itchatmp]: https://github.com/littlecodersh/itchatmp -[document]: https://itchat.readthedocs.org/zh/latest/ -[tutorial2]: http://python.jobbole.com/86532/ -[robot-source-code]: https://gist.github.com/littlecodersh/ec8ddab12364323c97d4e36459174f0d -[robot-qr]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FQRCode2.jpg?imageView/2/w/400/ -[robot-demo-file]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E5%BE%AE%E4%BF%A1%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%9B%BE%E7%89%87.png?imageView/2/w/300/ -[robot-demo-login]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E7%99%BB%E5%BD%95%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.jpg?imageView/2/w/450/ -[littlecodersh]: https://github.com/littlecodersh -[tempdban]: https://github.com/tempdban -[Chyroc]: https://github.com/Chyroc -[youfou-wxpy]: https://github.com/youfou/wxpy -[liuwons-wxBot]: https://github.com/liuwons/wxBot -[zixia-wechaty]: https://github.com/zixia/wechaty -[Mojo-Weixin]: https://github.com/sjdy521/Mojo-Weixin -[HanSon-vbot]: https://github.com/hanson/vbot -[yaphone-itchat4j]: https://github.com/yaphone/itchat4j -[kanjielu-jeeves]: https://github.com/kanjielu/jeeves -[issue#1]: https://github.com/littlecodersh/ItChat/issues/1 +这玩意多线程导致我没法捕捉错误,所以搞了个回调。用@itchat.error_register(True)来注册你的错误处理函数。 diff --git a/README_EN.md b/README_EN.md deleted file mode 100644 index ab6df03d..00000000 --- a/README_EN.md +++ /dev/null @@ -1,293 +0,0 @@ -# itchat - -[![Gitter][gitter-picture]][gitter] ![py27][py27] ![py35][py35] [Chinese version][chinese-version] - -itchat is an open source api for WeChat, a commonly-used Chinese social networking app. - -Accessing your personal wechat account through itchat in python has never been easier. - -A wechat robot can handle all the basic messages with only less than 30 lines of codes. - -And it's similiar to itchatmp (api for wechat massive platform), learn once and get two tools. - -Now Wechat is an important part of personal life, hopefully this repo can help you extend your personal wechat account's functionality and better user's experience with wechat. - -## Installation - -itchat can be installed with this little one-line command: - -```python -pip install itchat -``` - -## Simple uses - -With itchat, if you want to send a message to filehelper, this is how: - -```python -import itchat - -itchat.auto_login() - -itchat.send('Hello, filehelper', toUserName='filehelper') -``` - -And you only need to write this to reply personal text messages. - -```python -import itchat - -@itchat.msg_register(itchat.content.TEXT) -def text_reply(msg): - return msg.text - -itchat.auto_login() -itchat.run() -``` - -For more advanced uses you may continue on reading or browse the [document][document]. - -## Have a try - -This QRCode is a wechat account based on the framework of [demo code][robot-source-code]. Seeing is believing, so have a try:) - -![QRCode][robot-qr] - -## Screenshots - -![file-autoreply][robot-demo-file] ![login-page][robot-demo-login] - -## Advanced uses - -### Special usage of message dictionary - -You may find out that all the users and messages of itchat are dictionaries by printing them out onto the screen. - -But actually they are useful classes itchat created. - -They have useful keys and useful interfaces, like: - -```python -@itchat.msg_register(TEXT) -def _(msg): - # equals to print(msg['FromUserName']) - print(msg.fromUserName) -``` - -And like: - -```python -author = itchat.search_friends(nickName='LittleCoder')[0] -author.send('greeting, littlecoder!') -``` - -### Message register of various types - -The following is a demo of how itchat is configured to fetch and reply daily information. - -```python -import itchat, time -from itchat.content import * - -@itchat.msg_register([TEXT, MAP, CARD, NOTE, SHARING]) -def text_reply(msg): - msg.user.send('%s: %s' % (msg.type, msg.text)) - -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - msg.download(msg.fileName) - typeSymbol = { - PICTURE: 'img', - VIDEO: 'vid', }.get(msg.type, 'fil') - return '@%s@%s' % (typeSymbol, msg.fileName) - -@itchat.msg_register(FRIENDS) -def add_friend(msg): - msg.user.verify() - msg.user.send('Nice to meet you!') - -@itchat.msg_register(TEXT, isGroupChat=True) -def text_reply(msg): - if msg.isAt: - msg.user.send(u'@%s\u2005I received: %s' % ( - msg.actualNickName, msg.text)) - -itchat.auto_login(True) -itchat.run(True) -``` - -### Command line QR Code - -You can access the QR Code in command line through using this command: - -```python -itchat.auto_login(enableCmdQR=True) -``` - -Because of width of some character differs from systems, you may adjust the enableCmdQR to fix the problem. - -```python -# for some linux system, width of block character is one instead of two, so enableCmdQR should be 2 -itchat.auto_login(enableCmdQR=2) -``` - -Default background color of command line is dark (black), if it's not, you may set enableCmdQR to be negative: - -```python -itchat.auto_login(enableCmdQR=-1) -``` - -### Hot reload - -By using the following command, you may reload the program without re-scan QRCode in some time. - -```python -itchat.auto_login(hotReload=True) -``` - -### User search - -By using `search_friends`, you have four ways to search a user: -1. Get your own user information -2. Get user information through `UserName` -3. Get user information whose remark name or wechat account or nickname matches name key of the function -4. Get user information whose remark name, wechat account and nickname match what are given to the function - -Way 3, 4 can be used together, the following is the demo program: - -```python -# get your own user information -itchat.search_friends() -# get user information of specific username -itchat.search_friends(userName='@abcdefg1234567') -# get user information of function 3 -itchat.search_friends(name='littlecodersh') -# get user information of function 4 -itchat.search_friends(wechatAccount='littlecodersh') -# combination of way 3, 4 -itchat.search_friends(name='LittleCoder机器人', wechatAccount='littlecodersh') -``` - -There is detailed information about searching and getting of massive platforms and chatrooms in document. - -### Download and send attachments - -The attachment download function of itchat is in Text key of msg - -Name of the file (default name of picture) is in FileName key of msg - -Download function accept one location value (include the file name) and store attachment accordingly. - -```python -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - msg.download(msg.fileName) - itchat.send('@%s@%s' % ( - 'img' if msg['Type'] == 'Picture' else 'fil', msg['FileName']), - msg['FromUserName']) - return '%s received' % msg['Type'] -``` - -If you don't want a local copy of the picture, you may pass nothing to the function to get a binary string. - -```python -@itchat.msg_register([PICTURE, RECORDING, ATTACHMENT, VIDEO]) -def download_files(msg): - with open(msg.fileName, 'wb') as f: - f.write(msg.download()) -``` - -### Multi instance - -You may use the following commands to open multi instance. - -```python -import itchat - -newInstance = itchat.new_instance() -newInstance.auto_login(hotReload=True, statusStorageDir='newInstance.pkl') - -@newInstance.msg_register(itchat.content.TEXT) -def reply(msg): - return msg['Text'] - -newInstance.run() -``` - -### Set callback after login and logout - -Callback of login and logout are set through `loginCallback` and `exitCallback`. - -```python -import time - -import itchat - -def lc(): - print('finish login') -def ec(): - print('exit') - -itchat.auto_login(loginCallback=lc, exitCallback=ec) -time.sleep(3) -itchat.logout() -``` - -If loginCallback is not set, qr picture will be deleted and cmd will be cleared. - -If you exit through phone, exitCallback will also be called. - -## FAQ - -Q: How to use this package to use my wechat as an monitor? - -A: There are two ways: communicate with your own account or with filehelper. - -Q: Why sometimes I can't send messages? - -A: Some account simply can't send messages to yourself, so use `filehelper` instead. - -## Author - -[LittleCoder][littlecodersh]: Structure and py2 py3 version - -[tempdban][tempdban]: Structure and daily maintainance - -[Chyroc][Chyroc]: first py3 version - -## See also - -[liuwons/wxBot][liuwons-wxBot]: A wechat robot similiar to the robot branch - -[zixia/wechaty][zixia-wechaty]: Wechat for bot in Javascript(ES6), Personal Account Robot Framework/Library - -[sjdy521/Mojo-Weixin][Mojo-Weixin]: Wechat web api in Perl, available with HTTP requests - -[yaphone/itchat4j][yaphone-itchat4j]: Extend your wechat with java - -## Comments - -If you have any problems or suggestions, feel free to put it up in this [Issue][issue#1]. - -Or you may also use [![Gitter][gitter-picture]][gitter] - -[gitter-picture]: https://badges.gitter.im/littlecodersh/ItChat.svg -[gitter]: https://gitter.im/littlecodersh/ItChat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge -[py27]: https://img.shields.io/badge/python-2.7-ff69b4.svg -[py35]: https://img.shields.io/badge/python-3.5-red.svg -[chinese-version]: https://github.com/littlecodersh/ItChat/blob/master/README.md -[document]: https://itchat.readthedocs.org/zh/latest/ -[robot-source-code]: https://gist.github.com/littlecodersh/ec8ddab12364323c97d4e36459174f0d -[robot-qr]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FQRCode2.jpg?imageView/2/w/400/ -[robot-demo-file]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E5%BE%AE%E4%BF%A1%E8%8E%B7%E5%8F%96%E6%96%87%E4%BB%B6%E5%9B%BE%E7%89%87.png?imageView/2/w/300/ -[robot-demo-login]: http://7xrip4.com1.z0.glb.clouddn.com/ItChat%2FScreenshots%2F%E7%99%BB%E5%BD%95%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.jpg?imageView/2/w/450/ -[fields.py-2]: https://gist.github.com/littlecodersh/9a0c5466f442d67d910f877744011705 -[fields.py-3]: https://gist.github.com/littlecodersh/e93532d5e7ddf0ec56c336499165c4dc -[littlecodersh]: https://github.com/littlecodersh -[tempdban]: https://github.com/tempdban -[Chyroc]: https://github.com/Chyroc -[liuwons-wxBot]: https://github.com/liuwons/wxBot -[zixia-wechaty]: https://github.com/zixia/wechaty -[Mojo-Weixin]: https://github.com/sjdy521/Mojo-Weixin -[yaphone-itchat4j]: https://github.com/yaphone/itchat4j -[issue#1]: https://github.com/littlecodersh/ItChat/issues/1 diff --git a/itchat/__init__.py b/itchat/__init__.py index 256fc721..a7cb466b 100644 --- a/itchat/__init__.py +++ b/itchat/__init__.py @@ -59,9 +59,11 @@ def new_instance(): auto_login = originInstance.auto_login configured_reply = originInstance.configured_reply msg_register = originInstance.msg_register +error_register = originInstance.error_register run = originInstance.run # other functions search_friends = originInstance.search_friends search_chatrooms = originInstance.search_chatrooms search_mps = originInstance.search_mps set_logging = set_logging + diff --git a/itchat/components/__init__.py b/itchat/components/__init__.py index f088c173..cb17cd84 100644 --- a/itchat/components/__init__.py +++ b/itchat/components/__init__.py @@ -3,6 +3,7 @@ from .login import load_login from .messages import load_messages from .register import load_register +from .error import load_error def load_components(core): load_contact(core) @@ -10,3 +11,4 @@ def load_components(core): load_login(core) load_messages(core) load_register(core) + load_error(core) diff --git a/itchat/components/error.py b/itchat/components/error.py new file mode 100644 index 00000000..9053e7c0 --- /dev/null +++ b/itchat/components/error.py @@ -0,0 +1,9 @@ +def load_error(core): + core.error_register=error_register + +def error_register(self,accept_all_errors): + if not accept_all_errors: + raise NotImplementedError() + def register(fn): + self.functionDict['Error'].append(fn) + return register diff --git a/itchat/components/login.py b/itchat/components/login.py index c1fb0391..bc3a0371 100644 --- a/itchat/components/login.py +++ b/itchat/components/login.py @@ -1,7 +1,7 @@ -import os, time, re, io +import os, sys, time, re, io import threading import json, xml.dom.minidom -import random +import copy, pickle, random import traceback, logging try: from httplib import BadStatusLine @@ -18,6 +18,7 @@ from .messages import produce_msg logger = logging.getLogger('itchat') +requests.adapters.DEFAULT_RETRIES = 10 def load_login(core): core.login = login @@ -115,11 +116,11 @@ def get_QR(self, uuid=None, enableCmdQR=False, picDir=None, qrCallback=None): if hasattr(qrCallback, '__call__'): qrCallback(uuid=uuid, status='0', qrcode=qrStorage.getvalue()) else: - with open(picDir, 'wb') as f: - f.write(qrStorage.getvalue()) if enableCmdQR: utils.print_cmd_qr(qrCode.text(1), enableCmdQR=enableCmdQR) else: + with open(picDir, 'wb') as f: + f.write(qrStorage.getvalue()) utils.print_qr(picDir) return qrStorage @@ -168,7 +169,6 @@ def process_login_info(core, loginContent): else: core.loginInfo['fileUrl'] = core.loginInfo['syncUrl'] = core.loginInfo['url'] core.loginInfo['deviceid'] = 'e' + repr(random.random())[2:17] - core.loginInfo['logintime'] = int(time.time() * 1e3) core.loginInfo['BaseRequest'] = {} for node in xml.dom.minidom.parseString(r.text).documentElement.childNodes: if node.nodeName == 'skey': @@ -207,19 +207,19 @@ def web_init(self): self.storageClass.userName = dic['User']['UserName'] self.storageClass.nickName = dic['User']['NickName'] # deal with contact list returned when init - contactList = dic.get('ContactList', []) - chatroomList, otherList = [], [] - for m in contactList: - if m['Sex'] != 0: - otherList.append(m) - elif '@@' in m['UserName']: + contactList = dic.get('ContactList', []) + chatroomList, otherList = [], [] + for m in contactList: + if m['Sex'] != 0: + otherList.append(m) + elif '@@' in m['UserName']: m['MemberList'] = [] # don't let dirty info pollute the list - chatroomList.append(m) - elif '@' in m['UserName']: - # mp will be dealt in update_local_friends as well - otherList.append(m) + chatroomList.append(m) + elif '@' in m['UserName']: + # mp will be dealt in update_local_friends as well + otherList.append(m) if chatroomList: - update_local_chatrooms(self, chatroomList) + update_local_chatrooms(self, chatroomList) if otherList: update_local_friends(self, otherList) return dic @@ -270,11 +270,14 @@ def maintain_loop(): retryCount = 0 except requests.exceptions.ReadTimeout: pass - except: + except : retryCount += 1 - logger.error(traceback.format_exc()) + err=traceback.format_exc() + for f in self.functionDict['Error']: + f('Unknown Internet Error',err) if self.receivingRetryCount < retryCount: - self.alive = False + #self.alive = False + self.functionDict['Error']('Retry times reached limit','still retrying') else: time.sleep(1) self.logout() @@ -298,22 +301,25 @@ def sync_check(self): 'uin' : self.loginInfo['wxuin'], 'deviceid' : self.loginInfo['deviceid'], 'synckey' : self.loginInfo['synckey'], - '_' : self.loginInfo['logintime'], } + '_' : int(time.time() * 1000),} headers = { 'User-Agent' : config.USER_AGENT } - self.loginInfo['logintime'] += 1 try: r = self.s.get(url, params=params, headers=headers, timeout=config.TIMEOUT) except requests.exceptions.ConnectionError as e: try: if not isinstance(e.args[0].args[1], BadStatusLine): - raise + for f in self.functionDict['Error']: + f(e) + return # will return a package with status '0 -' # and value like: # 6f:00:8a:9c:09:74:e4:d8:e0:14:bf:96:3a:56:a0:64:1b:a4:25:5d:12:f4:31:a5:30:f1:c6:48:5f:c3:75:6a:99:93 # seems like status of typing, but before I make further achievement code will remain like this return '2' except: - raise + for f in self.functionDict['Error']: + f(None) + return r.raise_for_status() regx = r'window.synccheck={retcode:"(\d+)",selector:"(\d+)"}' pm = re.search(regx, r.text) @@ -336,7 +342,7 @@ def get_msg(self): r = self.s.post(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'] + self.loginInfo['SyncKey'] = dic['SyncCheckKey'] self.loginInfo['synckey'] = '|'.join(['%s_%s' % (item['Key'], item['Val']) for item in dic['SyncCheckKey']['List']]) return dic['AddMsgList'], dic['ModContactList'] diff --git a/itchat/components/register.py b/itchat/components/register.py index 079d3191..b497fb72 100644 --- a/itchat/components/register.py +++ b/itchat/components/register.py @@ -19,9 +19,10 @@ def load_register(core): def auto_login(self, hotReload=False, statusStorageDir='itchat.pkl', enableCmdQR=False, picDir=None, qrCallback=None, loginCallback=None, exitCallback=None): - if not test_connect(): - logger.info("You can't get access to internet or wechat domain, so exit.") - sys.exit() + x=test_connect() + if x!=True: + for f in self.functionDict['Error']: + f("You can't get access to wechat. ",x) self.useHotReload = hotReload self.hotReloadDir = statusStorageDir if hotReload: diff --git a/itchat/core.py b/itchat/core.py index 52d6ae4f..837ad25b 100644 --- a/itchat/core.py +++ b/itchat/core.py @@ -1,6 +1,8 @@ +import logging + import requests -from . import storage +from . import config, storage, utils, log from .components import load_components class Core(object): @@ -24,7 +26,7 @@ def __init__(self): self.loginInfo = {} self.s = requests.Session() self.uuid = None - self.functionDict = {'FriendChat': {}, 'GroupChat': {}, 'MpChat': {}} + self.functionDict = {'FriendChat': {}, 'GroupChat': {}, 'MpChat': {},'Error':[]} self.useHotReload, self.hotReloadDir = False, 'itchat.pkl' self.receivingRetryCount = 5 def login(self, enableCmdQR=False, picDir=None, qrCallback=None, @@ -125,7 +127,7 @@ def start_receiving(self, exitCallback=None, getReceivingFnOnly=False): def get_msg(self): ''' fetch messages for fetching - - method blocks for sometime until + - method blocks for sometime util - new messages are to be received - or anytime they like - synckey is updated with returned synccheckkey @@ -455,5 +457,5 @@ def search_chatrooms(self, name=None, userName=None): return self.storageClass.search_chatrooms(name, userName) def search_mps(self, name=None, userName=None): return self.storageClass.search_mps(name, userName) - +requests.adapters.DEFAULT_RETRIES = 10 load_components(Core) diff --git a/itchat/readme.md b/itchat/readme.md new file mode 100644 index 00000000..f4ccca8f --- /dev/null +++ b/itchat/readme.md @@ -0,0 +1,7 @@ +As itchat is multi-threaded, it's impossible to catch the internet errors it has produced. So I added some callbacks. + +In core.functionDict, I added the key "Error" whose value is a list, where we may store error-handling functions. + +Use '@itchat.error_register(True)' to register your error-handling function. + +这玩意多线程导致我没法捕捉错误,所以搞了个回调。用@itchat.error_register(True)来注册你的错误处理函数。 diff --git a/itchat/utils.py b/itchat/utils.py index e217368c..8b46540e 100644 --- a/itchat/utils.py +++ b/itchat/utils.py @@ -4,11 +4,6 @@ from HTMLParser import HTMLParser except ImportError: from html.parser import HTMLParser -try: - from urllib import quote as _quote - quote = lambda n: _quote(n.encode('utf8', 'replace')) -except ImportError: - from urllib.parse import quote import requests @@ -124,15 +119,14 @@ def print_line(msg, oneLine = False): ).decode(sys.stdin.encoding or 'utf8', 'replace')) sys.stdout.flush() -def test_connect(retryTime=5): +def test_connect(retryTime=15): for i in range(retryTime): try: r = requests.get(config.BASE_URL) return True except: if i == retryTime - 1: - logger.error(traceback.format_exc()) - return False + return traceback.format_exc() def contact_deep_copy(core, contact): with core.storageClass.updateLock: