commit
e759e5fa9d
@ -1,4 +1,4 @@
|
|||||||
# WeChatFerry wcfautopy 客户端(基于python客户端进行修改)
|
# WcfAuto 客户端(基于 python 客户端)
|
||||||
[](https://pypi.python.org/pypi/wcferry) [](https://pypi.python.org/pypi/wcferry) [](https://wechatferry.readthedocs.io/zh/latest/?badge=latest)
|
[](https://pypi.python.org/pypi/wcferry) [](https://pypi.python.org/pypi/wcferry) [](https://wechatferry.readthedocs.io/zh/latest/?badge=latest)
|
||||||
|
|
||||||
|[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/vAGpn1C9stI8Xzt1hUJhLA)|
|
|[📖 文档](https://wechatferry.readthedocs.io/)|[📺 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/vAGpn1C9stI8Xzt1hUJhLA)|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
```sh
|
```sh
|
||||||
pip install --upgrade wcferry
|
pip install --upgrade wcfauto
|
||||||
```
|
```
|
||||||
|
|
||||||
### Demo:
|
### Demo:
|
||||||
@ -18,62 +18,63 @@ pip install --upgrade wcferry
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from wcferry import Wcf, WxMsg, Register
|
|
||||||
|
from wcfauto import Register, Wcf, WxMsg
|
||||||
|
|
||||||
logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s")
|
logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s")
|
||||||
LOG = logging.getLogger("Demo")
|
LOG = logging.getLogger("Demo")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
receiver = Register()
|
receiver = Register()
|
||||||
|
|
||||||
@receiver.message_register(isDivision=True, isGroup=True, isPyq=False)
|
@receiver.message_register(isDivision=True, isGroup=True, isPyq=False)
|
||||||
def process_msg(bot: Wcf, msg: WxMsg):
|
def process_msg(bot: Wcf, msg: WxMsg):
|
||||||
"""
|
"""
|
||||||
同步消息函数装饰器
|
同步消息函数装饰器
|
||||||
"""
|
"""
|
||||||
LOG.info(f"收到消息: {msg}")
|
LOG.info(f"收到消息: {msg}")
|
||||||
|
|
||||||
sleep(5) # 等微信加载好,以免信息显示异常
|
sleep(5) # 等微信加载好,以免信息显示异常
|
||||||
LOG.info(f"已经登录: {True if bot.is_login() else False}")
|
LOG.info(f"已经登录: {True if bot.is_login() else False}")
|
||||||
LOG.info(f"wxid: {bot.get_self_wxid()}")
|
LOG.info(f"wxid: {bot.get_self_wxid()}")
|
||||||
|
|
||||||
# bot.disable_recv_msg() # 当需要停止接收消息时调用
|
# bot.disable_recv_msg() # 当需要停止接收消息时调用
|
||||||
sleep(5)
|
sleep(5)
|
||||||
ret = bot.send_text("Hello world.", "filehelper")
|
ret = bot.send_text("Hello world.", "filehelper")
|
||||||
LOG.info(f"send_text: {ret}")
|
LOG.info(f"send_text: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
# 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\)
|
# 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\)
|
||||||
ret = bot.send_image("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper")
|
ret = bot.send_image(
|
||||||
|
"https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper")
|
||||||
LOG.info(f"send_image: {ret}")
|
LOG.info(f"send_image: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
# 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\)
|
# 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\)
|
||||||
ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper")
|
ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper")
|
||||||
LOG.info(f"send_file: {ret}")
|
LOG.info(f"send_file: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
LOG.info(f"Message types:\n{bot.get_msg_types()}")
|
LOG.info(f"Message types:\n{bot.get_msg_types()}")
|
||||||
LOG.info(f"Contacts:\n{bot.get_contacts()}")
|
LOG.info(f"Contacts:\n{bot.get_contacts()}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
LOG.info(f"DBs:\n{bot.get_dbs()}")
|
LOG.info(f"DBs:\n{bot.get_dbs()}")
|
||||||
LOG.info(f"Tables:\n{bot.get_tables('db')}")
|
LOG.info(f"Tables:\n{bot.get_tables('db')}")
|
||||||
LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
||||||
|
|
||||||
# 需要真正的 V3、V4 信息
|
# 需要真正的 V3、V4 信息
|
||||||
# bot.accept_new_friend("v3", "v4")
|
# bot.accept_new_friend("v3", "v4")
|
||||||
|
|
||||||
# 添加群成员,填写正确的群 ID 和成员 wxid
|
# 添加群成员,填写正确的群 ID 和成员 wxid
|
||||||
# ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
# ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
||||||
# LOG.info(f"add_chatroom_members: {ret}")
|
# LOG.info(f"add_chatroom_members: {ret}")
|
||||||
|
|
||||||
# 删除群成员,填写正确的群 ID 和成员 wxid
|
# 删除群成员,填写正确的群 ID 和成员 wxid
|
||||||
# ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
# ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
||||||
# LOG.info(f"add_chatroom_members: {ret}")
|
# LOG.info(f"add_chatroom_members: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
bot.refresh_pyq(0) # 刷新朋友圈第一页
|
bot.refresh_pyq(0) # 刷新朋友圈第一页
|
||||||
# bot.refresh_pyq(id) # 从 id 开始刷新朋友圈
|
# bot.refresh_pyq(id) # 从 id 开始刷新朋友圈
|
||||||
@ -92,72 +93,8 @@ def main():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|||
|
|||
|
||||||
|:-:|:-:|
|
|:-:|:-:|
|
||||||
|后台回复 `WeChatFerry` 加群交流|如果你觉得有用|
|
|后台回复 `WeChatFerry` 加群交流|如果你觉得有用|
|
||||||
|
|
||||||
## 一起开发
|
|
||||||
### 配置环境
|
|
||||||
```sh
|
|
||||||
# 创建虚拟环境
|
|
||||||
python -m venv .env
|
|
||||||
# 激活虚拟环境
|
|
||||||
source .env/Scripts/activate
|
|
||||||
# 升级 pip
|
|
||||||
pip install --upgrade pip
|
|
||||||
# 安装依赖包
|
|
||||||
pip install grpcio-tools pynng
|
|
||||||
```
|
|
||||||
|
|
||||||
### 重新生成 PB 文件
|
|
||||||
```sh
|
|
||||||
# CMD
|
|
||||||
cd clients\wcfautopy\wcferry
|
|
||||||
python -m grpc_tools.protoc --python_out=. --proto_path=..\..\..\WeChatFerry\rpc\proto\ wcf.proto
|
|
||||||
|
|
||||||
# GitBash
|
|
||||||
cd clients/wcfautopy/wcferry
|
|
||||||
python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc/proto/ wcf.proto
|
|
||||||
```
|
|
||||||
|
|
||||||
## 版本更新
|
|
||||||
### 39.0.3.0 (2023.09.28)
|
|
||||||
* 修复登录账号昵称超长报错问题
|
|
||||||
|
|
||||||
<details><summary>点击查看更多</summary>
|
|
||||||
|
|
||||||
版本号:`w.x.y.z`。
|
|
||||||
|
|
||||||
其中:
|
|
||||||
* `w` 是微信的大版本号,如 `37` (3.7.a.a), `38` (3.8.a.a), `39` (3.9.a.a)
|
|
||||||
* `x` 是适配的微信的小版本号,从 0 开始
|
|
||||||
* `y` 是 `WeChatFerry` 的版本,从 0 开始
|
|
||||||
* `z` 是各客户端的版本,从 0 开始
|
|
||||||
|
|
||||||
功能:
|
|
||||||
|
|
||||||
* 检查登录状态
|
|
||||||
* 获取登录账号的 wxid
|
|
||||||
* 获取消息类型
|
|
||||||
* 获取所有联系人
|
|
||||||
* 获取所有好友
|
|
||||||
* 获取数据库
|
|
||||||
* 获取某数据库下的表
|
|
||||||
* 获取用户信息
|
|
||||||
* 发送文本消息(可 @)
|
|
||||||
* 发送图片(wcfautopy 客户端支持网络路径)
|
|
||||||
* 发送文件(wcfautopy 客户端支持网络路径)
|
|
||||||
* 允许接收消息
|
|
||||||
* 停止接收消息
|
|
||||||
* 执行 SQL 查询
|
|
||||||
* 接受好友申请
|
|
||||||
* 添加群成员
|
|
||||||
* 删除群成员
|
|
||||||
* 解密图片
|
|
||||||
* 获取朋友圈消息
|
|
||||||
* 某功能(Breaking Change)
|
|
||||||
|
|
||||||
</details>
|
|
@ -3,62 +3,63 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from wcferry import Wcf, WxMsg, Register
|
|
||||||
|
from wcfauto import Register, Wcf, WxMsg
|
||||||
|
|
||||||
logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s")
|
logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s")
|
||||||
LOG = logging.getLogger("Demo")
|
LOG = logging.getLogger("Demo")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
receiver = Register()
|
receiver = Register()
|
||||||
|
|
||||||
@receiver.message_register(isDivision=True, isGroup=True, isPyq=False)
|
@receiver.message_register(isDivision=True, isGroup=True, isPyq=False)
|
||||||
def process_msg(bot: Wcf, msg: WxMsg):
|
def process_msg(bot: Wcf, msg: WxMsg):
|
||||||
"""
|
"""
|
||||||
同步消息函数装饰器
|
同步消息函数装饰器
|
||||||
"""
|
"""
|
||||||
LOG.info(f"收到消息: {msg}")
|
LOG.info(f"收到消息: {msg}")
|
||||||
|
|
||||||
sleep(5) # 等微信加载好,以免信息显示异常
|
sleep(5) # 等微信加载好,以免信息显示异常
|
||||||
LOG.info(f"已经登录: {True if bot.is_login() else False}")
|
LOG.info(f"已经登录: {True if bot.is_login() else False}")
|
||||||
LOG.info(f"wxid: {bot.get_self_wxid()}")
|
LOG.info(f"wxid: {bot.get_self_wxid()}")
|
||||||
|
|
||||||
# bot.disable_recv_msg() # 当需要停止接收消息时调用
|
# bot.disable_recv_msg() # 当需要停止接收消息时调用
|
||||||
sleep(5)
|
sleep(5)
|
||||||
ret = bot.send_text("Hello world.", "filehelper")
|
ret = bot.send_text("Hello world.", "filehelper")
|
||||||
LOG.info(f"send_text: {ret}")
|
LOG.info(f"send_text: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
# 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\)
|
# 需要确保图片路径正确,建议使用绝对路径(使用双斜杠\\)
|
||||||
ret = bot.send_image("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper")
|
ret = bot.send_image(
|
||||||
|
"https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/QR.jpeg", "filehelper")
|
||||||
LOG.info(f"send_image: {ret}")
|
LOG.info(f"send_image: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
# 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\)
|
# 需要确保文件路径正确,建议使用绝对路径(使用双斜杠\\)
|
||||||
ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper")
|
ret = bot.send_file("https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD", "filehelper")
|
||||||
LOG.info(f"send_file: {ret}")
|
LOG.info(f"send_file: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
LOG.info(f"Message types:\n{bot.get_msg_types()}")
|
LOG.info(f"Message types:\n{bot.get_msg_types()}")
|
||||||
LOG.info(f"Contacts:\n{bot.get_contacts()}")
|
LOG.info(f"Contacts:\n{bot.get_contacts()}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
LOG.info(f"DBs:\n{bot.get_dbs()}")
|
LOG.info(f"DBs:\n{bot.get_dbs()}")
|
||||||
LOG.info(f"Tables:\n{bot.get_tables('db')}")
|
LOG.info(f"Tables:\n{bot.get_tables('db')}")
|
||||||
LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
LOG.info(f"Results:\n{bot.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}")
|
||||||
|
|
||||||
# 需要真正的 V3、V4 信息
|
# 需要真正的 V3、V4 信息
|
||||||
# bot.accept_new_friend("v3", "v4")
|
# bot.accept_new_friend("v3", "v4")
|
||||||
|
|
||||||
# 添加群成员,填写正确的群 ID 和成员 wxid
|
# 添加群成员,填写正确的群 ID 和成员 wxid
|
||||||
# ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
# ret = bot.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
||||||
# LOG.info(f"add_chatroom_members: {ret}")
|
# LOG.info(f"add_chatroom_members: {ret}")
|
||||||
|
|
||||||
# 删除群成员,填写正确的群 ID 和成员 wxid
|
# 删除群成员,填写正确的群 ID 和成员 wxid
|
||||||
# ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
# ret = bot.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
|
||||||
# LOG.info(f"add_chatroom_members: {ret}")
|
# LOG.info(f"add_chatroom_members: {ret}")
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
bot.refresh_pyq(0) # 刷新朋友圈第一页
|
bot.refresh_pyq(0) # 刷新朋友圈第一页
|
||||||
# bot.refresh_pyq(id) # 从 id 开始刷新朋友圈
|
# bot.refresh_pyq(id) # 从 id 开始刷新朋友圈
|
@ -1,21 +1,18 @@
|
|||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import wcfauto
|
||||||
from __future__ import print_function
|
from setuptools import find_packages, setup
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
import wcferry
|
|
||||||
|
|
||||||
with open("README.md", "r", encoding="utf-8") as fh:
|
with open("README.md", "r", encoding="utf-8") as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="wcferry",
|
name="wcfauto",
|
||||||
version=wcferry.__version__,
|
version=wcfauto.__version__,
|
||||||
author="Changhua",
|
author="bujinzhang",
|
||||||
author_email="lichanghua0821@gmail.com",
|
author_email="",
|
||||||
description="一个玩微信的工具",
|
description="一个玩微信的工具",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
@ -26,9 +23,7 @@ setup(
|
|||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"setuptools",
|
"setuptools",
|
||||||
"grpcio-tools",
|
"wcferry",
|
||||||
"pynng",
|
|
||||||
"requests",
|
|
||||||
],
|
],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Environment :: Win32 (MS Windows)",
|
"Environment :: Win32 (MS Windows)",
|
7
clients/pyauto/wcfauto/__init__.py
Normal file
7
clients/pyauto/wcfauto/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from wcfauto.auto_res import Register
|
||||||
|
from wcfauto.wcf import WcfV2 as Wcf
|
||||||
|
from wcfauto.wcf import WxMsgV2 as WxMsg
|
||||||
|
|
||||||
|
__version__ = "39.0.3.0"
|
7
clients/pyauto/wcfauto/auto_res/__init__.py
Normal file
7
clients/pyauto/wcfauto/auto_res/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from wcfauto.auto_res.bot import Register
|
||||||
|
from wcfauto.auto_res.core import load_function
|
||||||
|
|
||||||
|
|
||||||
|
Register = load_function(Register)
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from typing import Callable, Any
|
|
||||||
from wcferry.event import Event
|
|
||||||
from abc import abstractmethod
|
|
||||||
from wcferry.client import Wcf
|
|
||||||
import logging
|
import logging
|
||||||
|
from abc import abstractmethod
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
from wcfauto.event import Event
|
||||||
|
from wcfauto.wcf import WcfV2 as Wcf
|
||||||
|
|
||||||
|
|
||||||
class Register(Event):
|
class Register(Event):
|
@ -5,9 +5,10 @@ import functools
|
|||||||
import queue
|
import queue
|
||||||
import traceback
|
import traceback
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Callable, Any
|
from typing import Any, Callable
|
||||||
from wcferry.client import Wcf
|
|
||||||
from wcferry.wxmsg import WxMsg
|
from wcfauto.wcf import WcfV2 as Wcf
|
||||||
|
from wcfauto.wcf import WxMsgV2 as WxMsg
|
||||||
|
|
||||||
|
|
||||||
def load_function(cls):
|
def load_function(cls):
|
||||||
@ -51,8 +52,8 @@ def _processing_async_func(self,
|
|||||||
async def __async_func(bot: Wcf, message: WxMsg):
|
async def __async_func(bot: Wcf, message: WxMsg):
|
||||||
try:
|
try:
|
||||||
# 判断被装饰函数是否为协程函数, 本函数要求是协程函数
|
# 判断被装饰函数是否为协程函数, 本函数要求是协程函数
|
||||||
if not asyncio.iscoroutinefunction(func): raise ValueError(
|
if not asyncio.iscoroutinefunction(func):
|
||||||
f'这里应使用协程函数, 而被装饰函数-> ({func.__name__}) <-是非协程函数')
|
raise ValueError(f'这里应使用协程函数, 而被装饰函数-> ({func.__name__}) <-是非协程函数')
|
||||||
if message.is_pyq() and isPyq:
|
if message.is_pyq() and isPyq:
|
||||||
return await func(bot, message)
|
return await func(bot, message)
|
||||||
if not isDivision:
|
if not isDivision:
|
||||||
@ -78,8 +79,9 @@ def _processing_universal_func(self,
|
|||||||
def universal_func(bot: Wcf, message: WxMsg):
|
def universal_func(bot: Wcf, message: WxMsg):
|
||||||
try:
|
try:
|
||||||
# 判断被装饰函数是否为协程函数, 本函数要求是协程函数
|
# 判断被装饰函数是否为协程函数, 本函数要求是协程函数
|
||||||
if asyncio.iscoroutinefunction(func): raise ValueError(
|
if asyncio.iscoroutinefunction(func):
|
||||||
f'这里应使用非协程函数, 而被装饰函数-> ({func.__name__}) <-协程函数')
|
raise ValueError(
|
||||||
|
f'这里应使用非协程函数, 而被装饰函数-> ({func.__name__}) <-协程函数')
|
||||||
if message.is_pyq() and isPyq:
|
if message.is_pyq() and isPyq:
|
||||||
return func(bot, message)
|
return func(bot, message)
|
||||||
if not isDivision:
|
if not isDivision:
|
||||||
@ -115,5 +117,6 @@ def run(self, *args, **kwargs):
|
|||||||
self._LOG.debug("开始接受消息")
|
self._LOG.debug("开始接受消息")
|
||||||
self._wcf.keep_running()
|
self._wcf.keep_running()
|
||||||
|
|
||||||
|
|
||||||
def stop_receiving(self):
|
def stop_receiving(self):
|
||||||
return self._wcf.disable_recv_msg()
|
return self._wcf.disable_recv_msg()
|
6
clients/pyauto/wcfauto/event/__init__.py
Normal file
6
clients/pyauto/wcfauto/event/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from wcfauto.event.event import Event
|
||||||
|
from wcfauto.event.core import load_function
|
||||||
|
|
||||||
|
Event = load_function(Event)
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import traceback
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import traceback
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from abc import abstractmethod
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
|
||||||
class Event(object):
|
class Event(object):
|
@ -1,14 +1,32 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
|
||||||
import time
|
import time
|
||||||
from wcferry import wcf_pb2
|
|
||||||
|
from wcferry import Wcf, WxMsg
|
||||||
|
|
||||||
|
|
||||||
class WxMsg(dict):
|
class WcfV2(Wcf):
|
||||||
|
def __init__(self, host: str, port: int = 10086, debug: bool = True) -> None:
|
||||||
|
super().__init__(host, port, debug)
|
||||||
|
|
||||||
|
def get_msg(self, block=True) -> WxMsg:
|
||||||
|
"""从消息队列中获取消息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block (bool): 是否阻塞,默认阻塞
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
WxMsg: 微信消息
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Empty: 如果阻塞并且超时,抛出空异常,需要用户自行捕获
|
||||||
|
"""
|
||||||
|
return WxMsgV2(self.msgQ.get(block, timeout=1))
|
||||||
|
|
||||||
|
|
||||||
|
class WxMsgV2(WxMsg):
|
||||||
"""微信消息
|
"""微信消息
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (int): 消息类型,可通过 `get_msg_types` 获取
|
type (int): 消息类型,可通过 `get_msg_types` 获取
|
||||||
id (str): 消息 id
|
id (str): 消息 id
|
||||||
@ -20,10 +38,9 @@ class WxMsg(dict):
|
|||||||
extra (str): 视频或图片消息的路径
|
extra (str): 视频或图片消息的路径
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, msg: wcf_pb2.WxMsg) -> None:
|
def __init__(self, msg: WxMsg) -> None:
|
||||||
super(WxMsg, self).__init__()
|
# self._is_self = msg._is_self
|
||||||
self._is_self = msg.is_self
|
# self._is_group = msg._is_group
|
||||||
self._is_group = msg.is_group
|
|
||||||
self._type = msg.type
|
self._type = msg.type
|
||||||
self._id = msg.id
|
self._id = msg.id
|
||||||
self._ts = msg.ts
|
self._ts = msg.ts
|
||||||
@ -54,7 +71,8 @@ class WxMsg(dict):
|
|||||||
rmsg = self.__data['data']['content']
|
rmsg = self.__data['data']['content']
|
||||||
rev_type = re.findall('<sysmsg type="(.*?)"\s?', rmsg)
|
rev_type = re.findall('<sysmsg type="(.*?)"\s?', rmsg)
|
||||||
rev_w = re.findall("<replacemsg><!\[CDATA\[(.*?)]]></replacemsg>", rmsg)
|
rev_w = re.findall("<replacemsg><!\[CDATA\[(.*?)]]></replacemsg>", rmsg)
|
||||||
if len(rev_type) == 0 or len(rev_w) == 0: return
|
if len(rev_type) == 0 or len(rev_w) == 0:
|
||||||
|
return
|
||||||
if rev_type[0] == 'revokemsg' and rev_w[0] == '你撤回了一条消息':
|
if rev_type[0] == 'revokemsg' and rev_w[0] == '你撤回了一条消息':
|
||||||
self.__data['data']['content'] = rev_w[0]
|
self.__data['data']['content'] = rev_w[0]
|
||||||
self.__data['isRevokeMsg'] = True
|
self.__data['isRevokeMsg'] = True
|
@ -1,2 +0,0 @@
|
|||||||
include wcferry/*.dll
|
|
||||||
include wcferry/*.exe
|
|
@ -1,21 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
package com.iamteer.wcf;
|
|
||||||
|
|
||||||
message RoomData {
|
|
||||||
|
|
||||||
message RoomMember {
|
|
||||||
string wxid = 1;
|
|
||||||
string name = 2;
|
|
||||||
int32 state = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
repeated RoomMember members = 1;
|
|
||||||
|
|
||||||
int32 field_2 = 2;
|
|
||||||
int32 field_3 = 3;
|
|
||||||
int32 field_4 = 4;
|
|
||||||
int32 room_capacity = 5;
|
|
||||||
int32 field_6 = 6;
|
|
||||||
int64 field_7 = 7;
|
|
||||||
int64 field_8 = 8;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from wcferry.client import Wcf, __version__
|
|
||||||
from wcferry.wxmsg import WxMsg
|
|
||||||
from wcferry.auto_res import Register
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from wcferry.auto_res.bot import Register
|
|
||||||
from wcferry.auto_res.core import load_function
|
|
||||||
|
|
||||||
|
|
||||||
Register = load_function(Register)
|
|
||||||
|
|
@ -1,694 +0,0 @@
|
|||||||
#! /usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
__version__ = "39.0.3.0"
|
|
||||||
|
|
||||||
import atexit
|
|
||||||
import base64
|
|
||||||
import logging
|
|
||||||
import mimetypes
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from queue import Queue
|
|
||||||
from threading import Thread
|
|
||||||
from time import sleep
|
|
||||||
from typing import Callable, Dict, List, Optional
|
|
||||||
|
|
||||||
import pynng
|
|
||||||
import requests
|
|
||||||
from google.protobuf import json_format
|
|
||||||
from wcferry import wcf_pb2
|
|
||||||
from wcferry.roomdata_pb2 import RoomData
|
|
||||||
from wcferry.wxmsg import WxMsg
|
|
||||||
|
|
||||||
|
|
||||||
def _retry():
|
|
||||||
def decorator(func):
|
|
||||||
""" Retry the function """
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
def logerror(e):
|
|
||||||
func_name = re.findall(r"func: (.*?)\n", str(args[1]))[-1]
|
|
||||||
logging.getLogger("WCF").error(f"Call {func_name} failed: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
ret = func(*args, **kwargs)
|
|
||||||
except pynng.Timeout as _: # 如果超时,重试
|
|
||||||
try:
|
|
||||||
ret = func(*args, **kwargs)
|
|
||||||
except Exception as e:
|
|
||||||
logerror(e)
|
|
||||||
ret = wcf_pb2.Response()
|
|
||||||
except Exception as e: # 其他异常,退出
|
|
||||||
logerror(e)
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
return ret
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
class Wcf():
|
|
||||||
"""WeChatFerry, 一个玩微信的工具。
|
|
||||||
|
|
||||||
Args:
|
|
||||||
host (str): `wcferry` RPC 服务器地址,默认本地启动;也可以指定地址连接远程服务
|
|
||||||
port (int): `wcferry` RPC 服务器端口,默认为 10086,接收消息会占用 `port+1` 端口
|
|
||||||
debug (bool): 是否开启调试模式(仅本地启动有效)
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
contacts (list): 联系人缓存,调用 `get_contacts` 后更新
|
|
||||||
self_wxid (str): 登录账号 wxid
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, host: str = None, port: int = 10086, debug: bool = True) -> None:
|
|
||||||
self._local_mode = False
|
|
||||||
self._is_running = False
|
|
||||||
self._is_receiving_msg = False
|
|
||||||
self._wcf_root = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
self._dl_path = f"{self._wcf_root}/.dl"
|
|
||||||
os.makedirs(self._dl_path, exist_ok=True)
|
|
||||||
self.LOG = logging.getLogger("WCF")
|
|
||||||
self.LOG.info(f"wcferry version: {__version__}")
|
|
||||||
self.port = port
|
|
||||||
self.host = host
|
|
||||||
if host is None:
|
|
||||||
self._local_mode = True
|
|
||||||
self.host = "127.0.0.1"
|
|
||||||
cmd = fr'"{self._wcf_root}\wcf.exe" start {self.port} {"debug" if debug else ""}'
|
|
||||||
if os.system(cmd) != 0:
|
|
||||||
self.LOG.error("初始化失败!")
|
|
||||||
os._exit(-1)
|
|
||||||
|
|
||||||
self.cmd_url = f"tcp://{self.host}:{self.port}"
|
|
||||||
|
|
||||||
# 连接 RPC
|
|
||||||
self.cmd_socket = pynng.Pair1() # Client --> Server,发送消息
|
|
||||||
self.cmd_socket.send_timeout = 2000 # 发送 2 秒超时
|
|
||||||
self.cmd_socket.recv_timeout = 2000 # 接收 2 秒超时
|
|
||||||
try:
|
|
||||||
self.cmd_socket.dial(self.cmd_url, block=True)
|
|
||||||
except Exception as e:
|
|
||||||
self.LOG.error(f"连接失败: {e}")
|
|
||||||
os._exit(-2)
|
|
||||||
|
|
||||||
self.msg_socket = pynng.Pair1() # Server --> Client,接收消息
|
|
||||||
self.msg_socket.send_timeout = 2000 # 发送 2 秒超时
|
|
||||||
self.msg_socket.recv_timeout = 2000 # 接收 2 秒超时
|
|
||||||
self.msg_url = self.cmd_url.replace(str(self.port), str(self.port + 1))
|
|
||||||
|
|
||||||
atexit.register(self.cleanup) # 退出的时候停止消息接收,防止资源占用
|
|
||||||
while not self.is_login(): # 等待微信登录成功
|
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
self._is_running = True
|
|
||||||
self.contacts = []
|
|
||||||
self.msgQ = Queue()
|
|
||||||
self._SQL_TYPES = {1: int, 2: float, 3: lambda x: x.decode("utf-8"), 4: bytes, 5: lambda x: None}
|
|
||||||
self.self_wxid = self.get_self_wxid()
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
self.cleanup()
|
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
|
||||||
"""关闭连接,回收资源"""
|
|
||||||
if not self._is_running:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.disable_recv_msg()
|
|
||||||
self.cmd_socket.close()
|
|
||||||
|
|
||||||
if self._local_mode:
|
|
||||||
cmd = fr'"{self._wcf_root}\wcf.exe" stop'
|
|
||||||
if os.system(cmd) != 0:
|
|
||||||
self.LOG.error("退出失败!")
|
|
||||||
return
|
|
||||||
self._is_running = False
|
|
||||||
|
|
||||||
def keep_running(self):
|
|
||||||
"""阻塞进程,让 RPC 一直维持连接"""
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
sleep(1)
|
|
||||||
except Exception as e:
|
|
||||||
self.cleanup()
|
|
||||||
|
|
||||||
@_retry()
|
|
||||||
def _send_request(self, req: wcf_pb2.Request) -> wcf_pb2.Response:
|
|
||||||
data = req.SerializeToString()
|
|
||||||
self.cmd_socket.send(data)
|
|
||||||
rsp = wcf_pb2.Response()
|
|
||||||
rsp.ParseFromString(self.cmd_socket.recv_msg().bytes)
|
|
||||||
return rsp
|
|
||||||
|
|
||||||
def is_receiving_msg(self) -> bool:
|
|
||||||
"""是否已启动接收消息功能"""
|
|
||||||
return self._is_receiving_msg
|
|
||||||
|
|
||||||
def is_login(self) -> bool:
|
|
||||||
"""是否已经登录"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_IS_LOGIN # FUNC_IS_LOGIN
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
|
|
||||||
return rsp.status == 1
|
|
||||||
|
|
||||||
def get_self_wxid(self) -> str:
|
|
||||||
"""获取登录账户的 wxid"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_SELF_WXID # FUNC_GET_SELF_WXID
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
|
|
||||||
return rsp.str
|
|
||||||
|
|
||||||
def get_msg_types(self) -> Dict:
|
|
||||||
"""获取所有消息类型"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_MSG_TYPES # FUNC_GET_MSG_TYPES
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
types = json_format.MessageToDict(rsp.types).get("types", {})
|
|
||||||
types = {int(k): v for k, v in types.items()}
|
|
||||||
|
|
||||||
return dict(sorted(dict(types).items()))
|
|
||||||
|
|
||||||
def get_contacts(self) -> List[Dict]:
|
|
||||||
"""获取完整通讯录"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_CONTACTS # FUNC_GET_CONTACTS
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
contacts = json_format.MessageToDict(rsp.contacts).get("contacts", [])
|
|
||||||
|
|
||||||
self.contacts.clear()
|
|
||||||
for cnt in contacts:
|
|
||||||
gender = cnt.get("gender", "")
|
|
||||||
if gender == 1:
|
|
||||||
gender = "男"
|
|
||||||
elif gender == 2:
|
|
||||||
gender = "女"
|
|
||||||
else:
|
|
||||||
gender = ""
|
|
||||||
contact = {
|
|
||||||
"wxid": cnt.get("wxid", ""),
|
|
||||||
"code": cnt.get("code", ""),
|
|
||||||
"remark": cnt.get("remark", ""),
|
|
||||||
"name": cnt.get("name", ""),
|
|
||||||
"country": cnt.get("country", ""),
|
|
||||||
"province": cnt.get("province", ""),
|
|
||||||
"city": cnt.get("city", ""),
|
|
||||||
"gender": gender}
|
|
||||||
self.contacts.append(contact)
|
|
||||||
return self.contacts
|
|
||||||
|
|
||||||
def get_dbs(self) -> List[str]:
|
|
||||||
"""获取所有数据库"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_DB_NAMES # FUNC_GET_DB_NAMES
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
dbs = json_format.MessageToDict(rsp.dbs).get("names", [])
|
|
||||||
|
|
||||||
return dbs
|
|
||||||
|
|
||||||
def get_tables(self, db: str) -> List[Dict]:
|
|
||||||
"""获取 db 中所有表
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db (str): 数据库名(可通过 `get_dbs` 查询)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Dict]: `db` 下的所有表名及对应建表语句
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_DB_TABLES # FUNC_GET_DB_TABLES
|
|
||||||
req.str = db
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
tables = json_format.MessageToDict(rsp.tables).get("tables", [])
|
|
||||||
|
|
||||||
return tables
|
|
||||||
|
|
||||||
def get_user_info(self) -> Dict:
|
|
||||||
"""获取登录账号个人信息"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_GET_USER_INFO # FUNC_GET_USER_INFO
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
ui = json_format.MessageToDict(rsp.ui)
|
|
||||||
|
|
||||||
return ui
|
|
||||||
|
|
||||||
def send_text(self, msg: str, receiver: str, aters: Optional[str] = "") -> int:
|
|
||||||
"""发送文本消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): 要发送的消息,换行使用 `\\\\n` (单杠);如果 @ 人的话,需要带上跟 `aters` 里数量相同的 @
|
|
||||||
receiver (str): 消息接收人,wxid 或者 roomid
|
|
||||||
aters (str): 要 @ 的 wxid,多个用逗号分隔;`@所有人` 只需要 `notify@all`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_SEND_TXT # FUNC_SEND_TXT
|
|
||||||
req.txt.msg = msg
|
|
||||||
req.txt.receiver = receiver
|
|
||||||
if aters:
|
|
||||||
req.txt.aters = aters
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def _download_file(self, url: str) -> str:
|
|
||||||
path = None
|
|
||||||
if not self._local_mode:
|
|
||||||
self.LOG.error(f"只有本地模式才支持网络路径!")
|
|
||||||
return path
|
|
||||||
|
|
||||||
try:
|
|
||||||
headers = {
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', }
|
|
||||||
rsp = requests.get(url, headers=headers, stream=True, timeout=60)
|
|
||||||
rsp.raw.decode_content = True
|
|
||||||
|
|
||||||
# 提取文件名
|
|
||||||
fname = os.path.basename(url)
|
|
||||||
ct = rsp.headers["content-type"]
|
|
||||||
ext = mimetypes.guess_extension(ct)
|
|
||||||
if ext:
|
|
||||||
if ext not in fname:
|
|
||||||
fname = fname + ext
|
|
||||||
else:
|
|
||||||
fname = fname.split(ext)[0] + ext
|
|
||||||
|
|
||||||
# 保存文件,用完后删除
|
|
||||||
with open(f"{self._dl_path}/{fname}", "wb") as of:
|
|
||||||
of.write(rsp.content)
|
|
||||||
|
|
||||||
path = os.path.normpath(f"{self._dl_path}/{fname}")
|
|
||||||
except Exception as e:
|
|
||||||
self.LOG.error(f"网络资源下载失败: {e}")
|
|
||||||
|
|
||||||
return path
|
|
||||||
|
|
||||||
def _process_path(self, path) -> str:
|
|
||||||
"""处理路径,如果是网络路径则下载文件
|
|
||||||
"""
|
|
||||||
if path.startswith("http"):
|
|
||||||
path = self._download_file(path)
|
|
||||||
if not path:
|
|
||||||
return -102 # 下载失败
|
|
||||||
elif not os.path.exists(path):
|
|
||||||
self.LOG.error(f"图片或者文件不存在,请检查路径: {path}")
|
|
||||||
return -101 # 文件不存在
|
|
||||||
|
|
||||||
return path
|
|
||||||
|
|
||||||
def send_image(self, path: str, receiver: str) -> int:
|
|
||||||
"""发送图片,非线程安全
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): 图片路径,如:`C:/Projs/WeChatRobot/TEQuant.jpeg` 或 `https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/TEQuant.jpg`
|
|
||||||
receiver (str): 消息接收人,wxid 或者 roomid
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 为成功,其他失败
|
|
||||||
"""
|
|
||||||
path = self._process_path(path)
|
|
||||||
if isinstance(path, int):
|
|
||||||
return path
|
|
||||||
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_SEND_IMG # FUNC_SEND_IMG
|
|
||||||
req.file.path = path
|
|
||||||
req.file.receiver = receiver
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def send_file(self, path: str, receiver: str) -> int:
|
|
||||||
"""发送文件,非线程安全
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): 本地文件路径,如:`C:/Projs/WeChatRobot/README.MD` 或 `https://raw.githubusercontent.com/lich0821/WeChatFerry/master/README.MD`
|
|
||||||
receiver (str): 消息接收人,wxid 或者 roomid
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 为成功,其他失败
|
|
||||||
"""
|
|
||||||
path = self._process_path(path)
|
|
||||||
if isinstance(path, int):
|
|
||||||
return path
|
|
||||||
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_SEND_FILE # FUNC_SEND_FILE
|
|
||||||
req.file.path = path
|
|
||||||
req.file.receiver = receiver
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def send_xml(self, receiver: str, xml: str, type: int, path: str = None) -> int:
|
|
||||||
"""发送 XML
|
|
||||||
|
|
||||||
Args:
|
|
||||||
receiver (str): 消息接收人,wxid 或者 roomid
|
|
||||||
xml (str): xml 内容
|
|
||||||
type (int): xml 类型,如:0x21 为小程序
|
|
||||||
path (str): 封面图片路径
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 为成功,其他失败
|
|
||||||
"""
|
|
||||||
raise Exception("Not implemented, yet")
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_SEND_XML # FUNC_SEND_XML
|
|
||||||
req.xml.receiver = receiver
|
|
||||||
req.xml.content = xml
|
|
||||||
req.xml.type = type
|
|
||||||
if path:
|
|
||||||
req.xml.path = path
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def send_emotion(self, path: str, receiver: str) -> int:
|
|
||||||
"""发送表情
|
|
||||||
|
|
||||||
Args:
|
|
||||||
path (str): 本地表情路径,如:`C:/Projs/WeChatRobot/emo.gif`
|
|
||||||
receiver (str): 消息接收人,wxid 或者 roomid
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 为成功,其他失败
|
|
||||||
"""
|
|
||||||
raise Exception("Not implemented, yet")
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_SEND_EMOTION # FUNC_SEND_EMOTION
|
|
||||||
req.file.path = path
|
|
||||||
req.file.receiver = receiver
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def get_msg(self, block=True) -> WxMsg:
|
|
||||||
"""从消息队列中获取消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
block (bool): 是否阻塞,默认阻塞
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
WxMsg: 微信消息
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
Empty: 如果阻塞并且超时,抛出空异常,需要用户自行捕获
|
|
||||||
"""
|
|
||||||
return self.msgQ.get(block, timeout=1)
|
|
||||||
|
|
||||||
def enable_receiving_msg(self, pyq=False) -> bool:
|
|
||||||
"""允许接收消息,成功后通过 `get_msg` 读取消息"""
|
|
||||||
def listening_msg():
|
|
||||||
rsp = wcf_pb2.Response()
|
|
||||||
self.msg_socket.dial(self.msg_url, block=True)
|
|
||||||
while self._is_receiving_msg:
|
|
||||||
try:
|
|
||||||
rsp.ParseFromString(self.msg_socket.recv_msg().bytes)
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.msgQ.put(WxMsg(rsp.wxmsg))
|
|
||||||
|
|
||||||
# 退出前关闭通信通道
|
|
||||||
self.msg_socket.close()
|
|
||||||
|
|
||||||
if self._is_receiving_msg:
|
|
||||||
return True
|
|
||||||
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_ENABLE_RECV_TXT # FUNC_ENABLE_RECV_TXT
|
|
||||||
req.flag = pyq
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
if rsp.status != 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self._is_receiving_msg = True
|
|
||||||
# 阻塞,把控制权交给用户
|
|
||||||
# self.listening_msg(callback)
|
|
||||||
|
|
||||||
# 不阻塞,启动一个新的线程来接收消息
|
|
||||||
Thread(target=listening_msg, name="GetMessage", daemon=True).start()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def enable_recv_msg(self, callback: Callable[[WxMsg], None] = None) -> bool:
|
|
||||||
"""(不建议使用)设置接收消息回调,消息量大时可能会丢失消息
|
|
||||||
|
|
||||||
.. deprecated:: 3.7.0.30.13
|
|
||||||
"""
|
|
||||||
def listening_msg():
|
|
||||||
rsp = wcf_pb2.Response()
|
|
||||||
self.msg_socket.dial(self.msg_url, block=True)
|
|
||||||
while self._is_receiving_msg:
|
|
||||||
try:
|
|
||||||
rsp.ParseFromString(self.msg_socket.recv_msg().bytes)
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
callback(WxMsg(rsp.wxmsg))
|
|
||||||
# 退出前关闭通信通道
|
|
||||||
self.msg_socket.close()
|
|
||||||
|
|
||||||
if self._is_receiving_msg:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if callback is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_ENABLE_RECV_TXT # FUNC_ENABLE_RECV_TXT
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
if rsp.status != 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self._is_receiving_msg = True
|
|
||||||
# 阻塞,把控制权交给用户
|
|
||||||
# listening_msg()
|
|
||||||
|
|
||||||
# 不阻塞,启动一个新的线程来接收消息
|
|
||||||
Thread(target=listening_msg, name="GetMessage", daemon=True).start()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def disable_recv_msg(self) -> int:
|
|
||||||
"""停止接收消息"""
|
|
||||||
if not self._is_receiving_msg:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_DISABLE_RECV_TXT # FUNC_DISABLE_RECV_TXT
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
self._is_receiving_msg = False
|
|
||||||
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def query_sql(self, db: str, sql: str) -> List[Dict]:
|
|
||||||
"""执行 SQL,如果数据量大注意分页,以免 OOM
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db (str): 要查询的数据库
|
|
||||||
sql (str): 要执行的 SQL
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Dict]: 查询结果
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_EXEC_DB_QUERY # FUNC_EXEC_DB_QUERY
|
|
||||||
req.query.db = db
|
|
||||||
req.query.sql = sql
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
rows = json_format.MessageToDict(rsp.rows).get("rows", [])
|
|
||||||
for r in rows:
|
|
||||||
row = {}
|
|
||||||
for f in r["fields"]:
|
|
||||||
c = base64.b64decode(f.get("content", ""))
|
|
||||||
row[f["column"]] = self._SQL_TYPES[f["type"]](c)
|
|
||||||
result.append(row)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def accept_new_friend(self, v3: str, v4: str, scene: int = 30) -> int:
|
|
||||||
"""通过好友申请
|
|
||||||
|
|
||||||
Args:
|
|
||||||
v3 (str): 加密用户名 (好友申请消息里 v3 开头的字符串)
|
|
||||||
v4 (str): Ticket (好友申请消息里 v4 开头的字符串)
|
|
||||||
scene: 申请方式 (好友申请消息里的 scene); 为了兼容旧接口,默认为扫码添加 (30)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 1 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_ACCEPT_FRIEND # FUNC_ACCEPT_FRIEND
|
|
||||||
req.v.v3 = v3
|
|
||||||
req.v.v4 = v4
|
|
||||||
req.v.scene = scene
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def get_friends(self) -> List[Dict]:
|
|
||||||
"""获取好友列表"""
|
|
||||||
not_friends = {
|
|
||||||
"fmessage": "朋友推荐消息",
|
|
||||||
"medianote": "语音记事本",
|
|
||||||
"floatbottle": "漂流瓶",
|
|
||||||
"filehelper": "文件传输助手",
|
|
||||||
"newsapp": "新闻",
|
|
||||||
}
|
|
||||||
friends = []
|
|
||||||
for cnt in self.get_contacts():
|
|
||||||
if (cnt["wxid"].endswith("@chatroom") or # 群聊
|
|
||||||
cnt["wxid"].startswith("gh_") or # 公众号
|
|
||||||
cnt["wxid"] in not_friends.keys() # 其他杂号
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
friends.append(cnt)
|
|
||||||
|
|
||||||
return friends
|
|
||||||
|
|
||||||
def receive_transfer(self, wxid: str, transferid: str, transactionid: str) -> int:
|
|
||||||
"""接收转账
|
|
||||||
|
|
||||||
Args:
|
|
||||||
wxid (str): 转账消息里的发送人 wxid
|
|
||||||
transferid (str): 转账消息里的 transferid
|
|
||||||
transactionid (str): 转账消息里的 transactionid
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 1 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_RECV_TRANSFER # FUNC_RECV_TRANSFER
|
|
||||||
req.tf.wxid = wxid
|
|
||||||
req.tf.tfid = transferid
|
|
||||||
req.tf.taid = transactionid
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def refresh_pyq(self, id: int = 0) -> int:
|
|
||||||
"""刷新朋友圈
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id (int): 开始 id,0 为最新页
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 1 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_REFRESH_PYQ # FUNC_REFRESH_PYQ
|
|
||||||
req.ui64 = id
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def decrypt_image(self, src: str, dst: str) -> bool:
|
|
||||||
"""解密图片:
|
|
||||||
|
|
||||||
Args:
|
|
||||||
src (str): 加密的图片路径
|
|
||||||
dst (str): 解密的图片路径
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否成功
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_DECRYPT_IMAGE # FUNC_DECRYPT_IMAGE
|
|
||||||
req.dec.src = src
|
|
||||||
req.dec.dst = dst
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status == 1
|
|
||||||
|
|
||||||
def add_chatroom_members(self, roomid: str, wxids: str) -> int:
|
|
||||||
"""添加群成员
|
|
||||||
|
|
||||||
Args:
|
|
||||||
roomid (str): 待加群的 id
|
|
||||||
wxids (str): 要加到群里的 wxid,多个用逗号分隔
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 1 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_ADD_ROOM_MEMBERS # FUNC_ADD_ROOM_MEMBERS
|
|
||||||
req.m.roomid = roomid
|
|
||||||
req.m.wxids = wxids
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def del_chatroom_members(self, roomid: str, wxids: str) -> int:
|
|
||||||
"""删除群成员
|
|
||||||
|
|
||||||
Args:
|
|
||||||
roomid (str): 群的 id
|
|
||||||
wxids (str): 要删除成员的 wxid,多个用逗号分隔
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 1 为成功,其他失败
|
|
||||||
"""
|
|
||||||
req = wcf_pb2.Request()
|
|
||||||
req.func = wcf_pb2.FUNC_DEL_ROOM_MEMBERS # FUNC_DEL_ROOM_MEMBERS
|
|
||||||
req.m.roomid = roomid
|
|
||||||
req.m.wxids = wxids.replace(" ", "")
|
|
||||||
rsp = self._send_request(req)
|
|
||||||
return rsp.status
|
|
||||||
|
|
||||||
def get_chatroom_members(self, roomid: str) -> Dict:
|
|
||||||
"""获取群成员
|
|
||||||
|
|
||||||
Args:
|
|
||||||
roomid (str): 群的 id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict: 群成员列表: {wxid1: 昵称1, wxid2: 昵称2, ...}
|
|
||||||
"""
|
|
||||||
members = {}
|
|
||||||
contacts = self.query_sql("MicroMsg.db", "SELECT UserName, NickName FROM Contact;")
|
|
||||||
contacts = {contact["UserName"]: contact["NickName"]for contact in contacts}
|
|
||||||
crs = self.query_sql("MicroMsg.db", f"SELECT RoomData FROM ChatRoom WHERE ChatRoomName = '{roomid}';")
|
|
||||||
if not crs:
|
|
||||||
return members
|
|
||||||
|
|
||||||
bs = crs[0].get("RoomData")
|
|
||||||
if not bs:
|
|
||||||
return members
|
|
||||||
|
|
||||||
crd = RoomData()
|
|
||||||
crd.ParseFromString(bs)
|
|
||||||
if not bs:
|
|
||||||
return members
|
|
||||||
|
|
||||||
for member in crd.members:
|
|
||||||
members[member.wxid] = member.name if member.name else contacts.get(member.wxid, "")
|
|
||||||
|
|
||||||
return members
|
|
||||||
|
|
||||||
def get_alias_in_chatroom(self, wxid: str, roomid: str) -> str:
|
|
||||||
"""获取群名片
|
|
||||||
|
|
||||||
Args:
|
|
||||||
wxid (str): wxid
|
|
||||||
roomid (str): 群的 id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 群名片
|
|
||||||
"""
|
|
||||||
nickname = self.query_sql("MicroMsg.db", f"SELECT NickName FROM Contact WHERE UserName = '{wxid}';")
|
|
||||||
if not nickname:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
nickname = nickname[0].get("NickName", "")
|
|
||||||
|
|
||||||
crs = self.query_sql("MicroMsg.db", f"SELECT RoomData FROM ChatRoom WHERE ChatRoomName = '{roomid}';")
|
|
||||||
if not crs:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
bs = crs[0].get("RoomData")
|
|
||||||
if not bs:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
crd = RoomData()
|
|
||||||
crd.ParseFromString(bs)
|
|
||||||
for member in crd.members:
|
|
||||||
if member.wxid == wxid:
|
|
||||||
return member.name if member.name else nickname
|
|
||||||
|
|
||||||
return ""
|
|
@ -1,11 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from wcferry.event.event import Event
|
|
||||||
from wcferry.event.core import load_function
|
|
||||||
|
|
||||||
Event = load_function(Event)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: roomdata.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eroomdata.proto\x12\x0f\x63om.iamteer.wcf\"\xf7\x01\n\x08RoomData\x12\x35\n\x07members\x18\x01 \x03(\x0b\x32$.com.iamteer.wcf.RoomData.RoomMember\x12\x0f\n\x07\x66ield_2\x18\x02 \x01(\x05\x12\x0f\n\x07\x66ield_3\x18\x03 \x01(\x05\x12\x0f\n\x07\x66ield_4\x18\x04 \x01(\x05\x12\x15\n\rroom_capacity\x18\x05 \x01(\x05\x12\x0f\n\x07\x66ield_6\x18\x06 \x01(\x05\x12\x0f\n\x07\x66ield_7\x18\x07 \x01(\x03\x12\x0f\n\x07\x66ield_8\x18\x08 \x01(\x03\x1a\x37\n\nRoomMember\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05state\x18\x03 \x01(\x05\x62\x06proto3')
|
|
||||||
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'roomdata_pb2', globals())
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
_ROOMDATA._serialized_start=36
|
|
||||||
_ROOMDATA._serialized_end=283
|
|
||||||
_ROOMDATA_ROOMMEMBER._serialized_start=228
|
|
||||||
_ROOMDATA_ROOMMEMBER._serialized_end=283
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
@ -1,74 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: wcf.proto
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xe8\x02\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.AddMembersH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x12\x0e\n\x04ui64\x18\x0c \x01(\x04H\x00\x12\x0e\n\x04\x66lag\x18\r \x01(\x08H\x00\x42\x05\n\x03msg\"\xab\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xba\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\n\n\x02id\x18\x03 \x01(\x04\x12\x0c\n\x04type\x18\x04 \x01(\r\x12\n\n\x02ts\x18\x05 \x01(\r\x12\x0e\n\x06roomid\x18\x06 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\t\x12\x0e\n\x06sender\x18\x08 \x01(\t\x12\x0c\n\x04sign\x18\t \x01(\t\x12\r\n\x05thumb\x18\n \x01(\t\x12\r\n\x05\x65xtra\x18\x0b \x01(\t\x12\x0b\n\x03xml\x18\x0c \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x05\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nAddMembers\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t*\x84\x04\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x14\n\x10\x46UNC_REFRESH_PYQ\x10S\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3')
|
|
||||||
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', globals())
|
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
DESCRIPTOR._serialized_options = b'\n\013com.iamteer'
|
|
||||||
_MSGTYPES_TYPESENTRY._options = None
|
|
||||||
_MSGTYPES_TYPESENTRY._serialized_options = b'8\001'
|
|
||||||
_FUNCTIONS._serialized_start=1878
|
|
||||||
_FUNCTIONS._serialized_end=2394
|
|
||||||
_REQUEST._serialized_start=19
|
|
||||||
_REQUEST._serialized_end=379
|
|
||||||
_RESPONSE._serialized_start=382
|
|
||||||
_RESPONSE._serialized_end=681
|
|
||||||
_EMPTY._serialized_start=683
|
|
||||||
_EMPTY._serialized_end=690
|
|
||||||
_WXMSG._serialized_start=693
|
|
||||||
_WXMSG._serialized_end=879
|
|
||||||
_TEXTMSG._serialized_start=881
|
|
||||||
_TEXTMSG._serialized_end=936
|
|
||||||
_PATHMSG._serialized_start=938
|
|
||||||
_PATHMSG._serialized_end=979
|
|
||||||
_XMLMSG._serialized_start=981
|
|
||||||
_XMLMSG._serialized_end=1052
|
|
||||||
_MSGTYPES._serialized_start=1054
|
|
||||||
_MSGTYPES._serialized_end=1151
|
|
||||||
_MSGTYPES_TYPESENTRY._serialized_start=1107
|
|
||||||
_MSGTYPES_TYPESENTRY._serialized_end=1151
|
|
||||||
_RPCCONTACT._serialized_start=1154
|
|
||||||
_RPCCONTACT._serialized_end=1289
|
|
||||||
_RPCCONTACTS._serialized_start=1291
|
|
||||||
_RPCCONTACTS._serialized_end=1339
|
|
||||||
_DBNAMES._serialized_start=1341
|
|
||||||
_DBNAMES._serialized_end=1365
|
|
||||||
_DBTABLE._serialized_start=1367
|
|
||||||
_DBTABLE._serialized_end=1403
|
|
||||||
_DBTABLES._serialized_start=1405
|
|
||||||
_DBTABLES._serialized_end=1445
|
|
||||||
_DBQUERY._serialized_start=1447
|
|
||||||
_DBQUERY._serialized_end=1481
|
|
||||||
_DBFIELD._serialized_start=1483
|
|
||||||
_DBFIELD._serialized_end=1539
|
|
||||||
_DBROW._serialized_start=1541
|
|
||||||
_DBROW._serialized_end=1578
|
|
||||||
_DBROWS._serialized_start=1580
|
|
||||||
_DBROWS._serialized_end=1614
|
|
||||||
_VERIFICATION._serialized_start=1616
|
|
||||||
_VERIFICATION._serialized_end=1669
|
|
||||||
_ADDMEMBERS._serialized_start=1671
|
|
||||||
_ADDMEMBERS._serialized_end=1714
|
|
||||||
_USERINFO._serialized_start=1716
|
|
||||||
_USERINFO._serialized_end=1784
|
|
||||||
_DECPATH._serialized_start=1786
|
|
||||||
_DECPATH._serialized_end=1821
|
|
||||||
_TRANSFER._serialized_start=1823
|
|
||||||
_TRANSFER._serialized_end=1875
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
Loading…
Reference in New Issue
Block a user