Merge branch 'lich0821:master' into master
This commit is contained in:
commit
52e822cd99
3
.gitignore
vendored
3
.gitignore
vendored
@ -34,3 +34,6 @@ rust/target/
|
||||
rust/wcferry/Cargo.lock
|
||||
rust/wcferry/.wcf.lock
|
||||
/rust/wcferry/target/CACHEDIR.TAG
|
||||
|
||||
http/dist/
|
||||
http/*.egg-info/
|
||||
|
38
README.MD
38
README.MD
@ -28,6 +28,8 @@
|
||||
|
||||
<details><summary>点击查看支持的客户端</summary>
|
||||
|
||||
* Go
|
||||
* HTTP
|
||||
* Java
|
||||
* Python
|
||||
* Rust
|
||||
@ -35,9 +37,9 @@
|
||||
|
||||

|
||||
|
||||
欢迎加群交流,后台回复 `WeChatFerry`:
|
||||
|
||||

|
||||
|||
|
||||
|:-:|:-:|
|
||||
|后台回复 `WeChatFerry` 加群交流|如果你觉得有用|
|
||||
|
||||
## 快速开始
|
||||
下载 [最新发布的版本](https://github.com/lich0821/WeChatFerry/releases/latest)。根据技术栈,选择客户端,将相应 `dll` 和 `exe` 文件放到对应位置。
|
||||
@ -45,6 +47,29 @@
|
||||
### Go
|
||||
参考 [Go README.MD](go/README.MD)
|
||||
|
||||
### HTTP
|
||||
[](https://pypi.python.org/pypi/wcfhttp)
|
||||
|
||||
* 安装
|
||||
```sh
|
||||
pip install --upgrade wcfhttp
|
||||
```
|
||||
|
||||
* 运行
|
||||
```sh
|
||||
# 查看版本
|
||||
wcfhttp -v
|
||||
|
||||
# 查看帮助
|
||||
wcfhttp -h
|
||||
|
||||
# 忽略新消息运行
|
||||
wcfhttp
|
||||
|
||||
# 新消息转发到指定地址
|
||||
wcfhttp --cb http://your_host:your_port/callback
|
||||
```
|
||||
|
||||
### Java
|
||||
参考 [Java README.MD](java/README.MD)
|
||||
|
||||
@ -95,6 +120,7 @@ vcpkg integrate install
|
||||
WeChatFerry
|
||||
├── docs # 文档
|
||||
├── go # Go 客户端
|
||||
├── http # HTTP 客户端
|
||||
├── java # Java 客户端
|
||||
├── launcher # 图形版启动器
|
||||
├── python # Python 客户端
|
||||
@ -110,6 +136,12 @@ WeChatFerry
|
||||
└── WeChatFerry.sln # VS2019 工程文件
|
||||
```
|
||||
|
||||
### go
|
||||
Go 客户端。
|
||||
|
||||
### http
|
||||
HTTP 客户端。
|
||||
|
||||
### java
|
||||
Java 客户端。
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -1,9 +0,0 @@
|
||||
# Generate all fields as pointers.
|
||||
* mangle_names:M_STRIP_PACKAGE
|
||||
* fallback_type:FT_POINTER
|
||||
MsgTypes* fallback_type:FT_CALLBACK
|
||||
RpcContact* fallback_type:FT_CALLBACK
|
||||
DbNames* fallback_type:FT_CALLBACK
|
||||
DbTable* fallback_type:FT_CALLBACK
|
||||
DbField* fallback_type:FT_CALLBACK
|
||||
DbRow* fallback_type:FT_CALLBACK
|
@ -5,112 +5,112 @@ option go_package = "./wcf";
|
||||
option java_package = "com.iamteer";
|
||||
|
||||
enum Functions {
|
||||
FUNC_RESERVED = 0x00;
|
||||
FUNC_IS_LOGIN = 0x01;
|
||||
FUNC_GET_SELF_WXID = 0x10;
|
||||
FUNC_GET_MSG_TYPES = 0x11;
|
||||
FUNC_GET_CONTACTS = 0x12;
|
||||
FUNC_GET_DB_NAMES = 0x13;
|
||||
FUNC_GET_DB_TABLES = 0x14;
|
||||
FUNC_GET_USER_INFO = 0x15;
|
||||
FUNC_SEND_TXT = 0x20;
|
||||
FUNC_SEND_IMG = 0x21;
|
||||
FUNC_SEND_FILE = 0x22;
|
||||
FUNC_SEND_XML = 0x23;
|
||||
FUNC_SEND_EMOTION = 0x24;
|
||||
FUNC_ENABLE_RECV_TXT = 0x30;
|
||||
FUNC_DISABLE_RECV_TXT = 0x40;
|
||||
FUNC_EXEC_DB_QUERY = 0x50;
|
||||
FUNC_ACCEPT_FRIEND = 0x51;
|
||||
FUNC_ADD_ROOM_MEMBERS = 0x52;
|
||||
FUNC_RECV_TRANSFER = 0x53;
|
||||
FUNC_DECRYPT_IMAGE = 0x60;
|
||||
FUNC_RESERVED = 0x00;
|
||||
FUNC_IS_LOGIN = 0x01;
|
||||
FUNC_GET_SELF_WXID = 0x10;
|
||||
FUNC_GET_MSG_TYPES = 0x11;
|
||||
FUNC_GET_CONTACTS = 0x12;
|
||||
FUNC_GET_DB_NAMES = 0x13;
|
||||
FUNC_GET_DB_TABLES = 0x14;
|
||||
FUNC_GET_USER_INFO = 0x15;
|
||||
FUNC_SEND_TXT = 0x20;
|
||||
FUNC_SEND_IMG = 0x21;
|
||||
FUNC_SEND_FILE = 0x22;
|
||||
FUNC_SEND_XML = 0x23;
|
||||
FUNC_SEND_EMOTION = 0x24;
|
||||
FUNC_ENABLE_RECV_TXT = 0x30;
|
||||
FUNC_DISABLE_RECV_TXT = 0x40;
|
||||
FUNC_EXEC_DB_QUERY = 0x50;
|
||||
FUNC_ACCEPT_FRIEND = 0x51;
|
||||
FUNC_ADD_ROOM_MEMBERS = 0x52;
|
||||
FUNC_RECV_TRANSFER = 0x53;
|
||||
FUNC_DECRYPT_IMAGE = 0x60;
|
||||
}
|
||||
|
||||
message Request
|
||||
{
|
||||
Functions func = 1;
|
||||
oneof msg
|
||||
{
|
||||
Empty empty = 2;
|
||||
string str = 3;
|
||||
TextMsg txt = 4;
|
||||
PathMsg file = 5;
|
||||
DbQuery query = 6;
|
||||
Verification v = 7;
|
||||
AddMembers m = 8;
|
||||
XmlMsg xml = 9;
|
||||
DecPath dec = 10;
|
||||
Transfer tf = 11;
|
||||
}
|
||||
Functions func = 1;
|
||||
oneof msg
|
||||
{
|
||||
Empty empty = 2;
|
||||
string str = 3;
|
||||
TextMsg txt = 4;
|
||||
PathMsg file = 5;
|
||||
DbQuery query = 6;
|
||||
Verification v = 7;
|
||||
AddMembers m = 8;
|
||||
XmlMsg xml = 9;
|
||||
DecPath dec = 10;
|
||||
Transfer tf = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message Response
|
||||
{
|
||||
Functions func = 1;
|
||||
oneof msg
|
||||
{
|
||||
int32 status = 2; // Int 状态,通用
|
||||
string str = 3; // 字符串
|
||||
WxMsg wxmsg = 4; // 微信消息
|
||||
MsgTypes types = 5; // 消息类型
|
||||
RpcContacts contacts = 6; // 联系人
|
||||
DbNames dbs = 7; // 数据库列表
|
||||
DbTables tables = 8; // 表列表
|
||||
DbRows rows = 9; // 行列表
|
||||
UserInfo ui = 10; // 个人信息
|
||||
};
|
||||
Functions func = 1;
|
||||
oneof msg
|
||||
{
|
||||
int32 status = 2; // Int 状态,通用
|
||||
string str = 3; // 字符串
|
||||
WxMsg wxmsg = 4; // 微信消息
|
||||
MsgTypes types = 5; // 消息类型
|
||||
RpcContacts contacts = 6; // 联系人
|
||||
DbNames dbs = 7; // 数据库列表
|
||||
DbTables tables = 8; // 表列表
|
||||
DbRows rows = 9; // 行列表
|
||||
UserInfo ui = 10; // 个人信息
|
||||
};
|
||||
}
|
||||
|
||||
message Empty { }
|
||||
|
||||
message WxMsg
|
||||
{
|
||||
bool is_self = 1; // 是否自己发送的
|
||||
bool is_group = 2; // 是否群消息
|
||||
int32 type = 3; // 消息类型
|
||||
string id = 4; // 消息 id
|
||||
string xml = 5; // 消息 xml
|
||||
string sender = 6; // 消息发送者
|
||||
string roomid = 7; // 群 id(如果是群消息的话)
|
||||
string content = 8; // 消息内容
|
||||
string thumb = 9; // 缩略图
|
||||
string extra = 10; // 附加内容
|
||||
bool is_self = 1; // 是否自己发送的
|
||||
bool is_group = 2; // 是否群消息
|
||||
int32 type = 3; // 消息类型
|
||||
string id = 4; // 消息 id
|
||||
string xml = 5; // 消息 xml
|
||||
string sender = 6; // 消息发送者
|
||||
string roomid = 7; // 群 id(如果是群消息的话)
|
||||
string content = 8; // 消息内容
|
||||
string thumb = 9; // 缩略图
|
||||
string extra = 10; // 附加内容
|
||||
}
|
||||
|
||||
message TextMsg
|
||||
{
|
||||
string msg = 1; // 要发送的消息内容
|
||||
string receiver = 2; // 消息接收人,当为群时可@
|
||||
string aters = 3; // 要@的人列表,逗号分隔
|
||||
string msg = 1; // 要发送的消息内容
|
||||
string receiver = 2; // 消息接收人,当为群时可@
|
||||
string aters = 3; // 要@的人列表,逗号分隔
|
||||
}
|
||||
|
||||
message PathMsg
|
||||
{
|
||||
string path = 1; // 要发送的图片的路径
|
||||
string receiver = 2; // 消息接收人
|
||||
string path = 1; // 要发送的图片的路径
|
||||
string receiver = 2; // 消息接收人
|
||||
}
|
||||
|
||||
message XmlMsg
|
||||
{
|
||||
string receiver = 1; // 消息接收人
|
||||
string content = 2; // xml 内容
|
||||
string path = 3; // 图片路径
|
||||
int32 type = 4; // 消息类型
|
||||
string receiver = 1; // 消息接收人
|
||||
string content = 2; // xml 内容
|
||||
string path = 3; // 图片路径
|
||||
int32 type = 4; // 消息类型
|
||||
}
|
||||
|
||||
message MsgTypes { map<int32, string> types = 1; }
|
||||
|
||||
message RpcContact
|
||||
{
|
||||
string wxid = 1; // 微信 id
|
||||
string code = 2; // 微信号
|
||||
string remark = 3; // 备注
|
||||
string name = 4; // 微信昵称
|
||||
string country = 5; // 国家
|
||||
string province = 6; // 省/州
|
||||
string city = 7; // 城市
|
||||
int32 gender = 8; // 性别
|
||||
string wxid = 1; // 微信 id
|
||||
string code = 2; // 微信号
|
||||
string remark = 3; // 备注
|
||||
string name = 4; // 微信昵称
|
||||
string country = 5; // 国家
|
||||
string province = 6; // 省/州
|
||||
string city = 7; // 城市
|
||||
int32 gender = 8; // 性别
|
||||
}
|
||||
message RpcContacts { repeated RpcContact contacts = 1; }
|
||||
|
||||
@ -118,54 +118,55 @@ message DbNames { repeated string names = 1; }
|
||||
|
||||
message DbTable
|
||||
{
|
||||
string name = 1; // 表名
|
||||
string sql = 2; // 建表 SQL
|
||||
string name = 1; // 表名
|
||||
string sql = 2; // 建表 SQL
|
||||
}
|
||||
message DbTables { repeated DbTable tables = 1; }
|
||||
|
||||
message DbQuery
|
||||
{
|
||||
string db = 1; // 目标数据库
|
||||
string sql = 2; // 查询 SQL
|
||||
string db = 1; // 目标数据库
|
||||
string sql = 2; // 查询 SQL
|
||||
}
|
||||
|
||||
message DbField
|
||||
{
|
||||
int32 type = 1; // 字段类型
|
||||
string column = 2; // 字段名称
|
||||
bytes content = 3; // 字段内容
|
||||
int32 type = 1; // 字段类型
|
||||
string column = 2; // 字段名称
|
||||
bytes content = 3; // 字段内容
|
||||
}
|
||||
message DbRow { repeated DbField fields = 1; }
|
||||
message DbRows { repeated DbRow rows = 1; }
|
||||
|
||||
message Verification
|
||||
{
|
||||
string v3 = 1;
|
||||
string v4 = 2;
|
||||
string v3 = 1; // 加密的用户名
|
||||
string v4 = 2; // Ticket
|
||||
int32 scene = 3; // 添加方式:17 名片,30 扫码
|
||||
}
|
||||
|
||||
message AddMembers
|
||||
{
|
||||
string roomid = 1; // 要加的群ID
|
||||
string wxids = 2; // 要加群的人列表,逗号分隔
|
||||
string roomid = 1; // 要加的群ID
|
||||
string wxids = 2; // 要加群的人列表,逗号分隔
|
||||
}
|
||||
|
||||
message UserInfo
|
||||
{
|
||||
string wxid = 1; // 微信ID
|
||||
string name = 2; // 昵称
|
||||
string mobile = 3; // 手机号
|
||||
string home = 4; // 文件/图片等父路径
|
||||
string wxid = 1; // 微信ID
|
||||
string name = 2; // 昵称
|
||||
string mobile = 3; // 手机号
|
||||
string home = 4; // 文件/图片等父路径
|
||||
}
|
||||
|
||||
message DecPath
|
||||
{
|
||||
string src = 1; // 源路径
|
||||
string dst = 2; // 目标路径
|
||||
string src = 1; // 源路径
|
||||
string dst = 2; // 目标路径
|
||||
}
|
||||
|
||||
message Transfer
|
||||
{
|
||||
string wxid = 1; // 转账人
|
||||
string tid = 2; // 转账id transferid
|
||||
string wxid = 1; // 转账人
|
||||
string tid = 2; // 转账id transferid
|
||||
}
|
||||
|
@ -132,8 +132,25 @@ func (c *Client) ExecDBQuery(db, sql string) []*DbRow {
|
||||
}
|
||||
return recv.GetRows().GetRows()
|
||||
}
|
||||
func (c *Client) AcceptFriend() int32 {
|
||||
err := c.send(genFunReq(Functions_FUNC_ACCEPT_FRIEND).build())
|
||||
|
||||
/*AcceptFriend 接收好友请求
|
||||
* 接收好友请求
|
||||
*
|
||||
* @param v3 xml.attrib["encryptusername"] // 加密的用户名
|
||||
* @param v4 xml.attrib["ticket"] // Ticket
|
||||
* @param scene 17 // 添加方式:17 名片,30 扫码
|
||||
*/
|
||||
func (c *Client) AcceptFriend(v3, v4 string, scene int32) int32 {
|
||||
req := genFunReq(Functions_FUNC_ACCEPT_FRIEND)
|
||||
q := Request_V{
|
||||
V: &Verification{
|
||||
V3: v3,
|
||||
V4: v4,
|
||||
Scene: scene,
|
||||
}}
|
||||
|
||||
req.Msg = &q
|
||||
err := c.send(req.build())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
@ -143,6 +160,59 @@ func (c *Client) AcceptFriend() int32 {
|
||||
}
|
||||
return recv.GetStatus()
|
||||
}
|
||||
func (c *Client) AddChatroomMembers(roomID, wxIDs string) int32 {
|
||||
req := genFunReq(Functions_FUNC_ADD_ROOM_MEMBERS)
|
||||
q := Request_M{
|
||||
M: &AddMembers{Roomid: roomID, Wxids: wxIDs},
|
||||
}
|
||||
req.Msg = &q
|
||||
err := c.send(req.build())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
recv, err := c.Recv()
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
return recv.GetStatus()
|
||||
}
|
||||
|
||||
// ReceiveTransfer 接收转账
|
||||
func (c *Client) ReceiveTransfer(transferId, wxID string) int32 {
|
||||
req := genFunReq(Functions_FUNC_RECV_TRANSFER)
|
||||
q := Request_Tf{
|
||||
Tf: &Transfer{Tid: transferId, Wxid: wxID},
|
||||
}
|
||||
req.Msg = &q
|
||||
err := c.send(req.build())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
recv, err := c.Recv()
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
return recv.GetStatus()
|
||||
}
|
||||
|
||||
// DecryptImage 解密图片 加密路径,解密路径
|
||||
func (c *Client) DecryptImage(src, dst string) int32 {
|
||||
req := genFunReq(Functions_FUNC_DECRYPT_IMAGE)
|
||||
q := Request_Dec{
|
||||
Dec: &DecPath{Src: src, Dst: dst},
|
||||
}
|
||||
req.Msg = &q
|
||||
err := c.send(req.build())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
recv, err := c.Recv()
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
}
|
||||
return recv.GetStatus()
|
||||
}
|
||||
|
||||
func (c *Client) AddChatRoomMembers(roomId string, wxIds []string) int32 {
|
||||
req := genFunReq(Functions_FUNC_ADD_ROOM_MEMBERS)
|
||||
q := Request_M{
|
||||
@ -292,8 +362,8 @@ func (c *Client) OnMSG(f func(msg *WxMsg)) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
socket.SetOption(mangos.OptionRecvDeadline, 2000)
|
||||
socket.SetOption(mangos.OptionSendDeadline, 2000)
|
||||
_ = socket.SetOption(mangos.OptionRecvDeadline, 2000)
|
||||
_ = socket.SetOption(mangos.OptionSendDeadline, 2000)
|
||||
err = socket.Dial(addPort(c.add))
|
||||
|
||||
if err != nil {
|
||||
@ -305,7 +375,6 @@ func (c *Client) OnMSG(f func(msg *WxMsg)) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logs.Info(len(recv))
|
||||
_ = proto.Unmarshal(recv, msg)
|
||||
go f(msg.GetWxmsg())
|
||||
}
|
||||
|
596
go/wcf/wcf.pb.go
596
go/wcf/wcf.pb.go
File diff suppressed because it is too large
Load Diff
@ -112,7 +112,7 @@ func TestAcceptFriend(t *testing.T) {
|
||||
logs.Err(err)
|
||||
return
|
||||
}
|
||||
logs.Info(wcf.AcceptFriend())
|
||||
logs.Info(wcf.AcceptFriend("encryptusername", "ticket", 17))
|
||||
}
|
||||
func TestGetUserInfo(t *testing.T) {
|
||||
wcf, err := NewWCF("")
|
||||
|
2
http/MANIFEST.in
Normal file
2
http/MANIFEST.in
Normal file
@ -0,0 +1,2 @@
|
||||
include wcferry/*.dll
|
||||
include wcferry/*.exe
|
28
http/README.MD
Normal file
28
http/README.MD
Normal file
@ -0,0 +1,28 @@
|
||||
# WeChatFerry HTTP 客户端
|
||||
[](https://pypi.python.org/pypi/wcfhttp)
|
||||
|
||||
基于 [wcferry](https://pypi.org/project/wcferry/) 封装的 HTTP 客户端。
|
||||
|
||||
## 快速开始
|
||||
### 安装
|
||||
```sh
|
||||
pip install --upgrade wcfhttp
|
||||
```
|
||||
|
||||
### 运行
|
||||
```sh
|
||||
# 查看版本
|
||||
wcfhttp -v
|
||||
|
||||
# 查看帮助
|
||||
wcfhttp -h
|
||||
|
||||
# 忽略新消息运行
|
||||
wcfhttp
|
||||
|
||||
# 新消息转发到指定地址
|
||||
wcfhttp --cb http://your_host:your_port/callback
|
||||
```
|
||||
|
||||
### 接收消息回调接口文档
|
||||
参考文档(默认地址为:http://localhost:9999/docs )`接收消息回调样例`。
|
50
http/setup.py
Normal file
50
http/setup.py
Normal file
@ -0,0 +1,50 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from __future__ import print_function
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
import wcfhttp
|
||||
|
||||
with open("README.md", "r", encoding="utf-8") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
|
||||
setup(
|
||||
name="wcfhttp",
|
||||
version=wcfhttp.core.__version__,
|
||||
author="Changhua",
|
||||
author_email="lichanghua0821@gmail.com",
|
||||
description="一个玩微信的工具",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
license="MIT",
|
||||
url="https://github.com/lich0821/WeChatFerry",
|
||||
python_requires=">=3.8",
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'wcfhttp=wcfhttp.main:main'
|
||||
]
|
||||
},
|
||||
install_requires=[
|
||||
"setuptools",
|
||||
f"wcferry=={wcfhttp.core.__version__}",
|
||||
"fastapi",
|
||||
"uvicorn[standard]"
|
||||
],
|
||||
classifiers=[
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Customer Service",
|
||||
"Topic :: Communications :: Chat",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Programming Language :: Python",
|
||||
],
|
||||
project_urls={
|
||||
"Documentation": "https://wechatferry.readthedocs.io/zh/latest/index.html",
|
||||
"GitHub": "https://github.com/lich0821/WeChatFerry/",
|
||||
},
|
||||
)
|
3
http/wcfhttp/__init__.py
Normal file
3
http/wcfhttp/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from wcfhttp.core import Http, __version__
|
225
http/wcfhttp/core.py
Normal file
225
http/wcfhttp/core.py
Normal file
@ -0,0 +1,225 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from wcferry import Wcf, WxMsg
|
||||
|
||||
__version__ = "3.7.0.30.25"
|
||||
|
||||
|
||||
class Msg(BaseModel):
|
||||
id: str
|
||||
type: int
|
||||
xml: str
|
||||
sender: str
|
||||
roomid: str
|
||||
content: str
|
||||
thumb: str
|
||||
extra: str
|
||||
is_self: bool
|
||||
is_group: bool
|
||||
|
||||
|
||||
class Http(FastAPI):
|
||||
"""WeChatFerry HTTP 客户端,文档地址:http://IP:PORT/docs"""
|
||||
|
||||
def __init__(self, wcf: Wcf, cb: str, **extra: Any) -> None:
|
||||
super().__init__(**extra)
|
||||
self.LOG = logging.getLogger(__name__)
|
||||
self.wcf = wcf
|
||||
self._set_cb(cb)
|
||||
|
||||
self.add_api_route("/login", self.is_login, methods=["GET"], summary="获取登录状态")
|
||||
self.add_api_route("/wxid", self.get_self_wxid, methods=["GET"], summary="获取登录账号 wxid")
|
||||
self.add_api_route("/user-info", self.get_user_info, methods=["GET"], summary="获取登录账号个人信息")
|
||||
self.add_api_route("/msg-types", self.get_msg_types, methods=["GET"], summary="获取消息类型")
|
||||
self.add_api_route("/contacts", self.get_contacts, methods=["GET"], summary="获取完整通讯录")
|
||||
self.add_api_route("/friends", self.get_friends, methods=["GET"], summary="获取好友列表")
|
||||
self.add_api_route("/dbs", self.get_dbs, methods=["GET"], summary="获取所有数据库")
|
||||
self.add_api_route("/{db}/tables", self.get_tables, methods=["GET"], summary="获取 db 中所有表")
|
||||
|
||||
self.add_api_route("/msg_cb", self.msg_cb, methods=["POST"], summary="接收消息回调样例")
|
||||
self.add_api_route("/text", self.send_text, methods=["POST"], summary="发送文本消息")
|
||||
self.add_api_route("/image", self.send_image, methods=["POST"], summary="发送图片消息")
|
||||
self.add_api_route("/file", self.send_file, methods=["POST"], summary="发送文件消息")
|
||||
self.add_api_route("/xml", self.send_xml, methods=["POST"], summary="发送 XML 消息")
|
||||
self.add_api_route("/emotion", self.send_emotion, methods=["POST"], summary="发送表情消息")
|
||||
self.add_api_route("/sql", self.query_sql, methods=["POST"], summary="执行 SQL,如果数据量大注意分页,以免 OOM")
|
||||
self.add_api_route("/new-friend", self.accept_new_friend, methods=["POST"], summary="通过好友申请")
|
||||
self.add_api_route("/chatroom-member", self.add_chatroom_members, methods=["POST"], summary="添加群成员")
|
||||
self.add_api_route("/transfer", self.receive_transfer, methods=["POST"], summary="接收转账")
|
||||
self.add_api_route("/dec-image", self.decrypt_image, methods=["POST"], summary="解密图片")
|
||||
|
||||
def _set_cb(self, cb):
|
||||
def callback(msg: WxMsg):
|
||||
data = {}
|
||||
data["id"] = msg.id
|
||||
data["type"] = msg.type
|
||||
data["xml"] = msg.xml
|
||||
data["sender"] = msg.sender
|
||||
data["roomid"] = msg.roomid
|
||||
data["content"] = msg.content
|
||||
data["thumb"] = msg.thumb
|
||||
data["extra"] = msg.extra
|
||||
data["is_self"] = msg.from_self()
|
||||
data["is_group"] = msg.from_group()
|
||||
|
||||
try:
|
||||
rsp = requests.post(url=cb, json=data)
|
||||
if rsp.status_code != 200:
|
||||
self.LOG.error(f"消息转发失败,HTTP 状态码为: {rsp.status_code}")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"消息转发异常: {e}")
|
||||
|
||||
if cb:
|
||||
self.LOG.info(f"消息回调: {cb}")
|
||||
self.wcf.enable_recv_msg(callback=callback)
|
||||
else:
|
||||
self.LOG.info(f"没有设置回调,打印消息")
|
||||
self.wcf.enable_recv_msg(print)
|
||||
|
||||
def is_login(self) -> dict:
|
||||
"""获取登录状态"""
|
||||
ret = self.wcf.is_login()
|
||||
return {"status": 0, "message": "成功", "data": {"login": ret}}
|
||||
|
||||
def get_self_wxid(self) -> dict:
|
||||
"""获取登录账号 wxid"""
|
||||
ret = self.wcf.get_self_wxid()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"wxid": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_msg_types(self) -> dict:
|
||||
"""获取消息类型"""
|
||||
ret = self.wcf.get_msg_types()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"types": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_contacts(self) -> dict:
|
||||
"""获取完整通讯录"""
|
||||
ret = self.wcf.get_contacts()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"contacts": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_friends(self) -> dict:
|
||||
"""获取好友列表"""
|
||||
ret = self.wcf.get_friends()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"friends": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_dbs(self) -> dict:
|
||||
"""获取所有数据库"""
|
||||
ret = self.wcf.get_dbs()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"dbs": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_tables(self, db: str) -> dict:
|
||||
"""获取 db 中所有表"""
|
||||
ret = self.wcf.get_tables(db)
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"tables": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def get_user_info(self) -> dict:
|
||||
"""获取登录账号个人信息"""
|
||||
ret = self.wcf.get_user_info()
|
||||
if ret:
|
||||
return {"status": 0, "message": "成功", "data": {"ui": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def msg_cb(self, msg: Msg):
|
||||
"""示例回调方法,简单打印消息"""
|
||||
print(f"收到消息:{msg}")
|
||||
return {"status": 0, "message": "成功"}
|
||||
|
||||
def send_text(self, msg: str = Body("消息"), receiver: str = Body("filehelper"), aters: str = Body("")) -> dict:
|
||||
"""发送文本消息,可参考:robot.py 里 sendTextMsg"""
|
||||
ret = self.wcf.send_text(msg, receiver, aters)
|
||||
return {"status": ret, "message": "成功"if ret == 0 else "失败"}
|
||||
|
||||
def send_image(self,
|
||||
path: str = Body("C:\\Projs\\WeChatRobot\\TEQuant.jpeg", description="图片路径"),
|
||||
receiver: str = Body("filehelper", description="roomid 或者 wxid")) -> dict:
|
||||
"""发送图片消息"""
|
||||
ret = self.wcf.send_image(path, receiver)
|
||||
return {"status": ret, "message": "成功"if ret == 0 else "失败"}
|
||||
|
||||
def send_file(self,
|
||||
path: str = Body("C:\\Projs\\WeChatRobot\\TEQuant.jpeg", description="本地文件路径,不支持网络路径"),
|
||||
receiver: str = Body("filehelper", description="roomid 或者 wxid")) -> dict:
|
||||
"""发送文件消息"""
|
||||
ret = self.wcf.send_file(path, receiver)
|
||||
return {"status": ret, "message": "成功"if ret == 0 else "失败"}
|
||||
|
||||
def send_xml(
|
||||
self, receiver: str = Body("filehelper", description="roomid 或者 wxid"),
|
||||
xml:
|
||||
str = Body(
|
||||
'<?xml version="1.0"?><msg><appmsg appid="" sdkver="0"><title>叮当药房,24小时服务,28分钟送药到家!</title><des>叮当快药首家承诺范围内28分钟送药到家!叮当快药核心区域内7*24小时全天候服务,送药上门!叮当快药官网为您提供快捷便利,正品低价,安全放心的购药、送药服务体验。</des><action>view</action><type>33</type></appmsg><fromusername>wxid_xxxxxxxxxxxxxx</fromusername><scene>0</scene><appinfo><version>1</version><appname /></appinfo><commenturl /></msg>',
|
||||
description="xml 内容"),
|
||||
type: int = Body(0x21, description="xml 类型,0x21 为小程序"),
|
||||
path: str = Body(None, description="封面图片路径")) -> dict:
|
||||
"""发送 XML 消息"""
|
||||
ret = self.wcf.send_xml(receiver, xml, type, path)
|
||||
return {"status": ret, "message": "成功"if ret == 0 else "失败"}
|
||||
|
||||
def send_emotion(self,
|
||||
path: str = Body("C:/Projs/WeChatRobot/emo.gif", description="本地文件路径,不支持网络路径"),
|
||||
receiver: str = Body("filehelper", description="roomid 或者 wxid")) -> dict:
|
||||
"""发送表情消息"""
|
||||
ret = self.wcf.send_emotion(path, receiver)
|
||||
return {"status": ret, "message": "成功"if ret == 0 else "失败"}
|
||||
|
||||
def query_sql(self,
|
||||
db: str = Body("MicroMsg.db", description="数据库"),
|
||||
sql: str = Body("SELECT * FROM Contact LIMIT 1;", description="SQL 语句")) -> dict:
|
||||
"""执行 SQL,如果数据量大注意分页,以免 OOM"""
|
||||
ret = self.wcf.query_sql(db, sql)
|
||||
if ret:
|
||||
for row in ret:
|
||||
for k, v in row.items():
|
||||
print(k, type(v))
|
||||
if type(v) is bytes:
|
||||
row[k] = base64.b64encode(v)
|
||||
return {"status": 0, "message": "成功", "data": {"bs64": ret}}
|
||||
return {"status": -1, "message": "失败"}
|
||||
|
||||
def accept_new_friend(self,
|
||||
v3: str = Body("v3", description="加密用户名 (好友申请消息里 v3 开头的字符串)"),
|
||||
v4: str = Body("v4", description="Ticket (好友申请消息里 v4 开头的字符串)"),
|
||||
scene: int = Body(30, description="申请方式 (好友申请消息里的 scene)")) -> dict:
|
||||
"""通过好友申请"""
|
||||
ret = self.wcf.accept_new_friend(v3, v4, scene)
|
||||
return {"status": ret, "message": "成功"if ret == 1 else "失败"}
|
||||
|
||||
def add_chatroom_members(self,
|
||||
roomid: str = Body("xxxxxxxx@chatroom", description="待加群的 id"),
|
||||
wxids: str = Body("wxid_xxxxxxxxxxxxx", description="要加到群里的 wxid,多个用逗号分隔")) -> dict:
|
||||
"""添加群成员"""
|
||||
ret = self.wcf.add_chatroom_members(roomid, wxids)
|
||||
return {"status": ret, "message": "成功"if ret == 1 else "失败"}
|
||||
|
||||
def receive_transfer(self,
|
||||
wxid: str = Body("wxid_xxxxxxxxxxxxx", description="转账消息里的发送人 wxid"),
|
||||
transferid: str = Body("transferid", description="转账消息里的 transferid")) -> dict:
|
||||
"""接收转账"""
|
||||
ret = self.wcf.receive_transfer(wxid, transferid)
|
||||
return {"status": ret, "message": "成功"if ret == 1 else "失败"}
|
||||
|
||||
def decrypt_image(self,
|
||||
src: str = Body("C:\\...", description="加密的图片路径,从图片消息中获取"),
|
||||
dst: str = Body("C:\\...", description="解密的图片路径")) -> dict:
|
||||
"""接收转账"""
|
||||
ret = self.wcf.decrypt_image(src, dst)
|
||||
return {"status": ret, "message": "成功"if ret else "失败"}
|
40
http/wcfhttp/main.py
Normal file
40
http/wcfhttp/main.py
Normal file
@ -0,0 +1,40 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
import uvicorn
|
||||
from wcferry import Wcf
|
||||
from wcfhttp import Http, __version__
|
||||
|
||||
|
||||
def main():
|
||||
parse = argparse.ArgumentParser()
|
||||
parse.add_argument("-v", "--version", action="version", version=f"{__version__}")
|
||||
parse.add_argument("--wcf_host", type=str, default=None, help="WeChatFerry 监听地址,默认本地启动监听 0.0.0.0")
|
||||
parse.add_argument("--wcf_port", type=int, default=10086, help="WeChatFerry 监听端口 (同时占用 port + 1 端口),默认 10086")
|
||||
parse.add_argument("--wcf_debug", type=bool, default=False, help="是否打开 WeChatFerry 调试开关")
|
||||
parse.add_argument("--host", type=str, default="0.0.0.0", help="wcfhttp 监听地址,默认监听 0.0.0.0")
|
||||
parse.add_argument("--port", type=int, default=9999, help="wcfhttp 监听端口,默认 9999")
|
||||
parse.add_argument("--cb", type=str, default="", help="接收消息回调地址")
|
||||
|
||||
logging.basicConfig(level="INFO", format="%(asctime)s %(message)s")
|
||||
args = parse.parse_args()
|
||||
cb = args.cb
|
||||
if not cb:
|
||||
logging.warning("没有设置接收消息回调,消息直接通过日志打印;请通过 --cb 设置消息回调")
|
||||
logging.warning(f"回调接口规范参考接收消息回调样例:http://{args.host}:{args.port}/docs")
|
||||
|
||||
wcf = Wcf(args.wcf_host, args.wcf_port, args.wcf_debug)
|
||||
home = "https://github.com/lich0821/WeChatFerry"
|
||||
http = Http(wcf=wcf,
|
||||
cb=cb,
|
||||
title="WeChatFerry HTTP 客户端",
|
||||
description=f"Github: <a href='{home}'>WeChatFerry</a>",)
|
||||
|
||||
uvicorn.run(app=http, host=args.host, port=args.port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user