diff --git a/.gitignore b/.gitignore
index c0ca475b..e63e229d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ test.py
itchat.pkl
QR.jpg
.DS_Store
+QR.png
diff --git a/README.md b/README.md
index 75767d9b..3cceb963 100644
--- a/README.md
+++ b/README.md
@@ -239,6 +239,27 @@ itchat.logout()
若不设置loginCallback的值,则将会自动删除二维码图片并清空命令行显示。
+### RPC调用
+
+如果你想启动RPC服务器,请使用下面的命令,监听地址和端口可根据自己需求修改
+
+```python
+import itchat
+
+itchat.start_rpc_server('localhost', 9000)
+```
+
+几乎所有的API都已经导出,细节请查看rpc.py文件
+
+客户端代码示例
+
+```python
+import xmlrpc.client
+
+rpc = xmlrpc.client.ServerProxy('http://localhost:9000/')
+rpc.get_friends()
+```
+
## 常见问题与解答
Q: 如何通过这个包将自己的微信号变为控制器?
diff --git a/README.rst b/README.rst
index 47b6df45..a1180ac6 100644
--- a/README.rst
+++ b/README.rst
@@ -235,6 +235,26 @@ 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.
+*RPC Call*
+
+If you want to start RPC server, please use the command below. You can specify the listening address and port.
+
+.. code:: python
+ import itchat
+
+ itchat.start_rpc_server('localhost', 9000)
+
+Almost all the APIs are exported for RPC use, please check rpc.py for detail
+
+Client side code:
+
+.. code:: python
+ import xmlrpc.client
+
+ rpc = xmlrpc.client.ServerProxy('http://localhost:9000/')
+ rpc.get_friends()
+
+
**FAQ**
Q: Why I can't send files whose name is encoded in utf8?
diff --git a/README_EN.md b/README_EN.md
index ab6df03d..a38da6e8 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -237,6 +237,27 @@ 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.
+### RPC Call
+
+If you want to start RPC server, please use the command below. You can specify the listening address and port.
+
+```python
+import itchat
+
+itchat.start_rpc_server('localhost', 9000)
+```
+
+Almost all the APIs are exported for RPC use, please check rpc.py for detail
+
+Client side code:
+
+```python
+import xmlrpc.client
+
+rpc = xmlrpc.client.ServerProxy('http://localhost:9000/')
+rpc.get_friends()
+```
+
## FAQ
Q: How to use this package to use my wechat as an monitor?
diff --git a/itchat/__init__.py b/itchat/__init__.py
index 256fc721..adb930cc 100644
--- a/itchat/__init__.py
+++ b/itchat/__init__.py
@@ -18,6 +18,10 @@ def new_instance():
# but it makes auto-fill a real mess, so forgive me for my following **
# actually it toke me less than 30 seconds, god bless Uganda
+# rpc
+start_rpc_server = originInstance.start_rpc_server
+recv = originInstance.recv
+
# components.login
login = originInstance.login
get_QRuuid = originInstance.get_QRuuid
diff --git a/itchat/components/__init__.py b/itchat/components/__init__.py
index f088c173..c4122f22 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 .rpc import load_rpc
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_rpc(core)
diff --git a/itchat/components/rpc.py b/itchat/components/rpc.py
new file mode 100644
index 00000000..d389db67
--- /dev/null
+++ b/itchat/components/rpc.py
@@ -0,0 +1,119 @@
+import base64
+import io
+import json
+import logging
+import threading
+import types
+
+import itchat.returnvalues as rv
+import itchat.storage.templates as tpl
+import six.moves.xmlrpc_client as cli
+import six.moves.xmlrpc_server as rpc
+
+logger = logging.getLogger('itchat')
+
+exported_functions = [
+ # components.login
+ 'login',
+ 'get_QRuuid',
+ 'get_QR',
+ 'check_login',
+ 'web_init',
+ 'show_mobile_login',
+ 'start_receiving',
+ 'get_msg',
+ 'logout',
+
+ # components.contact
+ 'update_chatroom',
+ 'update_friend',
+ 'get_contacts',
+ 'get_friends',
+ 'get_chatrooms',
+ 'get_mps',
+ 'set_alias',
+ 'set_pinned',
+ 'add_friend',
+ 'get_head_img',
+ 'create_chatroom',
+ 'set_chatroom_name',
+ 'delete_member_from_chatroom',
+ 'add_member_into_chatroom',
+
+ # components.messages
+ 'send_raw_msg',
+ 'send_msg',
+ 'upload_file',
+ 'send_file',
+ 'send_image',
+ 'send_video',
+ 'send',
+ 'revoke',
+
+ # components.hotreload
+ 'dump_login_status',
+ 'load_login_status',
+
+ # components.register
+ 'auto_login',
+ 'configured_reply',
+ 'msg_register',
+ 'run',
+
+ # other functions
+ 'search_friends',
+ 'search_chatrooms',
+ 'search_mps',
+ 'set_logging',
+ 'recv',
+]
+
+
+def dump_obj(marshaller, value, write, escape=cli.escape):
+ if isinstance(value, io.BytesIO):
+ write('')
+ encoded = base64.encodebytes(value.getvalue())
+ write(encoded.decode('ascii'))
+ write('')
+ elif isinstance(value, int):
+ write('')
+ write('%d' % value)
+ write('')
+ else:
+ write('')
+ write(escape(json.dumps(value)))
+ write('')
+
+
+def register_types():
+ cli.Marshaller.dispatch[tpl.Chatroom] = dump_obj
+ cli.Marshaller.dispatch[tpl.ContactList] = dump_obj
+ cli.Marshaller.dispatch[tpl.User] = dump_obj
+ cli.Marshaller.dispatch[tpl.ChatroomMember] = dump_obj
+ cli.Marshaller.dispatch[tpl.MassivePlatform] = dump_obj
+ cli.Marshaller.dispatch[rv.ReturnValue] = dump_obj
+ cli.Marshaller.dispatch[io.BytesIO] = dump_obj
+ cli.Marshaller.dispatch[int] = dump_obj
+
+
+def load_rpc(core):
+ core.start_rpc_server = start_rpc_server
+
+
+def start_rpc_server(self, host, port, block=False):
+ server = rpc.SimpleXMLRPCServer((host, port), logRequests=False, allow_none=True)
+ server.register_introspection_functions()
+ for i in exported_functions:
+ if hasattr(self, i):
+ server.register_function(getattr(self, i))
+ logger.info('Starting RPC server')
+ self.rpc = server
+ if not block:
+ rpc_thread = threading.Thread(target=server.serve_forever, args=())
+ rpc_thread.daemon = True
+ register_types()
+ rpc_thread.start()
+ else:
+ register_types()
+ server.serve_forever()
+
diff --git a/itchat/core.py b/itchat/core.py
index 52d6ae4f..950a1a13 100644
--- a/itchat/core.py
+++ b/itchat/core.py
@@ -27,6 +27,8 @@ def __init__(self):
self.functionDict = {'FriendChat': {}, 'GroupChat': {}, 'MpChat': {}}
self.useHotReload, self.hotReloadDir = False, 'itchat.pkl'
self.receivingRetryCount = 5
+ self.rpc = None
+
def login(self, enableCmdQR=False, picDir=None, qrCallback=None,
loginCallback=None, exitCallback=None):
''' log in like web wechat does
@@ -455,5 +457,13 @@ 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)
-
+ def recv(self):
+ ''' receive cached message on msgList
+ '''
+ return self.storageClass.recv()
+ def start_rpc_server(self, host, port):
+ ''' start rpc server
+ it is defined in components/rpc.py
+ '''
+ raise NotImplementedError()
load_components(Core)
diff --git a/itchat/storage/__init__.py b/itchat/storage/__init__.py
index ec6c8ba7..24b1d034 100644
--- a/itchat/storage/__init__.py
+++ b/itchat/storage/__init__.py
@@ -115,3 +115,12 @@ def search_mps(self, name=None, userName=None):
if name in m['NickName']:
matchList.append(copy.deepcopy(m))
return matchList
+ def recv(self):
+ rtn = []
+ count = 1024
+ while count > 0 and not self.msgList.empty():
+ elem = self.msgList.get()
+ rtn.append(dict(elem))
+ count -= 1
+ return rtn
+
\ No newline at end of file
diff --git a/scripts/itchat_server b/scripts/itchat_server
new file mode 100755
index 00000000..38305e70
--- /dev/null
+++ b/scripts/itchat_server
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+HOST=localhost
+PORT=9000
+QRCODE=True
+
+usage() { echo "Usage: $0 [-h ] [-p ] [-2]" 1>&2; exit 1; }
+
+while getopts ":h:p:2" o; do
+ case "${o}" in
+ h)
+ HOST=${OPTARG}
+ ;;
+ p)
+ PORT=${OPTARG}
+ ;;
+ 2)
+ QRCODE=2
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+cat << EOF | python3
+import sys
+import itchat
+
+def quit():
+ print('error, exiting...')
+ sys.exit(-1)
+
+if __name__ == '__main__':
+ try:
+ itchat.auto_login(enableCmdQR=${QRCODE}, hotReload=True, exitCallback=quit)
+ itchat.start_rpc_server('${HOST}', ${PORT}, block=True)
+ except KeyboardInterrupt:
+ print('exiting...')
+ sys.exit(0)
+EOF
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 51e04f1d..1173033e 100644
--- a/setup.py
+++ b/setup.py
@@ -49,6 +49,8 @@
install_requires=['requests', 'pyqrcode', 'pypng'],
+ scripts = ['scripts/itchat_server'],
+
# List additional groups of dependencies here
extras_require={},
)