feat(0): [java]-[wechat-ferry-mvn]-项目名称及目录统一调整
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
# WeChatFerry Java 客户端 maven版
|
||||
|
||||
⚠️ **只支持 Windows** ⚠️
|
||||
|
||||
## 快速使用
|
||||
|
||||
### 环境准备
|
||||
|
||||
| 名称 | 版本 |
|
||||
|-------|-----------|
|
||||
| JDK | 1.8+ |
|
||||
| Maven | 3.8+ |
|
||||
| 微信 | 3.9.10.27 |
|
||||
|
||||
### 下载文件
|
||||
|
||||
* 下载 [最新发布的文件](https://github.com/lich0821/WeChatFerry/releases/latest)
|
||||
|
||||
### 使用惯用 IDE,打开工程
|
||||
|
||||
可以直接以WeChatFerry为根目录打开
|
||||
|
||||
或者以WeChatFerry/clients/java/wechat-ferry-mvn为根目录打开
|
||||
|
||||
### 添加Maven
|
||||
|
||||
找到 WeChatFerry/clients/java/wechat-ferry-mvn/pom.xml 文件,右键添加到Maven中,会自动下载依赖
|
||||
|
||||
### 替换对应版本的dll
|
||||
|
||||
把刚下载的最新发布文件解压到本项目中的 dll 文件目录下,直接替换原因文件即可
|
||||
|
||||
### 修改配置文件
|
||||
|
||||
配置文件:src/main/resources/application.yml
|
||||
|
||||
根据自己的dll目录位置修改配置文件
|
||||
|
||||
```yaml
|
||||
# 本服务参数
|
||||
wcferry:
|
||||
# DLL文件位置
|
||||
dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll
|
||||
# socket端口
|
||||
socket-port: 10086
|
||||
```
|
||||
|
||||
### 编译运行
|
||||
|
||||
找到 src/main/java/com/wechat/ferry/WeChatFerryApplication.java 类
|
||||
|
||||
直接启动即可
|
||||
|
||||
### 访问
|
||||
|
||||
启动后springboot自身的端口为 9201 socket的端口为 10086
|
||||
|
||||
swagger地址:http://localhost:9201/swagger-ui/index.html
|
||||
|
||||
## 参与开发
|
||||
|
||||
### 核心依赖
|
||||
|
||||
| 依赖 | 版本 | 说明 |
|
||||
|---------------|--------|----------|
|
||||
| Spring Boot | 2.7.18 | 基础框架 |
|
||||
| protobuf-java | 3.22.2 | rpc |
|
||||
| jna | 5.6.0 | 态访问系统本地库 |
|
||||
| nng-java | 1.4.0 | 本地包 |
|
||||
|
||||
### 模块结构
|
||||
|
||||
```
|
||||
wechat-ferry-mvn
|
||||
├─dll 核心dll
|
||||
│ ├─sdk.dll sdk文件
|
||||
│ └─readme.txt 本目录说明文件
|
||||
│
|
||||
├─src 源
|
||||
│ ├─main 重启命令
|
||||
│ │ ├─java(com.wechat.ferry) java代码目录
|
||||
│ │ │ ├─config 配置
|
||||
│ │ │ ├─entity 聚合模型
|
||||
│ │ │ │ ├─dto DTO模型
|
||||
│ │ │ │ ├─po 实体模型
|
||||
│ │ │ │ └─vo 视图层返回体目录
|
||||
│ │ │ ├─enums 枚举
|
||||
│ │ │ ├─handle 处理层
|
||||
│ │ │ ├─service 业务接口
|
||||
│ │ │ │ └─impl 业务实现类
|
||||
│ │ │ ├─utils 工具类
|
||||
│ │ │ └─WcferryApplication.java 启动类
|
||||
│ │ │
|
||||
│ │ │resources 资源目录
|
||||
│ │ │ ├─libs 本程序内置依赖包
|
||||
│ │ │ ├─proto proto文件(此目录打包将被排除)
|
||||
│ │ │ ├─win32-x86-64 依赖程序
|
||||
│ │ │ ├─application.yml 本程序主配置文件
|
||||
│ │ │ └─logback-spring.xml 日志配置文件
|
||||
│
|
||||
├─pom.xml POM文件
|
||||
├─README.MD 说明文件
|
||||
│
|
||||
|
||||
```
|
||||
|
||||
### 配置说明
|
||||
|
||||
本程序主配置文件为 application.yml
|
||||
|
||||
#### 配置参数
|
||||
|
||||
本程序内置参数统一前缀:wechat.ferry 所有自定义本服务的参数请都放置在此前缀下,如:
|
||||
|
||||
```ymal
|
||||
wechat:
|
||||
ferry:
|
||||
# DLL文件位置
|
||||
dll-path: /dll/sdk.dll
|
||||
```
|
||||
|
||||
#### 生成proto文件
|
||||
|
||||
本程序已经集成了生成proto文件的maven插件,直接install即可生成proto文件,且会在打包程序中去除 src/main/resources/proto
|
||||
下面的内容,只保留实体类中的文件
|
||||
|
||||
默认install会重新根据.proto文件重新生成实体,如果不想被替换,请删除 src/main/resources/proto 下对应的.proto文件即可
|
||||
|
||||
### 提交规范
|
||||
|
||||
本模块希望大家使用统一提交格式,便于区分
|
||||
|
||||
格式:类型(任务号/缺陷号/没有使用0替代): [模块名称]-[子模块名称]-本次修改的说明
|
||||
|
||||
如:
|
||||
|
||||
```cmd
|
||||
feat(0): [java]-[wechat-ferry-mvn]-基础类目录划分迁移及代码格式
|
||||
```
|
||||
|
||||
| 名称 | 版本 |
|
||||
|------|--------------|
|
||||
| feat | 新功能 |
|
||||
| fix | 缺陷 |
|
||||
| ... | 其他等git规范中的均可 |
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
@@ -0,0 +1,6 @@
|
||||
说明:
|
||||
由于项目规范限制,本目录保留,但最新的DLL还需要各位自行去下载解压
|
||||
请把最新的 https://github.com/lich0821/WeChatFerry/releases/latest 下载的文件解压后放到该目录下
|
||||
sdk.dll
|
||||
spy.dll
|
||||
spy_debug.dll
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.18</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>wechat-ferry-mvn</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>wechat-ferry-mvn</name>
|
||||
<description>WeChatFerry客户端Java-Maven版</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 日志 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- swagger3包-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<!-- Alibaba Fastjson -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.52</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
<version>2.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.22.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 引入本地lib -->
|
||||
<dependency>
|
||||
<groupId>io.sisu.nng</groupId>
|
||||
<artifactId>nng-java</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<!-- system表示依赖不是由maven仓库,而是本地的jar包 -->
|
||||
<scope>system</scope>
|
||||
<!-- jar包位置 -->
|
||||
<systemPath>${project.basedir}/src/main/resources/libs/nng-java-1.4.0-SNAPSHOT.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.5.0.Final</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
|
||||
<!-- 同时添加maven插件,用于编译protobuf生成java文件 -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<configuration>
|
||||
<!-- 注意,需要与 com.google.protobuf:protobuf-java 一致 -->
|
||||
<protocArtifact>
|
||||
com.google.protobuf:protoc:3.22.2:exe:${os.detected.classifier}
|
||||
</protocArtifact>
|
||||
<!-- 默认值,proto源文件路径 -->
|
||||
<protoSourceRoot>${project.basedir}/src/main/resources/proto</protoSourceRoot>
|
||||
<!-- 默认值,proto目标java文件路径,如果不指定则只在jar与编译中生成 -->
|
||||
<outputDirectory>src/main/java</outputDirectory>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<!-- 设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件 -->
|
||||
<clearOutputDirectory>false</clearOutputDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/*.proto</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.wechat.ferry;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-21 12:19
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class WeChatFerryApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WeChatFerryApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.wechat.ferry.config;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* 配置类-protobuf
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-26 21:35
|
||||
*/
|
||||
@Configuration
|
||||
public class ProtobufConfig {
|
||||
|
||||
/**
|
||||
* protobuf 序列化
|
||||
*/
|
||||
@Bean
|
||||
ProtobufHttpMessageConverter protobufHttpMessageConverter() {
|
||||
return new ProtobufHttpMessageConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* protobuf 反序列化
|
||||
*/
|
||||
@Bean
|
||||
RestTemplate restTemplate(ProtobufHttpMessageConverter protobufHttpMessageConverter) {
|
||||
return new RestTemplate(Collections.singletonList(protobufHttpMessageConverter));
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.wechat.ferry.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.oas.annotations.EnableOpenApi;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
/**
|
||||
* 配置类-swagger
|
||||
* http://localhost:9201/swagger-ui/index.html
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-24 22:13
|
||||
*/
|
||||
@EnableOpenApi
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public Docket api() {
|
||||
return new Docket(DocumentationType.SWAGGER_2).select()
|
||||
// 替换为您的Controller所在的包路径
|
||||
.apis(RequestHandlerSelectors.basePackage("com.wechat.ferry.controller"))
|
||||
// 地址
|
||||
.paths(PathSelectors.any()).build().apiInfo(apiInfo());
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
// 文档标题
|
||||
.title("WeChatFerry接口文档")
|
||||
// 文档路径
|
||||
.description("微信机器人底层框架接口文档")
|
||||
// 文档版本
|
||||
.version("1.0.0").build();
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
package com.wechat.ferry.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 配置文件-WeChatFerry的配置文件
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-21 21:35
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "wechat.ferry")
|
||||
public class WeChatFerryProperties {
|
||||
|
||||
/**
|
||||
* dll文件位置
|
||||
*/
|
||||
private String dllPath;
|
||||
|
||||
/**
|
||||
* socket端口
|
||||
*/
|
||||
private Integer socketPort;
|
||||
|
||||
}
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
package com.wechat.ferry.config;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.wechat.ferry.handle.WechatSocketClient;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 配置类-注入微信客户端
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-30 12:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class WechatConfiguration {
|
||||
|
||||
@Resource
|
||||
private WeChatFerryProperties properties;
|
||||
|
||||
@Bean
|
||||
public WechatSocketClient client() {
|
||||
log.debug("测试:端口:{},地址:{}", properties.getSocketPort(), properties.getDllPath());
|
||||
// 连接远程 RPC
|
||||
// Client client = new Client("127.0.0.1", 10086);
|
||||
|
||||
// 本地启动 RPC
|
||||
// Client client = new Client(); // 默认 10086 端口
|
||||
// Client client = new Client(10088,true); // 也可以指定端口
|
||||
WechatSocketClient wechatSocketClient = new WechatSocketClient(properties.getSocketPort(), properties.getDllPath());
|
||||
|
||||
// 是否已登录
|
||||
// log.info("isLogin: {}", client.isLogin());
|
||||
|
||||
// 登录账号 wxid
|
||||
// log.info("wxid: {}", client.getSelfWxid());
|
||||
|
||||
// 消息类型
|
||||
// log.info("message types: {}", client.getMsgTypes());
|
||||
|
||||
// 所有联系人(包括群聊、公众号、好友……)
|
||||
// client.printContacts(client.getContacts());
|
||||
|
||||
// 获取数据库
|
||||
log.info("dbs: {}", wechatSocketClient.getDbNames());
|
||||
|
||||
// 获取数据库下的表
|
||||
String db = "MicroMsg.db";
|
||||
// log.info("tables in {}: {}", db, client.getDbTables(db));
|
||||
|
||||
// 发送文本消息,aters 是要 @ 的 wxid,多个用逗号分隔;消息里@的数量要与aters里的数量对应
|
||||
// client.sendText("Hello", "filehelper", "");
|
||||
// client.sendText("Hello @某人1 @某人2", "xxxxxxxx@chatroom", "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2");
|
||||
|
||||
// 发送图片消息,图片必须要存在
|
||||
// client.sendImage("C:\\Projs\\WeChatFerry\\TEQuant.jpeg", "filehelper");
|
||||
|
||||
// 发送文件消息,文件必须要存在
|
||||
// client.sendFile("C:\\Projs\\WeChatFerry\\README.MD", "filehelper");
|
||||
|
||||
String xml =
|
||||
"<?xml version=\"1.0\"?><msg><appmsg appid=\"\" sdkver=\"0\"><title>叮当药房,24小时服务,28分钟送药到家!</title><des>叮当快药首家承诺范围内28分钟送药到家!叮当快药核心区域内7*24小时全天候服务,送药上门!叮当快药官网为您提供快捷便利,正品低价,安全放心的购药、送药服务体验。</des><action>view</action><type>33</type><showtype>0</showtype><content /><url>https://mp.weixin.qq.com/mp/waerrpage?appid=wxc2edadc87077fa2a&type=upgrade&upgradetype=3#wechat_redirect</url><dataurl /><lowurl /><lowdataurl /><recorditem /><thumburl /><messageaction /><md5>7f6f49d301ebf47100199b8a4fcf4de4</md5><extinfo /><sourceusername>gh_c2b88a38c424@app</sourceusername><sourcedisplayname>叮当快药 药店送药到家夜间买药</sourcedisplayname><commenturl /><appattach><totallen>0</totallen><attachid /><emoticonmd5></emoticonmd5><fileext>jpg</fileext><filekey>da0e08f5c7259d03da150d5e7ca6d950</filekey><cdnthumburl>3057020100044b30490201000204e4c0232702032f4ef20204a6bace6f02046401f62d042430326337303430352d333734332d343362652d623335322d6233333566623266376334620204012400030201000405004c537600</cdnthumburl><aeskey>0db26456caf243fbd4efb99058a01d66</aeskey><cdnthumbaeskey>0db26456caf243fbd4efb99058a01d66</cdnthumbaeskey><encryver>1</encryver><cdnthumblength>61558</cdnthumblength><cdnthumbheight>100</cdnthumbheight><cdnthumbwidth>100</cdnthumbwidth></appattach><weappinfo><pagepath>pages/index/index.html</pagepath><username>gh_c2b88a38c424@app</username><appid>wxc2edadc87077fa2a</appid><version>197</version><type>2</type><weappiconurl>http://wx.qlogo.cn/mmhead/Q3auHgzwzM4727n0NQ0ZIPQPlfp15m1WLsnrXbo1kLhFGcolgLyc0A/96</weappiconurl><appservicetype>0</appservicetype><shareId>1_wxc2edadc87077fa2a_29177e9a9b918cb9e75964f80bb8f32e_1677849476_0</shareId></weappinfo><websearch /></appmsg><fromusername>wxid_eob5qfcrv4zd22</fromusername><scene>0</scene><appinfo><version>1</version><appname /></appinfo><commenturl /></msg>";
|
||||
// client.sendXml("filehelper", xml, "", 0x21);
|
||||
|
||||
// 发送表情消息,gif 必须要存在
|
||||
// client.sendEmotion("C:\\Projs\\WeChatFerry\\emo.gif", "filehelper");
|
||||
|
||||
// 接收消息,并调用 printWxMsg 处理
|
||||
wechatSocketClient.enableRecvMsg(100);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
while (wechatSocketClient.getIsReceivingMsg()) {
|
||||
wechatSocketClient.printWxMsg(wechatSocketClient.getMsg());
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
// client.diableRecvMsg(); // 需要停止时调用
|
||||
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
wechatSocketClient.keepRunning();
|
||||
}
|
||||
}).start();
|
||||
|
||||
return wechatSocketClient;
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
package com.wechat.ferry.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.wechat.ferry.entity.TResponse;
|
||||
import com.wechat.ferry.enums.ResponseCodeEnum;
|
||||
import com.wechat.ferry.service.TestService;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/test")
|
||||
@Api(tags = "测试-接口")
|
||||
public class TestController {
|
||||
|
||||
private TestService testService;
|
||||
|
||||
@Autowired
|
||||
public void setTestService(TestService testService) {
|
||||
this.testService = testService;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "测试", notes = "login")
|
||||
@PostMapping(value = "/login")
|
||||
public TResponse<Object> login() {
|
||||
Boolean flag = testService.isLogin();
|
||||
return TResponse.ok(ResponseCodeEnum.SUCCESS, flag);
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.wechat.ferry.entity;
|
||||
|
||||
/**
|
||||
* 返回类接口
|
||||
*/
|
||||
public interface IResponse {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
String getCode();
|
||||
|
||||
/**
|
||||
* 返回信息
|
||||
*/
|
||||
String getMsg();
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.wechat.ferry.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.wechat.ferry.enums.ResponseCodeEnum;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 返回类封装
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class TResponse<T> {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
@ApiModelProperty(value = "状态码")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 返回信息
|
||||
*/
|
||||
@ApiModelProperty(value = "返回信息")
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 响应时间
|
||||
*/
|
||||
@ApiModelProperty(value = "响应时间")
|
||||
private String time;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
@ApiModelProperty(value = "响应数据")
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 返回类
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2023/4/5 11:31
|
||||
* @param t 返回码类
|
||||
* @param data 返回数据
|
||||
* @return TResponse对象
|
||||
*/
|
||||
public TResponse(IResponse t, T data) {
|
||||
this(t);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回类
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2023/4/5 11:31
|
||||
* @param t 返回码类
|
||||
* @param msg 返回信息
|
||||
* @return TResponse对象
|
||||
*/
|
||||
public TResponse(IResponse t, String msg) {
|
||||
this.code = t.getCode();
|
||||
this.msg = msg;
|
||||
this.time = LocalDateTime.now().format(FORMATTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回类
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2023/4/5 11:31
|
||||
* @param t 返回码类
|
||||
* @param msg 返回信息
|
||||
* @return TResponse对象
|
||||
*/
|
||||
public TResponse(IResponse t, T data, String msg) {
|
||||
this(t, data);
|
||||
// 重写返回信息-替换默认的信息
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public TResponse(IResponse t) {
|
||||
this.code = t.getCode();
|
||||
this.msg = t.getMsg();
|
||||
this.time = LocalDateTime.now().format(FORMATTER);
|
||||
}
|
||||
|
||||
public static <T> TResponse<T> ok(IResponse t) {
|
||||
return new TResponse<>(t);
|
||||
}
|
||||
|
||||
public static <T> TResponse<T> ok(IResponse t, T data) {
|
||||
return new TResponse<>(t, data);
|
||||
}
|
||||
|
||||
public static <T> TResponse<T> fail(String msg) {
|
||||
return new TResponse<>(ResponseCodeEnum.FAILED, msg);
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
+31905
File diff suppressed because it is too large
Load Diff
+3
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
package com.wechat.ferry.entity.vo.response;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO-微信消息
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-26 19:56
|
||||
*/
|
||||
@Data
|
||||
public class WxMsgResp {
|
||||
|
||||
/**
|
||||
* 是否自己发送的
|
||||
*/
|
||||
@ApiModelProperty(value = "是否自己发送的")
|
||||
private Boolean isSelf;
|
||||
|
||||
/**
|
||||
* 是否群消息
|
||||
*/
|
||||
@ApiModelProperty(value = "是否群消息")
|
||||
private Boolean isGroup;
|
||||
|
||||
/**
|
||||
* 消息id
|
||||
*/
|
||||
@ApiModelProperty(value = "消息id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@ApiModelProperty(value = "消息类型")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@ApiModelProperty(value = "消息类型")
|
||||
private Integer ts;
|
||||
|
||||
/**
|
||||
* 群id(如果是群消息的话)
|
||||
*/
|
||||
@ApiModelProperty(value = "群id(如果是群消息的话)")
|
||||
private String roomId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@ApiModelProperty(value = "消息内容")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息发送者
|
||||
*/
|
||||
@ApiModelProperty(value = "消息发送者")
|
||||
private String sender;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
@ApiModelProperty(value = "签名")
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 缩略图
|
||||
*/
|
||||
@ApiModelProperty(value = "缩略图")
|
||||
private String thumb;
|
||||
|
||||
/**
|
||||
* 附加内容
|
||||
*/
|
||||
@ApiModelProperty(value = "附加内容")
|
||||
private String extra;
|
||||
|
||||
/**
|
||||
* 消息xml
|
||||
*/
|
||||
@ApiModelProperty(value = "消息xml")
|
||||
private String xml;
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.wechat.ferry.enums;
|
||||
|
||||
import com.wechat.ferry.entity.IResponse;
|
||||
|
||||
/**
|
||||
* 枚举-返回类状态码
|
||||
*/
|
||||
public enum ResponseCodeEnum implements IResponse {
|
||||
|
||||
/**
|
||||
* 成功-200
|
||||
*/
|
||||
SUCCESS("200", "请求成功"),
|
||||
|
||||
/**
|
||||
* 参数错误-400
|
||||
*/
|
||||
PARAM_ERROR("400", "参数错误"),
|
||||
|
||||
/**
|
||||
* 401-身份验证失败
|
||||
*/
|
||||
NO_AUTH("401", "身份验证失败"),
|
||||
|
||||
/**
|
||||
* 403-您无权访问此资源
|
||||
*/
|
||||
UNAUTHORIZED("403", "您无权访问此资源"),
|
||||
|
||||
/**
|
||||
* 404-未找到该资源
|
||||
*/
|
||||
NOT_FOUND("404", "未找到该资源"),
|
||||
|
||||
/**
|
||||
* 失败-500
|
||||
*/
|
||||
FAILED("500", "请求失败"),
|
||||
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String msg;
|
||||
|
||||
ResponseCodeEnum(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name() + "{" + code + '|' + msg + "}";
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+554
@@ -0,0 +1,554 @@
|
||||
package com.wechat.ferry.handle;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.wechat.ferry.entity.po.Wcf;
|
||||
import com.wechat.ferry.entity.po.Wcf.DbQuery;
|
||||
import com.wechat.ferry.entity.po.Wcf.DbRow;
|
||||
import com.wechat.ferry.entity.po.Wcf.DbTable;
|
||||
import com.wechat.ferry.entity.po.Wcf.DecPath;
|
||||
import com.wechat.ferry.entity.po.Wcf.Functions;
|
||||
import com.wechat.ferry.entity.po.Wcf.MemberMgmt;
|
||||
import com.wechat.ferry.entity.po.Wcf.Request;
|
||||
import com.wechat.ferry.entity.po.Wcf.Response;
|
||||
import com.wechat.ferry.entity.po.Wcf.RpcContact;
|
||||
import com.wechat.ferry.entity.po.Wcf.UserInfo;
|
||||
import com.wechat.ferry.entity.po.Wcf.Verification;
|
||||
import com.wechat.ferry.entity.po.Wcf.WxMsg;
|
||||
import com.wechat.ferry.entity.vo.response.WxMsgResp;
|
||||
import com.wechat.ferry.service.SDK;
|
||||
import com.sun.jna.Native;
|
||||
|
||||
import io.sisu.nng.Socket;
|
||||
import io.sisu.nng.pair.Pair1Socket;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class WechatSocketClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WechatSocketClient.class);
|
||||
private static final int BUFFER_SIZE = 16 * 1024 * 1024; // 16M
|
||||
private Socket cmdSocket = null;
|
||||
private Socket msgSocket = null;
|
||||
private static String DEFAULT_HOST = "127.0.0.1";
|
||||
private static int PORT = 10086;
|
||||
private static String CMDURL = "tcp://%s:%s";
|
||||
private static String DEFAULT_DLL_PATH = System.getProperty("user.dir") + "\\dll\\sdk.dll";
|
||||
private boolean isReceivingMsg = false;
|
||||
private boolean isLocalHostPort = false;
|
||||
private BlockingQueue<WxMsg> msgQ;
|
||||
|
||||
private String host;
|
||||
private int port;
|
||||
private String dllPath;
|
||||
|
||||
public WechatSocketClient() {
|
||||
this(DEFAULT_HOST, PORT, false, DEFAULT_DLL_PATH);
|
||||
}
|
||||
|
||||
public WechatSocketClient(int port, String dllPath) {
|
||||
this(DEFAULT_HOST, port, false, dllPath);
|
||||
}
|
||||
|
||||
public WechatSocketClient(String host, int port, boolean debug, String dllPath) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.dllPath = dllPath;
|
||||
|
||||
SDK INSTANCE = Native.load(dllPath, SDK.class);
|
||||
int status = INSTANCE.WxInitSDK(debug, port);
|
||||
if (status != 0) {
|
||||
logger.error("启动 RPC 失败: {}", status);
|
||||
System.exit(-1);
|
||||
}
|
||||
connectRPC(String.format(CMDURL, host, port), INSTANCE);
|
||||
if (DEFAULT_HOST.equals(host) || "localhost".equalsIgnoreCase(host)) {
|
||||
isLocalHostPort = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void connectRPC(String url, SDK INSTANCE) {
|
||||
try {
|
||||
cmdSocket = new Pair1Socket();
|
||||
cmdSocket.dial(url);
|
||||
// logger.info("请点击登录微信");
|
||||
while (!isLogin()) {
|
||||
// 直到登录成功
|
||||
waitMs(1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("连接 RPC 失败: ", e);
|
||||
System.exit(-1);
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
logger.info("关闭...");
|
||||
diableRecvMsg();
|
||||
if (isLocalHostPort) {
|
||||
INSTANCE.WxDestroySDK();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private Response sendCmd(Request req) {
|
||||
try {
|
||||
ByteBuffer bb = ByteBuffer.wrap(req.toByteArray());
|
||||
cmdSocket.send(bb);
|
||||
ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE);
|
||||
long size = cmdSocket.receive(ret, true);
|
||||
return Response.parseFrom(Arrays.copyOfRange(ret.array(), 0, (int)size));
|
||||
} catch (Exception e) {
|
||||
logger.error("命令调用失败: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前微信客户端是否登录微信号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isLogin() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_IS_LOGIN_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getStatus() == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得微信客户端登录的微信ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getSelfWxid() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_SELF_WXID_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getStr();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有消息类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<Integer, String> getMsgTypes() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_MSG_TYPES_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getTypes().getTypesMap();
|
||||
}
|
||||
|
||||
return Wcf.MsgTypes.newBuilder().build().getTypesMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有联系人
|
||||
* "fmessage": "朋友推荐消息",
|
||||
* "medianote": "语音记事本",
|
||||
* "floatbottle": "漂流瓶",
|
||||
* "filehelper": "文件传输助手",
|
||||
* "newsapp": "新闻",
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<RpcContact> getContacts() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_CONTACTS_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getContacts().getContactsList();
|
||||
}
|
||||
|
||||
return Wcf.RpcContacts.newBuilder().build().getContactsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sql执行结果
|
||||
*
|
||||
* @param db 数据库名
|
||||
* @param sql 执行的sql语句
|
||||
* @return
|
||||
*/
|
||||
public List<DbRow> querySql(String db, String sql) {
|
||||
DbQuery dbQuery = DbQuery.newBuilder().setSql(sql).setDb(db).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_EXEC_DB_QUERY_VALUE).setQuery(dbQuery).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getRows().getRowsList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有数据库名
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getDbNames() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_NAMES_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getDbs().getNamesList();
|
||||
}
|
||||
|
||||
return Wcf.DbNames.newBuilder().build().getNamesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定数据库中的所有表
|
||||
*
|
||||
* @param db
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getDbTables(String db) {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_TABLES_VALUE).setStr(db).build();
|
||||
Response rsp = sendCmd(req);
|
||||
Map<String, String> tables = new HashMap<>();
|
||||
if (rsp != null) {
|
||||
for (DbTable tbl : rsp.getTables().getTablesList()) {
|
||||
tables.put(tbl.getName(), tbl.getSql());
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param msg: 消息内容(如果是 @ 消息则需要有跟 @ 的人数量相同的 @)
|
||||
* @param receiver: 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx),群聊为
|
||||
* roomid(xxxxxxxxxx@chatroom)
|
||||
* @param aters: 群聊时要 @ 的人(私聊时为空字符串),多个用逗号分隔。@所有人 用
|
||||
* notify@all(必须是群主或者管理员才有权限)
|
||||
* @return int
|
||||
* @Description 发送文本消息
|
||||
* @author Changhua
|
||||
* @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ",
|
||||
* "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2");
|
||||
**/
|
||||
public int sendText(String msg, String receiver, String aters) {
|
||||
Wcf.TextMsg textMsg = Wcf.TextMsg.newBuilder().setMsg(msg).setReceiver(receiver).setAters(aters).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_TXT_VALUE).setTxt(textMsg).build();
|
||||
logger.debug("sendText: {}", bytesToHex(req.toByteArray()));
|
||||
Response rsp = sendCmd(req);
|
||||
int ret = -1;
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送图片消息
|
||||
*
|
||||
* @param path 图片地址
|
||||
* @param receiver 接收者微信id
|
||||
* @return 发送结果状态码
|
||||
*/
|
||||
public int sendImage(String path, String receiver) {
|
||||
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_IMG_VALUE).setFile(pathMsg).build();
|
||||
logger.debug("sendImage: {}", bytesToHex(req.toByteArray()));
|
||||
Response rsp = sendCmd(req);
|
||||
int ret = -1;
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文件消息
|
||||
*
|
||||
* @param path 文件地址
|
||||
* @param receiver 接收者微信id
|
||||
* @return 发送结果状态码
|
||||
*/
|
||||
public int sendFile(String path, String receiver) {
|
||||
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_FILE_VALUE).setFile(pathMsg).build();
|
||||
logger.debug("sendFile: {}", bytesToHex(req.toByteArray()));
|
||||
Response rsp = sendCmd(req);
|
||||
int ret = -1;
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送Xml消息
|
||||
*
|
||||
* @param receiver 接收者微信id
|
||||
* @param xml xml内容
|
||||
* @param path
|
||||
* @param type
|
||||
* @return 发送结果状态码
|
||||
*/
|
||||
public int sendXml(String receiver, String xml, String path, int type) {
|
||||
Wcf.XmlMsg xmlMsg = Wcf.XmlMsg.newBuilder().setContent(xml).setReceiver(receiver).setPath(path).setType(type).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_XML_VALUE).setXml(xmlMsg).build();
|
||||
logger.debug("sendXml: {}", bytesToHex(req.toByteArray()));
|
||||
Response rsp = sendCmd(req);
|
||||
int ret = -1;
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送表情消息
|
||||
*
|
||||
* @param path 表情路径
|
||||
* @param receiver 消息接收者
|
||||
* @return 发送结果状态码
|
||||
*/
|
||||
public int sendEmotion(String path, String receiver) {
|
||||
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_EMOTION_VALUE).setFile(pathMsg).build();
|
||||
logger.debug("sendEmotion: {}", bytesToHex(req.toByteArray()));
|
||||
Response rsp = sendCmd(req);
|
||||
int ret = -1;
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收好友请求
|
||||
*
|
||||
* @param v3 xml.attrib["encryptusername"]
|
||||
* @param v4 xml.attrib["ticket"]
|
||||
* @return 结果状态码
|
||||
*/
|
||||
public int acceptNewFriend(String v3, String v4) {
|
||||
int ret = -1;
|
||||
Verification verification = Verification.newBuilder().setV3(v3).setV4(v4).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ACCEPT_FRIEND_VALUE).setV(verification).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加群成员为微信好友
|
||||
*
|
||||
* @param roomID 群ID
|
||||
* @param wxIds 要加群的人列表,逗号分隔
|
||||
* @return 1 为成功,其他失败
|
||||
*/
|
||||
public int addChatroomMembers(String roomID, String wxIds) {
|
||||
int ret = -1;
|
||||
MemberMgmt memberMgmt = MemberMgmt.newBuilder().setRoomid(roomID).setWxids(wxIds).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ADD_ROOM_MEMBERS_VALUE).setM(memberMgmt).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密图片
|
||||
*
|
||||
* @param srcPath 加密的图片路径
|
||||
* @param dstPath 解密的图片路径
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean decryptImage(String srcPath, String dstPath) {
|
||||
int ret = -1;
|
||||
DecPath build = DecPath.newBuilder().setSrc(srcPath).setDst(dstPath).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
}
|
||||
return ret == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取个人信息
|
||||
*
|
||||
* @return 个人信息
|
||||
*/
|
||||
public UserInfo getUserInfo() {
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_USER_INFO_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
return rsp.getUi();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getIsReceivingMsg() {
|
||||
return isReceivingMsg;
|
||||
}
|
||||
|
||||
public WxMsg getMsg() {
|
||||
try {
|
||||
return msgQ.take();
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是艾特自己的消息
|
||||
*
|
||||
* @param wxMsgXml
|
||||
* @param wxMsgContent
|
||||
* @return
|
||||
*/
|
||||
public boolean isAtMeMsg(String wxMsgXml, String wxMsgContent) {
|
||||
String format = String.format("<atuserlist><![CDATA[%s]]></atuserlist>", getSelfWxid());
|
||||
boolean isAtAll = wxMsgContent.startsWith("@所有人") || wxMsgContent.startsWith("@all");
|
||||
if (wxMsgXml.contains(format) && !isAtAll) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void listenMsg(String url) {
|
||||
try {
|
||||
msgSocket = new Pair1Socket();
|
||||
msgSocket.dial(url);
|
||||
msgSocket.setReceiveTimeout(2000); // 2 秒超时
|
||||
} catch (Exception e) {
|
||||
logger.error("创建消息 RPC 失败", e);
|
||||
return;
|
||||
}
|
||||
ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);
|
||||
while (isReceivingMsg) {
|
||||
try {
|
||||
long size = msgSocket.receive(bb, true);
|
||||
WxMsg wxMsg = Response.parseFrom(Arrays.copyOfRange(bb.array(), 0, (int)size)).getWxmsg();
|
||||
msgQ.put(wxMsg);
|
||||
} catch (Exception e) {
|
||||
// 多半是超时,忽略吧
|
||||
}
|
||||
}
|
||||
try {
|
||||
msgSocket.close();
|
||||
} catch (Exception e) {
|
||||
logger.error("关闭连接失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableRecvMsg(int qSize) {
|
||||
if (isReceivingMsg) {
|
||||
return;
|
||||
}
|
||||
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ENABLE_RECV_TXT_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp == null) {
|
||||
logger.error("启动消息接收失败");
|
||||
isReceivingMsg = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isReceivingMsg = true;
|
||||
msgQ = new ArrayBlockingQueue<>(qSize);
|
||||
String msgUrl = "tcp://" + this.host + ":" + (this.port + 1);
|
||||
Thread thread = new Thread(() -> listenMsg(msgUrl));
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public int diableRecvMsg() {
|
||||
if (!isReceivingMsg) {
|
||||
return 1;
|
||||
}
|
||||
int ret = -1;
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DISABLE_RECV_TXT_VALUE).build();
|
||||
Response rsp = sendCmd(req);
|
||||
if (rsp != null) {
|
||||
ret = rsp.getStatus();
|
||||
if (ret == 0) {
|
||||
isReceivingMsg = false;
|
||||
}
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void waitMs(int ms) {
|
||||
try {
|
||||
Thread.sleep(ms);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public void printContacts(List<RpcContact> contacts) {
|
||||
for (RpcContact c : contacts) {
|
||||
int value = c.getGender();
|
||||
String gender;
|
||||
if (value == 1) {
|
||||
gender = "男";
|
||||
} else if (value == 2) {
|
||||
gender = "女";
|
||||
} else {
|
||||
gender = "未知";
|
||||
}
|
||||
|
||||
logger.info("{}, {}, {}, {}, {}, {}, {}", c.getWxid(), c.getName(), c.getCode(), c.getCountry(), c.getProvince(), c.getCity(), gender);
|
||||
}
|
||||
}
|
||||
|
||||
public void printWxMsg(WxMsg msg) {
|
||||
WxMsgResp wxMsgResp = new WxMsgResp();
|
||||
wxMsgResp.setIsSelf(msg.getIsSelf());
|
||||
wxMsgResp.setIsGroup(msg.getIsGroup());
|
||||
wxMsgResp.setId(msg.getId());
|
||||
wxMsgResp.setType(msg.getType());
|
||||
wxMsgResp.setTs(msg.getTs());
|
||||
wxMsgResp.setRoomId(msg.getRoomid());
|
||||
wxMsgResp.setContent(msg.getContent());
|
||||
wxMsgResp.setSender(msg.getSender());
|
||||
wxMsgResp.setSign(msg.getSign());
|
||||
wxMsgResp.setThumb(msg.getThumb());
|
||||
wxMsgResp.setExtra(msg.getExtra());
|
||||
wxMsgResp.setXml(msg.getXml().replace("\n", "").replace("\t", ""));
|
||||
|
||||
String jsonString = JSONObject.toJSONString(wxMsgResp);
|
||||
log.info("收到消息: {}", jsonString);
|
||||
}
|
||||
|
||||
private String bytesToHex(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void keepRunning() {
|
||||
while (true) {
|
||||
waitMs(1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.wechat.ferry.service;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
|
||||
/**
|
||||
* SDK.dll的接口类
|
||||
*
|
||||
* @Description
|
||||
* @Author xinggq
|
||||
* @Date 2024/7/10
|
||||
*/
|
||||
public interface SDK extends Library {
|
||||
|
||||
/**
|
||||
* 初始化SDK
|
||||
*
|
||||
* @param debug
|
||||
* @param port
|
||||
* @return
|
||||
*/
|
||||
int WxInitSDK(boolean debug, int port);
|
||||
|
||||
/**
|
||||
* 退出SDK
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int WxDestroySDK();
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.wechat.ferry.service;
|
||||
|
||||
/**
|
||||
* 业务接口-注册
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-29 20:58
|
||||
*/
|
||||
public interface TestService {
|
||||
|
||||
Boolean isLogin();
|
||||
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
package com.wechat.ferry.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.wechat.ferry.handle.WechatSocketClient;
|
||||
import com.wechat.ferry.service.TestService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 业务实现层-注册
|
||||
*
|
||||
* @author chandler
|
||||
* @date 2024-09-29 20:58
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TestServiceImpl implements TestService {
|
||||
|
||||
@Resource
|
||||
private WechatSocketClient wechatSocketClient;
|
||||
|
||||
@Override
|
||||
public Boolean isLogin() {
|
||||
|
||||
boolean flag = wechatSocketClient.isLogin();
|
||||
log.info("flag:{}", flag);
|
||||
List<String> list = wechatSocketClient.getDbNames();
|
||||
log.info("list:{}", list);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.wechat.ferry.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.List;
|
||||
|
||||
import org.dom4j.Attribute;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class XmlJsonConvertUtil {
|
||||
|
||||
public static String readFile(String path) {
|
||||
String str = "";
|
||||
try {
|
||||
File file = new File(path);
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
FileChannel fc = fis.getChannel();
|
||||
ByteBuffer bb = ByteBuffer.allocate(new Long(file.length()).intValue());
|
||||
// fc向buffer中读入数据
|
||||
fc.read(bb);
|
||||
bb.flip();
|
||||
str = new String(bb.array(), "UTF8");
|
||||
fc.close();
|
||||
fis.close();
|
||||
} catch (Exception e) {
|
||||
log.error("异常:{} ", e.getMessage());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* xml转json
|
||||
*
|
||||
* @param xmlStr
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject xml2Json(String xmlStr) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
xmlStr = xmlStr.replace("<?xml version=\\\"1.0\\\"?>\\n", "");
|
||||
Document doc = DocumentHelper.parseText(xmlStr);
|
||||
dom4j2Json(doc.getRootElement(), json);
|
||||
} catch (DocumentException e) {
|
||||
log.error("异常:{} ", e.getMessage());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* xml转json
|
||||
*
|
||||
* @param element
|
||||
* @param json
|
||||
*/
|
||||
public static void dom4j2Json(Element element, JSONObject json) {
|
||||
// 如果是属性
|
||||
for (Object o : element.attributes()) {
|
||||
Attribute attr = (Attribute)o;
|
||||
if (!isEmpty(attr.getValue())) {
|
||||
json.put("@" + attr.getName(), attr.getValue());
|
||||
}
|
||||
}
|
||||
List<Element> chdEl = element.elements();
|
||||
if (chdEl.isEmpty() && !isEmpty(element.getText())) {
|
||||
// 如果没有子元素,只有一个值
|
||||
json.put(element.getName(), element.getText());
|
||||
}
|
||||
|
||||
for (Element e : chdEl) {
|
||||
// 有子元素
|
||||
if (!e.elements().isEmpty()) {
|
||||
// 子元素也有子元素
|
||||
JSONObject chdjson = new JSONObject();
|
||||
dom4j2Json(e, chdjson);
|
||||
Object o = json.get(e.getName());
|
||||
if (o != null) {
|
||||
JSONArray jsona = null;
|
||||
if (o instanceof JSONObject) {
|
||||
// 如果此元素已存在,则转为jsonArray
|
||||
JSONObject jsono = (JSONObject)o;
|
||||
json.remove(e.getName());
|
||||
jsona = new JSONArray();
|
||||
jsona.add(jsono);
|
||||
jsona.add(chdjson);
|
||||
}
|
||||
if (o instanceof JSONArray) {
|
||||
jsona = (JSONArray)o;
|
||||
jsona.add(chdjson);
|
||||
}
|
||||
json.put(e.getName(), jsona);
|
||||
} else {
|
||||
if (!chdjson.isEmpty()) {
|
||||
json.put(e.getName(), chdjson);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// 子元素没有子元素
|
||||
for (Object o : element.attributes()) {
|
||||
Attribute attr = (Attribute)o;
|
||||
if (!isEmpty(attr.getValue())) {
|
||||
json.put("@" + attr.getName(), attr.getValue());
|
||||
}
|
||||
}
|
||||
if (!e.getText().isEmpty()) {
|
||||
json.put(e.getName(), e.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEmpty(String str) {
|
||||
if (str == null || str.trim().isEmpty() || "null".equals(str)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# 配置文件
|
||||
|
||||
# 服务端配置
|
||||
server:
|
||||
# 端口设置
|
||||
port: 9201
|
||||
|
||||
spring:
|
||||
# 配置应用信息
|
||||
application:
|
||||
# 应用名
|
||||
name: wechat-ferry
|
||||
# swagger适配
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
|
||||
# 本服务参数
|
||||
wechat:
|
||||
ferry:
|
||||
# DLL文件位置
|
||||
dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll
|
||||
# socket端口
|
||||
socket-port: 10086
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration debug="false" scan="false">
|
||||
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
|
||||
<property name="log.path" value="logs/${spring.application.name}"/>
|
||||
<property name="log.name" value="${spring.application.name}"/>
|
||||
|
||||
<!-- 彩色日志格式 -->
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
|
||||
<!-- 彩色日志依赖的渲染类 -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<!-- Console log output -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file debug output -->
|
||||
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<!-- 追加方式记录日志 -->
|
||||
<append>true</append>
|
||||
<!-- 日志文件的格式 -->
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- Log file error output -->
|
||||
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>50MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<!-- 追加方式记录日志 -->
|
||||
<append>true</append>
|
||||
<!-- 日志文件的格式 -->
|
||||
<encoder>
|
||||
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
<!-- 此日志文件只记录error级别的 -->
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!--
|
||||
DEBUG:输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。
|
||||
INFO: 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。
|
||||
WARN: 输出警告信息;表明会出现潜在错误的情形。
|
||||
ERROR:输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
|
||||
FATAL: 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
|
||||
ALL level:打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
|
||||
OFF level:关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。
|
||||
日志级别(按照范围从小到大排序):OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
|
||||
范围大的会包含范围小的,例如日志设置为INFO级别的话则FATAL、ERROR、WARN、INFO的日志开关都是打开的,而DEBUG的日志开关将是关闭的。
|
||||
-->
|
||||
<!--
|
||||
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
|
||||
<logger>仅有一个name属性,
|
||||
一个可选的level和一个可选的addtivity属性。
|
||||
name:用来指定受此logger约束的某一个包或者具体的某一个类。
|
||||
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
|
||||
如果未设置此属性,那么当前logger将会继承上级的级别。
|
||||
-->
|
||||
|
||||
<!-- 日志监听器 屏蔽 -->
|
||||
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO">
|
||||
<appender-ref ref="console"/>
|
||||
</logger>
|
||||
|
||||
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="debug"/>
|
||||
<appender-ref ref="error"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
@@ -0,0 +1,236 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package wcf;
|
||||
option java_package = "com.wechat.ferry.entity.po";
|
||||
|
||||
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_GET_AUDIO_MSG = 0x16;
|
||||
FUNC_SEND_TXT = 0x20;
|
||||
FUNC_SEND_IMG = 0x21;
|
||||
FUNC_SEND_FILE = 0x22;
|
||||
FUNC_SEND_XML = 0x23;
|
||||
FUNC_SEND_EMOTION = 0x24;
|
||||
FUNC_SEND_RICH_TXT = 0x25;
|
||||
FUNC_SEND_PAT_MSG = 0x26;
|
||||
FUNC_FORWARD_MSG = 0x27;
|
||||
FUNC_ENABLE_RECV_TXT = 0x30;
|
||||
FUNC_DISABLE_RECV_TXT = 0x40;
|
||||
FUNC_EXEC_DB_QUERY = 0x50;
|
||||
FUNC_ACCEPT_FRIEND = 0x51;
|
||||
FUNC_RECV_TRANSFER = 0x52;
|
||||
FUNC_REFRESH_PYQ = 0x53;
|
||||
FUNC_DOWNLOAD_ATTACH = 0x54;
|
||||
FUNC_GET_CONTACT_INFO = 0x55;
|
||||
FUNC_REVOKE_MSG = 0x56;
|
||||
FUNC_REFRESH_QRCODE = 0x57;
|
||||
FUNC_DECRYPT_IMAGE = 0x60;
|
||||
FUNC_EXEC_OCR = 0x61;
|
||||
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
||||
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
||||
FUNC_INV_ROOM_MEMBERS = 0x72;
|
||||
}
|
||||
|
||||
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; // 通过好友验证参数结构
|
||||
MemberMgmt m = 8; // 群成员管理,添加、删除、邀请
|
||||
XmlMsg xml = 9; // XML参数结构
|
||||
DecPath dec = 10; // 解密图片参数结构
|
||||
Transfer tf = 11; // 接收转账参数结构
|
||||
uint64 ui64 = 12 [ jstype = JS_STRING ]; // 64 位整数,通用
|
||||
bool flag = 13; // 布尔值
|
||||
AttachMsg att = 14; // 下载图片、视频、文件参数结构
|
||||
AudioMsg am = 15; // 保存语音参数结构
|
||||
RichText rt = 16; // 发送卡片消息结构
|
||||
PatMsg pm = 17; // 发送拍一拍参数结构
|
||||
ForwardMsg fm = 18; // 转发消息参数结构
|
||||
}
|
||||
}
|
||||
|
||||
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; // 个人信息
|
||||
OcrMsg ocr = 11; // OCR 结果
|
||||
};
|
||||
}
|
||||
|
||||
message Empty { }
|
||||
|
||||
message WxMsg
|
||||
{
|
||||
bool is_self = 1; // 是否自己发送的
|
||||
bool is_group = 2; // 是否群消息
|
||||
uint64 id = 3 [ jstype = JS_STRING ]; // 消息 id
|
||||
uint32 type = 4; // 消息类型
|
||||
uint32 ts = 5; // 消息类型
|
||||
string roomid = 6; // 群 id(如果是群消息的话)
|
||||
string content = 7; // 消息内容
|
||||
string sender = 8; // 消息发送者
|
||||
string sign = 9; // Sign
|
||||
string thumb = 10; // 缩略图
|
||||
string extra = 11; // 附加内容
|
||||
string xml = 12; // 消息 xml
|
||||
}
|
||||
|
||||
message TextMsg
|
||||
{
|
||||
string msg = 1; // 要发送的消息内容
|
||||
string receiver = 2; // 消息接收人,当为群时可@
|
||||
string aters = 3; // 要@的人列表,逗号分隔
|
||||
}
|
||||
|
||||
message PathMsg
|
||||
{
|
||||
string path = 1; // 要发送的图片的路径
|
||||
string receiver = 2; // 消息接收人
|
||||
}
|
||||
|
||||
message XmlMsg
|
||||
{
|
||||
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; // 性别
|
||||
}
|
||||
message RpcContacts { repeated RpcContact contacts = 1; }
|
||||
|
||||
message DbNames { repeated string names = 1; }
|
||||
|
||||
message DbTable
|
||||
{
|
||||
string name = 1; // 表名
|
||||
string sql = 2; // 建表 SQL
|
||||
}
|
||||
message DbTables { repeated DbTable tables = 1; }
|
||||
|
||||
message DbQuery
|
||||
{
|
||||
string db = 1; // 目标数据库
|
||||
string sql = 2; // 查询 SQL
|
||||
}
|
||||
|
||||
message DbField
|
||||
{
|
||||
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; // Ticket
|
||||
int32 scene = 3; // 添加方式:17 名片,30 扫码
|
||||
}
|
||||
|
||||
message MemberMgmt
|
||||
{
|
||||
string roomid = 1; // 要加的群ID
|
||||
string wxids = 2; // 要加群的人列表,逗号分隔
|
||||
}
|
||||
|
||||
message UserInfo
|
||||
{
|
||||
string wxid = 1; // 微信ID
|
||||
string name = 2; // 昵称
|
||||
string mobile = 3; // 手机号
|
||||
string home = 4; // 文件/图片等父路径
|
||||
}
|
||||
|
||||
message DecPath
|
||||
{
|
||||
string src = 1; // 源路径
|
||||
string dst = 2; // 目标路径
|
||||
}
|
||||
|
||||
message Transfer
|
||||
{
|
||||
string wxid = 1; // 转账人
|
||||
string tfid = 2; // 转账id transferid
|
||||
string taid = 3; // Transaction id
|
||||
}
|
||||
|
||||
message AttachMsg
|
||||
{
|
||||
uint64 id = 1 [ jstype = JS_STRING ]; // 消息 id
|
||||
string thumb = 2; // 消息中的 thumb
|
||||
string extra = 3; // 消息中的 extra
|
||||
}
|
||||
|
||||
message AudioMsg
|
||||
{
|
||||
uint64 id = 1 [ jstype = JS_STRING ]; // 语音消息 id
|
||||
string dir = 2; // 存放目录
|
||||
}
|
||||
|
||||
message RichText
|
||||
{
|
||||
string name = 1; // 显示名字
|
||||
string account = 2; // 公众号 id
|
||||
string title = 3; // 标题
|
||||
string digest = 4; // 摘要
|
||||
string url = 5; // 链接
|
||||
string thumburl = 6; // 缩略图
|
||||
string receiver = 7; // 接收人
|
||||
}
|
||||
|
||||
message PatMsg
|
||||
{
|
||||
string roomid = 1; // 群 id
|
||||
string wxid = 2; // wxid
|
||||
}
|
||||
|
||||
message OcrMsg
|
||||
{
|
||||
int32 status = 1; // 状态
|
||||
string result = 2; // 结果
|
||||
}
|
||||
|
||||
message ForwardMsg
|
||||
{
|
||||
uint64 id = 1 [ jstype = JS_STRING ]; // 待转发消息 ID
|
||||
string receiver = 2; // 转发接收目标,群为 roomId,个人为 wxid
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file !.gitkeep
|
||||
Binary file not shown.
Reference in New Issue
Block a user