Add python cilent
This commit is contained in:
parent
5f4472b0fb
commit
177bdd1c61
26
python/README.MD
Normal file
26
python/README.MD
Normal file
@ -0,0 +1,26 @@
|
||||
# WeChatFerry Python 客户端
|
||||
代码于 `Python3.7` 环境开发。
|
||||
|
||||
## 配置环境
|
||||
```sh
|
||||
# 创建虚拟环境
|
||||
python -m venv .env
|
||||
# 激活虚拟环境
|
||||
source .env/Scripts/activate
|
||||
# 升级 pip
|
||||
pip install --upgrade pip
|
||||
# 安装依赖包
|
||||
pip install grpcio grpcio-tools
|
||||
```
|
||||
|
||||
## 运行
|
||||
```sh
|
||||
# 启动客户端
|
||||
python demo.py
|
||||
```
|
||||
|
||||
## 重新生成 gRPC 文件
|
||||
```sh
|
||||
cd wcf
|
||||
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I=../ wcf.proto
|
||||
```
|
49
python/demo.py
Normal file
49
python/demo.py
Normal file
@ -0,0 +1,49 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import signal
|
||||
from time import sleep
|
||||
|
||||
from wcf.client import Wcf
|
||||
|
||||
|
||||
def main():
|
||||
logging.info("Start demo...")
|
||||
wcf = Wcf()
|
||||
|
||||
def handler(sig, frame):
|
||||
wcf.cleanup()
|
||||
exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, handler)
|
||||
sleep(1) # Slow down
|
||||
print(f"Is Login: {True if wcf.is_login() else False}")
|
||||
print(f"SelfWxid: {wcf.get_self_wxid()}")
|
||||
|
||||
sleep(1)
|
||||
wcf.enable_recv_msg(print)
|
||||
# wcf.disable_recv_msg() # Call anytime when you don't want to receive messages
|
||||
|
||||
ret = wcf.send_text("Hello world.", "filehelper")
|
||||
print(f"send_text: {ret}")
|
||||
|
||||
ret = wcf.send_image("TEQuant.jpeg", "filehelper")
|
||||
print(f"send_image: {ret}")
|
||||
|
||||
print(f"Message types:\n{wcf.get_msg_types()}")
|
||||
print(f"Contacts:\n{wcf.get_contacts()}")
|
||||
|
||||
print(f"DBs:\n{wcf.get_dbs()}")
|
||||
print(f"Tables:\n{wcf.get_tables('db')}")
|
||||
print(f"Results:\n{wcf.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
||||
|
||||
# wcf.accept_new_friend("v3", "v4") # 需要真正的 V3、V4 信息
|
||||
|
||||
# Keep running to receive messages
|
||||
wcf.keep_running()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level='DEBUG')
|
||||
main()
|
131
python/wcf/client.py
Normal file
131
python/wcf/client.py
Normal file
@ -0,0 +1,131 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import atexit
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import grpc
|
||||
|
||||
WCF_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, WCF_ROOT)
|
||||
import wcf_pb2 # noqa
|
||||
import wcf_pb2_grpc # noqa
|
||||
|
||||
|
||||
class Wcf():
|
||||
def __init__(self, host_port: str = "localhost:10086") -> None:
|
||||
self._enable_recv_msg = False
|
||||
self.LOG = logging.getLogger("WCF")
|
||||
self._sdk = ctypes.cdll.LoadLibrary(f"{WCF_ROOT}/sdk.dll")
|
||||
if self._sdk.WxInitSDK() != 0:
|
||||
self.LOG.error("初始化失败!")
|
||||
return
|
||||
|
||||
self._channel = grpc.insecure_channel(host_port)
|
||||
self._stub = wcf_pb2_grpc.WcfStub(self._channel)
|
||||
atexit.register(self.disable_recv_msg) # 退出的时候停止消息接收,防止内存泄露
|
||||
self._is_running = True
|
||||
|
||||
def __del__(self) -> None:
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self) -> None:
|
||||
if not self._is_running:
|
||||
return
|
||||
|
||||
self.disable_recv_msg()
|
||||
self._channel.close()
|
||||
self._sdk.WxDestroySDK()
|
||||
handle = self._sdk._handle
|
||||
del self._sdk
|
||||
ctypes.windll.kernel32.FreeLibrary(handle)
|
||||
self._is_running = False
|
||||
|
||||
def keep_running(self):
|
||||
try:
|
||||
while True:
|
||||
sleep(1)
|
||||
except Exception as e:
|
||||
self.cleanup()
|
||||
|
||||
def is_login(self) -> int:
|
||||
rsp = self._stub.RpcIsLogin(wcf_pb2.Empty())
|
||||
return rsp.status
|
||||
|
||||
def get_self_wxid(self) -> str:
|
||||
rsp = self._stub.RpcGetSelfWxid(wcf_pb2.Empty())
|
||||
return rsp.str
|
||||
|
||||
def _rpc_get_message(self, func):
|
||||
rsps = self._stub.RpcEnableRecvMsg(wcf_pb2.Empty())
|
||||
try:
|
||||
for rsp in rsps:
|
||||
func(rsp)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"RpcEnableRecvMsg: {e}")
|
||||
finally:
|
||||
self.disable_recv_msg()
|
||||
|
||||
def enable_recv_msg(self, callback: Callable[..., Any] = None) -> bool:
|
||||
if self._enable_recv_msg:
|
||||
return True
|
||||
|
||||
if callback is None:
|
||||
return False
|
||||
|
||||
self._enable_recv_msg = True
|
||||
# 阻塞,把控制权交给用户
|
||||
# self._rpc_get_message(callback)
|
||||
|
||||
# 不阻塞,启动一个新的线程来接收消息
|
||||
Thread(target=self._rpc_get_message, name="GetMessage", args=(callback,), daemon=True).start()
|
||||
|
||||
return True
|
||||
|
||||
def disable_recv_msg(self) -> int:
|
||||
if not self._enable_recv_msg:
|
||||
return -1
|
||||
|
||||
rsp = self._stub.RpcDisableRecvMsg(wcf_pb2.Empty())
|
||||
if rsp.status == 0:
|
||||
self._enable_recv_msg = False
|
||||
|
||||
return rsp.status
|
||||
|
||||
def send_text(self, msg: str, receiver: str, aters: Optional[str] = "") -> int:
|
||||
rsp = self._stub.RpcSendTextMsg(wcf_pb2.TextMsg(msg=msg, receiver=receiver, aters=aters))
|
||||
return rsp.status
|
||||
|
||||
def send_image(self, path: str, receiver: str) -> int:
|
||||
rsp = self._stub.RpcSendImageMsg(wcf_pb2.ImageMsg(path=path, receiver=receiver))
|
||||
return rsp.status
|
||||
|
||||
def get_msg_types(self) -> wcf_pb2.MsgTypes:
|
||||
rsp = self._stub.RpcGetMsgTypes(wcf_pb2.Empty())
|
||||
return rsp
|
||||
|
||||
def get_contacts(self) -> wcf_pb2.Contacts:
|
||||
rsp = self._stub.RpcGetContacts(wcf_pb2.Empty())
|
||||
return rsp
|
||||
|
||||
def get_dbs(self) -> wcf_pb2.DbNames:
|
||||
rsp = self._stub.RpcGetDbNames(wcf_pb2.Empty())
|
||||
return rsp
|
||||
|
||||
def get_tables(self, db: str) -> wcf_pb2.DbTables:
|
||||
rsp = self._stub.RpcGetDbTables(wcf_pb2.String(str=db))
|
||||
return rsp
|
||||
|
||||
def query_sql(self, db: str, sql: str) -> wcf_pb2.DbRows:
|
||||
rsp = self._stub.RpcExecDbQuery(wcf_pb2.DbQuery(db=db, sql=sql))
|
||||
return rsp
|
||||
|
||||
def accept_new_friend(self, v3: str, v4: str) -> int:
|
||||
rsp = self._stub.RpcAcceptNewFriend(wcf_pb2.Verification(v3=v3, v4=v4))
|
||||
return rsp.status
|
Loading…
Reference in New Issue
Block a user