解决本地运行的问题
This commit is contained in:
168
BACKEND_PROTOCOL.md
Normal file
168
BACKEND_PROTOCOL.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# WorkTool 后端通信协议文档
|
||||||
|
|
||||||
|
本文档详细描述了 WorkTool 机器人(客户端)与后台服务器(服务端)之间的通信协议。
|
||||||
|
|
||||||
|
## 1. 连接方式
|
||||||
|
|
||||||
|
WorkTool 使用 WebSocket 协议与服务端进行长连接通信。
|
||||||
|
|
||||||
|
* **WebSocket URL**: `ws://<服务器IP>:<端口>/webserver/wework/<链接号>`
|
||||||
|
* `<链接号>` (robotId): 机器人的唯一标识,由用户在 APP 端设置。
|
||||||
|
* **通信格式**: JSON
|
||||||
|
* **心跳机制**: 客户端每 5 秒发送一次心跳包 `{"type": 11}`,服务端可忽略或回复。
|
||||||
|
|
||||||
|
## 2. 数据包结构
|
||||||
|
|
||||||
|
所有的通信数据(发送和接收)都包裹在 `WeworkMessageListBean` 结构中。
|
||||||
|
|
||||||
|
### 2.1 外层包装 (WeworkMessageListBean)
|
||||||
|
|
||||||
|
服务端发送给客户端的 JSON 必须符合以下结构:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"socketType": 2, // 消息类型:2 表示下发指令列表
|
||||||
|
"list": [ // 指令列表,可包含多个指令
|
||||||
|
{
|
||||||
|
"type": 203, // 具体指令类型 (例如 203=发送消息)
|
||||||
|
"messageId": "uuid", // 消息ID (可选,用于回调对应)
|
||||||
|
... // 指令参数 (详见下文)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"encryptType": 0 // 加密类型:0=不加密, 1=AES加密 (默认0)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 消息确认
|
||||||
|
|
||||||
|
当客户端收到服务端消息后,会回复一个确认包(通常服务端不需要处理):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"socketType": 1, // SOCKET_TYPE_MESSAGE_CONFIRM
|
||||||
|
"messageId": "..." // 收到的消息ID
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 常用指令详解
|
||||||
|
|
||||||
|
以下列出常用的指令及其 JSON 参数示例。参数放在 `list` 数组的对象中。
|
||||||
|
|
||||||
|
### 3.1 发送文本消息 (SEND_MESSAGE)
|
||||||
|
* **Type**: `203`
|
||||||
|
* **说明**: 在指定房间(群或好友)发送文本。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 203,
|
||||||
|
"titleList": ["张三", "技术交流群"], // 接收者名称列表 (群名或好友名)
|
||||||
|
"receivedContent": "你好,这是测试消息", // 消息内容
|
||||||
|
"at": "李四" // (可选) 需要 @ 的人昵称,仅在群聊有效
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 转发消息 (RELAY_MESSAGE)
|
||||||
|
* **Type**: `205`
|
||||||
|
* **说明**: 将某人的消息转发给其他人。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 205,
|
||||||
|
"titleList": ["来源群名"], // 消息来源房间名
|
||||||
|
"receivedName": "张三", // 消息发送者昵称
|
||||||
|
"originalContent": "原消息内容", // 用于匹配消息内容
|
||||||
|
"textType": 1, // 消息类型 (1=文本, 2=图片, 详见附录)
|
||||||
|
"nameList": ["目标群1", "李四"], // 转发目标列表
|
||||||
|
"extraText": "这是转发的消息" // (可选) 转发时的附加留言
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 推送文件/图片 (PUSH_FILE)
|
||||||
|
* **Type**: `218`
|
||||||
|
* **说明**: 发送网络文件或图片。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 218,
|
||||||
|
"titleList": ["张三"],
|
||||||
|
"objectName": "文件名.jpg", // 保存的文件名
|
||||||
|
"fileUrl": "http://example.com/a.jpg", // 文件下载地址
|
||||||
|
"fileType": "image", // 文件类型 (image, video, file)
|
||||||
|
"extraText": "请查收图片" // (可选) 附加留言
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 推送链接 (PUSH_LINK)
|
||||||
|
* **Type**: `224`
|
||||||
|
* **说明**: 发送卡片链接。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 224,
|
||||||
|
"titleList": ["张三"],
|
||||||
|
"objectName": "百度一下", // 链接标题
|
||||||
|
"receivedContent": "你就知道", // 链接描述/副标题
|
||||||
|
"originalContent": "https://www.baidu.com", // 跳转链接
|
||||||
|
"fileUrl": "http://example.com/icon.png" // 链接图标地址
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.5 获取最近聊天列表 (GET_RECENT_LIST)
|
||||||
|
* **Type**: `505`
|
||||||
|
* **说明**: 获取消息列表首页的最近聊天记录。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 505
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.6 获取群信息 (GET_GROUP_INFO)
|
||||||
|
* **Type**: `501`
|
||||||
|
* **说明**: 获取指定群的成员列表等信息。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 501,
|
||||||
|
"selectList": ["技术交流群"] // 要查询的群名列表
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 执行结果回调
|
||||||
|
|
||||||
|
当机器人执行完指令后,会向服务端发送执行结果。
|
||||||
|
|
||||||
|
* **SocketType**: `3` (SOCKET_TYPE_RAW_CONFIRM)
|
||||||
|
* **数据结构**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"socketType": 3,
|
||||||
|
"messageId": "原始指令的messageId",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"errorCode": 0, // 0 表示成功,非 0 表示失败
|
||||||
|
"errorReason": "", // 失败原因
|
||||||
|
"successList": ["张三"], // 执行成功的对象列表
|
||||||
|
"failList": [], // 执行失败的对象列表
|
||||||
|
"rawMsg": "{...}", // 原始指令 JSON
|
||||||
|
"runTime": 1670000000000, // 开始执行时间戳
|
||||||
|
"timeCost": 1.5 // 耗时 (秒)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 常见错误码 (ErrorCode)
|
||||||
|
* `0`: 成功
|
||||||
|
* `201102`: 发送消息失败
|
||||||
|
* `201104`: 目标寻找失败 (未找到群或联系人)
|
||||||
|
* `501000`: 其他未知错误
|
||||||
|
|
||||||
|
## 附录:消息类型定义 (textType)
|
||||||
|
* 1: 文本 (TEXT_TYPE_PLAIN)
|
||||||
|
* 2: 图片 (TEXT_TYPE_IMAGE)
|
||||||
|
* 3: 语音 (TEXT_TYPE_VOICE)
|
||||||
|
* 5: 视频 (TEXT_TYPE_VIDEO)
|
||||||
|
* 8: 链接 (TEXT_TYPE_LINK)
|
||||||
|
* 9: 文件 (TEXT_TYPE_FILE)
|
||||||
|
* 15: 引用回复 (TEXT_TYPE_REPLY)
|
||||||
@@ -5,7 +5,7 @@ import com.blankj.utilcode.util.SPUtils
|
|||||||
object Constant {
|
object Constant {
|
||||||
|
|
||||||
val AVAILABLE_VERSION = arrayListOf("4.0.2", "4.0.6", "4.0.8", "4.0.10", "4.0.12", "4.0.16", "4.0.18", "4.0.19", "4.0.20", "4.1.0", "4.1.2", "4.1.3", "4.1.6", "4.1.7", "4.1.8", "4.1.9", "4.1.10")
|
val AVAILABLE_VERSION = arrayListOf("4.0.2", "4.0.6", "4.0.8", "4.0.10", "4.0.12", "4.0.16", "4.0.18", "4.0.19", "4.0.20", "4.1.0", "4.1.2", "4.1.3", "4.1.6", "4.1.7", "4.1.8", "4.1.9", "4.1.10")
|
||||||
val AVAILABLE_VERSION_MAP = mapOf(Pair("4.0.2", 40002), Pair("4.0.6", 40006), Pair("4.0.8", 40008), Pair("4.0.10", 40010), Pair("4.0.12", 40012), Pair("4.0.16", 40016), Pair("4.0.18", 40018), Pair("4.0.19", 40019), Pair("4.0.20", 40020), Pair("4.1.0", 40100), Pair("4.1.2", 40102), Pair("4.1.3", 40103), Pair("4.1.6", 40106), Pair("4.1.7", 40107), Pair("4.1.8", 40108), Pair("4.1.9", 40109), Pair("4.1.9", 40110))
|
val AVAILABLE_VERSION_MAP = mapOf(Pair("4.0.2", 40002), Pair("4.0.6", 40006), Pair("4.0.8", 40008), Pair("4.0.10", 40010), Pair("4.0.12", 40012), Pair("4.0.16", 40016), Pair("4.0.18", 40018), Pair("4.0.19", 40019), Pair("4.0.20", 40020), Pair("4.1.0", 40100), Pair("4.1.2", 40102), Pair("4.1.3", 40103), Pair("4.1.6", 40106), Pair("4.1.7", 40107), Pair("4.1.8", 40108), Pair("4.1.9", 40109), Pair("4.1.10", 40110))
|
||||||
const val PACKAGE_NAMES = "com.tencent.wework"
|
const val PACKAGE_NAMES = "com.tencent.wework"
|
||||||
const val WEWORK_NOTIFY = "wework_notify"
|
const val WEWORK_NOTIFY = "wework_notify"
|
||||||
const val BASE_LONG_INTERVAL = 5000L
|
const val BASE_LONG_INTERVAL = 5000L
|
||||||
@@ -14,7 +14,7 @@ object Constant {
|
|||||||
var LONG_INTERVAL = BASE_LONG_INTERVAL
|
var LONG_INTERVAL = BASE_LONG_INTERVAL
|
||||||
var CHANGE_PAGE_INTERVAL = BASE_CHANGE_PAGE_INTERVAL
|
var CHANGE_PAGE_INTERVAL = BASE_CHANGE_PAGE_INTERVAL
|
||||||
var POP_WINDOW_INTERVAL = BASE_POP_WINDOW_INTERVAL
|
var POP_WINDOW_INTERVAL = BASE_POP_WINDOW_INTERVAL
|
||||||
private const val DEFAULT_HOST = "wss://api.worktool.ymdyes.cn"
|
private const val DEFAULT_HOST = "ws://192.168.13.252:8080"
|
||||||
|
|
||||||
var version = Int.MAX_VALUE
|
var version = Int.MAX_VALUE
|
||||||
var myName = ""
|
var myName = ""
|
||||||
@@ -28,9 +28,9 @@ object Constant {
|
|||||||
val transformation = "AES/CBC/PKCS7Padding"
|
val transformation = "AES/CBC/PKCS7Padding"
|
||||||
val wssRegex = "^wss".toRegex()
|
val wssRegex = "^wss".toRegex()
|
||||||
val wsRegex = "^ws".toRegex()
|
val wsRegex = "^ws".toRegex()
|
||||||
val suffixString = "(-.*)?(…)?(\\(\\d+\\))?$"
|
val suffixString = "(\\s*-.*)?(…)?(\\(\\d+\\))?(\\s*[\\(\uff08].*?[\\)\uff09])?$"
|
||||||
val suffixRegex = "(-.*)?(…)?(\\(\\d+\\))?$".toRegex()
|
val suffixRegex = "(\\s*-.*)?(…)?(\\(\\d+\\))?(\\s*[\\(\uff08].*?[\\)\uff09])?$".toRegex()
|
||||||
val groupSuffixRegex = "(…)?(\\(\\d+\\))?$".toRegex()
|
val groupSuffixRegex = "(…)?(\\(\\d+\\))?(\\s*[\\(\uff08].*?[\\)\uff09])?$".toRegex()
|
||||||
val digitalRegex = "\\(\\d+\\)\$".toRegex()
|
val digitalRegex = "\\(\\d+\\)\$".toRegex()
|
||||||
var weworkCorpName: String
|
var weworkCorpName: String
|
||||||
get() = SPUtils.getInstance().getString("weworkCorpName", "")
|
get() = SPUtils.getInstance().getString("weworkCorpName", "")
|
||||||
|
|||||||
@@ -185,7 +185,9 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initData() {
|
private fun initData() {
|
||||||
HttpUtil.checkUpdate()
|
Constant.robotId = "111"
|
||||||
|
Constant.host = "ws://192.168.13.252:8080"
|
||||||
|
// HttpUtil.checkUpdate()
|
||||||
HttpUtil.getMyConfig(toast = false)
|
HttpUtil.getMyConfig(toast = false)
|
||||||
CacheUtil.autoDelete()
|
CacheUtil.autoDelete()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ dependencies {
|
|||||||
//工具集
|
//工具集
|
||||||
api 'com.blankj:utilcodex:1.31.0'
|
api 'com.blankj:utilcodex:1.31.0'
|
||||||
//toast
|
//toast
|
||||||
api 'com.github.getActivity:ToastUtils:10.5'
|
api(name: 'ToastUtils-10.5', ext: 'aar')
|
||||||
//Gson
|
//Gson
|
||||||
api 'com.google.code.gson:gson:2.8.5'
|
api 'com.google.code.gson:gson:2.8.5'
|
||||||
//网络
|
//网络
|
||||||
@@ -53,9 +53,9 @@ dependencies {
|
|||||||
api 'com.github.bumptech.glide:glide:4.9.0'
|
api 'com.github.bumptech.glide:glide:4.9.0'
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
|
||||||
//photoview
|
//photoview
|
||||||
api 'com.github.chrisbanes:PhotoView:2.1.3'
|
api(name: 'PhotoView-2.1.3', ext: 'aar')
|
||||||
//悬浮窗框架
|
//悬浮窗框架
|
||||||
api 'com.github.princekin-f:EasyFloat:1.3.4'
|
api(name: 'EasyFloat-1.3.4', ext: 'aar')
|
||||||
|
|
||||||
//leak
|
//leak
|
||||||
// debugApi 'com.squareup.leakcanary:leakcanary-android:1.5.4'
|
// debugApi 'com.squareup.leakcanary:leakcanary-android:1.5.4'
|
||||||
@@ -64,4 +64,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
flatDir {
|
||||||
|
dirs 'libs'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
baselibrary/libs/EasyFloat-1.3.4.aar
Normal file
BIN
baselibrary/libs/EasyFloat-1.3.4.aar
Normal file
Binary file not shown.
BIN
baselibrary/libs/PhotoView-2.1.3.aar
Normal file
BIN
baselibrary/libs/PhotoView-2.1.3.aar
Normal file
Binary file not shown.
BIN
baselibrary/libs/ToastUtils-10.5.aar
Normal file
BIN
baselibrary/libs/ToastUtils-10.5.aar
Normal file
Binary file not shown.
24
build.gradle
24
build.gradle
@@ -1,15 +1,16 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.3.72"
|
ext.kotlin_version = "1.7.10"
|
||||||
repositories {
|
repositories {
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
maven { url 'https://maven.fabric.io/public' }
|
maven { url 'https://www.jitpack.io' }
|
||||||
maven { url 'https://repo1.maven.org/maven2/' }
|
|
||||||
maven { url 'https://jitpack.io' }
|
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.android.tools.build:gradle:4.0.0"
|
classpath "com.android.tools.build:gradle:7.2.2"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
@@ -19,11 +20,14 @@ buildscript {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
google()
|
google()
|
||||||
jcenter()
|
mavenCentral()
|
||||||
maven { url 'https://maven.fabric.io/public' }
|
maven { url 'https://www.jitpack.io' }
|
||||||
maven { url 'https://repo1.maven.org/maven2/' }
|
flatDir {
|
||||||
maven { url 'https://jitpack.io' }
|
dirs "$rootProject.projectDir/baselibrary/libs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ android.useAndroidX=true
|
|||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
systemProp.http.agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user