Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add-rpc-and-recv-command #623

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ test.py
itchat.pkl
QR.jpg
.DS_Store
QR.png
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,27 @@ itchat.logout()

若不设置loginCallback的值,则将会自动删除二维码图片并清空命令行显示。

### RPC调用

如果你想启动RPC服务器,请使用下面的命令,监听地址和端口可根据自己需求修改

```python
import itchat

itchat.start_rpc_server(‘localhost’, 9000)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=> '

```

几乎所有的API都已经导出,细节请查看rpc.py文件

客户端代码示例

```python
import xmlrpc.client

rpc = xmlrpc.client.ServerProxy("http://localhost:9000/")
rpc.get_friends()
```

## 常见问题与解答

Q: 如何通过这个包将自己的微信号变为控制器?
Expand Down
20 changes: 20 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
21 changes: 21 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
4 changes: 4 additions & 0 deletions itchat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions itchat/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
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)
load_hotreload(core)
load_login(core)
load_messages(core)
load_register(core)
load_rpc(core)
119 changes: 119 additions & 0 deletions itchat/components/rpc.py
Original file line number Diff line number Diff line change
@@ -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('<value><base64>')
encoded = base64.encodebytes(value.getvalue())
write(encoded.decode('ascii'))
write('</base64></value>')
elif isinstance(value, int):
write('<value><i8>')
write('%d' % value)
write('</i8></value>')
else:
write('<value><string>')
write(escape(json.dumps(value)))
write('</string></value>')


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()

12 changes: 11 additions & 1 deletion itchat/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
9 changes: 9 additions & 0 deletions itchat/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

42 changes: 42 additions & 0 deletions scripts/itchat_server
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash

HOST=localhost
PORT=9000
QRCODE=True

usage() { echo "Usage: $0 [-h <HOST_IP> ] [-p <PORT>] [-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
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@

install_requires=['requests', 'pyqrcode', 'pypng'],

scripts = ['scripts/itchat_server'],

# List additional groups of dependencies here
extras_require={},
)