@dcrays/dcgchat-test 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -83
- package/index.ts +24 -24
- package/openclaw.plugin.json +9 -9
- package/package.json +58 -58
- package/src/api.ts +62 -62
- package/src/bot.ts +272 -272
- package/src/channel.ts +192 -192
- package/src/connection.ts +11 -11
- package/src/log.ts +46 -46
- package/src/monitor.ts +191 -190
- package/src/oss.ts +72 -72
- package/src/request.ts +194 -194
- package/src/runtime.ts +38 -38
- package/src/skill.ts +110 -110
- package/src/tool.ts +74 -64
- package/src/types.ts +103 -103
- package/src/userInfo.ts +97 -97
package/README.md
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
# OpenClaw DCG Chat 插件
|
|
2
|
-
|
|
3
|
-
连接 OpenClaw 与 DCG Chat 产品的通道插件。
|
|
4
|
-
|
|
5
|
-
## 架构
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
┌──────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────────────────┐
|
|
9
|
-
│ Web 前端 │ ←───────────────→ │ 公司后端服务 │ ←───────────────→ │ OpenClaw(工作电脑) │
|
|
10
|
-
└──────────┘ └──────────────┘ (OpenClaw 主动连) └─────────────────────┘
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
- OpenClaw 插件**主动连接**后端的 WebSocket 服务(不需要公网 IP)
|
|
14
|
-
- 后端收到用户消息后转发给 OpenClaw,OpenClaw 回复后发回后端
|
|
15
|
-
|
|
16
|
-
## 快速开始
|
|
17
|
-
|
|
18
|
-
### 1. 安装插件
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
pnpm openclaw plugins install -l /path/to/openclaw-dcgchat
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### 2. 配置
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
openclaw config set channels.dcgchat.enabled true
|
|
28
|
-
openclaw config set channels.dcgchat.wsUrl "ws://your-backend:8080/openclaw/ws"
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 3. 启动
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pnpm openclaw gateway
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 消息协议(MVP)
|
|
38
|
-
|
|
39
|
-
### 下行:后端 → OpenClaw(用户消息)
|
|
40
|
-
|
|
41
|
-
```json
|
|
42
|
-
{ "type": "message", "userId": "user_001", "text": "你好" }
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 上行:OpenClaw → 后端(Agent 回复)
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
{ "type": "reply", "userId": "user_001", "text": "你好!有什么可以帮你的?" }
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## 配置项
|
|
52
|
-
|
|
53
|
-
| 配置键 | 类型 | 说明 |
|
|
54
|
-
|--------|------|------|
|
|
55
|
-
| `channels.dcgchat.enabled` | boolean | 是否启用 |
|
|
56
|
-
| `channels.dcgchat.wsUrl` | string | 后端 WebSocket 地址 |
|
|
57
|
-
|
|
58
|
-
## 开发
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# 安装依赖
|
|
62
|
-
pnpm install
|
|
63
|
-
|
|
64
|
-
# 类型检查
|
|
65
|
-
pnpm typecheck
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 文件结构
|
|
69
|
-
|
|
70
|
-
- `index.ts` - 插件入口
|
|
71
|
-
- `src/channel.ts` - ChannelPlugin 定义
|
|
72
|
-
- `src/runtime.ts` - 插件 runtime
|
|
73
|
-
- `src/types.ts` - 类型定义
|
|
74
|
-
- `src/monitor.ts` - WebSocket 连接与断线重连
|
|
75
|
-
- `src/bot.ts` - 消息处理与 Agent 调用
|
|
76
|
-
|
|
77
|
-
## 后续迭代
|
|
78
|
-
|
|
79
|
-
- [ ] Token 认证
|
|
80
|
-
- [ ] 流式输出
|
|
81
|
-
- [ ] Typing 指示
|
|
82
|
-
- [ ] messageId 去重
|
|
83
|
-
- [ ] 错误消息类型
|
|
1
|
+
# OpenClaw DCG Chat 插件
|
|
2
|
+
|
|
3
|
+
连接 OpenClaw 与 DCG Chat 产品的通道插件。
|
|
4
|
+
|
|
5
|
+
## 架构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌──────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────────────────┐
|
|
9
|
+
│ Web 前端 │ ←───────────────→ │ 公司后端服务 │ ←───────────────→ │ OpenClaw(工作电脑) │
|
|
10
|
+
└──────────┘ └──────────────┘ (OpenClaw 主动连) └─────────────────────┘
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- OpenClaw 插件**主动连接**后端的 WebSocket 服务(不需要公网 IP)
|
|
14
|
+
- 后端收到用户消息后转发给 OpenClaw,OpenClaw 回复后发回后端
|
|
15
|
+
|
|
16
|
+
## 快速开始
|
|
17
|
+
|
|
18
|
+
### 1. 安装插件
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm openclaw plugins install -l /path/to/openclaw-dcgchat
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. 配置
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
openclaw config set channels.dcgchat.enabled true
|
|
28
|
+
openclaw config set channels.dcgchat.wsUrl "ws://your-backend:8080/openclaw/ws"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. 启动
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm openclaw gateway
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 消息协议(MVP)
|
|
38
|
+
|
|
39
|
+
### 下行:后端 → OpenClaw(用户消息)
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{ "type": "message", "userId": "user_001", "text": "你好" }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 上行:OpenClaw → 后端(Agent 回复)
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{ "type": "reply", "userId": "user_001", "text": "你好!有什么可以帮你的?" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 配置项
|
|
52
|
+
|
|
53
|
+
| 配置键 | 类型 | 说明 |
|
|
54
|
+
|--------|------|------|
|
|
55
|
+
| `channels.dcgchat.enabled` | boolean | 是否启用 |
|
|
56
|
+
| `channels.dcgchat.wsUrl` | string | 后端 WebSocket 地址 |
|
|
57
|
+
|
|
58
|
+
## 开发
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 安装依赖
|
|
62
|
+
pnpm install
|
|
63
|
+
|
|
64
|
+
# 类型检查
|
|
65
|
+
pnpm typecheck
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 文件结构
|
|
69
|
+
|
|
70
|
+
- `index.ts` - 插件入口
|
|
71
|
+
- `src/channel.ts` - ChannelPlugin 定义
|
|
72
|
+
- `src/runtime.ts` - 插件 runtime
|
|
73
|
+
- `src/types.ts` - 类型定义
|
|
74
|
+
- `src/monitor.ts` - WebSocket 连接与断线重连
|
|
75
|
+
- `src/bot.ts` - 消息处理与 Agent 调用
|
|
76
|
+
|
|
77
|
+
## 后续迭代
|
|
78
|
+
|
|
79
|
+
- [ ] Token 认证
|
|
80
|
+
- [ ] 流式输出
|
|
81
|
+
- [ ] Typing 指示
|
|
82
|
+
- [ ] messageId 去重
|
|
83
|
+
- [ ] 错误消息类型
|
package/index.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
-
import { dcgchatPlugin } from "./src/channel.js";
|
|
4
|
-
import { setDcgchatRuntime, setWorkspaceDir } from "./src/runtime.js";
|
|
5
|
-
import { monitoringToolMessage } from "./src/tool.js";
|
|
6
|
-
|
|
7
|
-
const plugin = {
|
|
8
|
-
id: "dcgchat",
|
|
9
|
-
name: "DCG Chat",
|
|
10
|
-
description: "连接 OpenClaw 与 DCG Chat 产品(WebSocket)",
|
|
11
|
-
configSchema: emptyPluginConfigSchema(),
|
|
12
|
-
register(api: OpenClawPluginApi) {
|
|
13
|
-
setDcgchatRuntime(api.runtime);
|
|
14
|
-
monitoringToolMessage(api);
|
|
15
|
-
api.registerChannel({ plugin: dcgchatPlugin });
|
|
16
|
-
api.registerTool((ctx) => {
|
|
17
|
-
const workspaceDir = ctx.workspaceDir;
|
|
18
|
-
setWorkspaceDir(workspaceDir);
|
|
19
|
-
return null;
|
|
20
|
-
});
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default plugin;
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { dcgchatPlugin } from "./src/channel.js";
|
|
4
|
+
import { setDcgchatRuntime, setWorkspaceDir } from "./src/runtime.js";
|
|
5
|
+
import { monitoringToolMessage } from "./src/tool.js";
|
|
6
|
+
|
|
7
|
+
const plugin = {
|
|
8
|
+
id: "dcgchat",
|
|
9
|
+
name: "DCG Chat",
|
|
10
|
+
description: "连接 OpenClaw 与 DCG Chat 产品(WebSocket)",
|
|
11
|
+
configSchema: emptyPluginConfigSchema(),
|
|
12
|
+
register(api: OpenClawPluginApi) {
|
|
13
|
+
setDcgchatRuntime(api.runtime);
|
|
14
|
+
monitoringToolMessage(api);
|
|
15
|
+
api.registerChannel({ plugin: dcgchatPlugin });
|
|
16
|
+
api.registerTool((ctx) => {
|
|
17
|
+
const workspaceDir = ctx.workspaceDir;
|
|
18
|
+
setWorkspaceDir(workspaceDir);
|
|
19
|
+
return null;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default plugin;
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "dcgchat",
|
|
3
|
-
"channels": ["dcgchat"],
|
|
4
|
-
"configSchema": {
|
|
5
|
-
"type": "object",
|
|
6
|
-
"additionalProperties": false,
|
|
7
|
-
"properties": {}
|
|
8
|
-
}
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "dcgchat",
|
|
3
|
+
"channels": ["dcgchat"],
|
|
4
|
+
"configSchema": {
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {}
|
|
8
|
+
}
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@dcrays/dcgchat-test",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "OpenClaw channel plugin for DCG Chat (WebSocket)",
|
|
6
|
-
"main": "index.ts",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"files": [
|
|
9
|
-
"index.ts",
|
|
10
|
-
"src",
|
|
11
|
-
"openclaw.plugin.json"
|
|
12
|
-
],
|
|
13
|
-
"keywords": [
|
|
14
|
-
"openclaw",
|
|
15
|
-
"dcgchat",
|
|
16
|
-
"websocket",
|
|
17
|
-
"ai"
|
|
18
|
-
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
21
|
-
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"ali-oss": "file:src/libs/ali-oss-6.23.0.tgz",
|
|
24
|
-
"ws": "file:src/libs/ws-8.19.0.tgz",
|
|
25
|
-
"axios": "file:src/libs/axios-1.13.6.tgz",
|
|
26
|
-
"md5": "file:src/libs/md5-2.3.0.tgz",
|
|
27
|
-
"unzipper": "file:src/libs/unzipper-0.12.3.tgz"
|
|
28
|
-
},
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@types/node": "^22.0.0",
|
|
31
|
-
"@types/ws": "^8.5.0",
|
|
32
|
-
"openclaw": "2026.2.13",
|
|
33
|
-
"tsx": "^4.19.0",
|
|
34
|
-
"typescript": "^5.7.0"
|
|
35
|
-
},
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"openclaw": ">=2026.2.13"
|
|
38
|
-
},
|
|
39
|
-
"openclaw": {
|
|
40
|
-
"extensions": [
|
|
41
|
-
"./index.ts"
|
|
42
|
-
],
|
|
43
|
-
"channel": {
|
|
44
|
-
"id": "dcgchat",
|
|
45
|
-
"label": "DCG Chat",
|
|
46
|
-
"selectionLabel": "DCG Chat",
|
|
47
|
-
"docsPath": "/channels/dcgchat",
|
|
48
|
-
"docsLabel": "dcgchat",
|
|
49
|
-
"blurb": "连接 OpenClaw 与 DCG Chat 产品",
|
|
50
|
-
"order": 80
|
|
51
|
-
},
|
|
52
|
-
"install": {
|
|
53
|
-
"npmSpec": "@dcrays/dcgchat-test",
|
|
54
|
-
"localPath": "extensions/dcgchat",
|
|
55
|
-
"defaultChoice": "npm"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@dcrays/dcgchat-test",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "OpenClaw channel plugin for DCG Chat (WebSocket)",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"files": [
|
|
9
|
+
"index.ts",
|
|
10
|
+
"src",
|
|
11
|
+
"openclaw.plugin.json"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"openclaw",
|
|
15
|
+
"dcgchat",
|
|
16
|
+
"websocket",
|
|
17
|
+
"ai"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"ali-oss": "file:src/libs/ali-oss-6.23.0.tgz",
|
|
24
|
+
"ws": "file:src/libs/ws-8.19.0.tgz",
|
|
25
|
+
"axios": "file:src/libs/axios-1.13.6.tgz",
|
|
26
|
+
"md5": "file:src/libs/md5-2.3.0.tgz",
|
|
27
|
+
"unzipper": "file:src/libs/unzipper-0.12.3.tgz"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"@types/ws": "^8.5.0",
|
|
32
|
+
"openclaw": "2026.2.13",
|
|
33
|
+
"tsx": "^4.19.0",
|
|
34
|
+
"typescript": "^5.7.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"openclaw": ">=2026.2.13"
|
|
38
|
+
},
|
|
39
|
+
"openclaw": {
|
|
40
|
+
"extensions": [
|
|
41
|
+
"./index.ts"
|
|
42
|
+
],
|
|
43
|
+
"channel": {
|
|
44
|
+
"id": "dcgchat",
|
|
45
|
+
"label": "DCG Chat",
|
|
46
|
+
"selectionLabel": "DCG Chat",
|
|
47
|
+
"docsPath": "/channels/dcgchat",
|
|
48
|
+
"docsLabel": "dcgchat",
|
|
49
|
+
"blurb": "连接 OpenClaw 与 DCG Chat 产品",
|
|
50
|
+
"order": 80
|
|
51
|
+
},
|
|
52
|
+
"install": {
|
|
53
|
+
"npmSpec": "@dcrays/dcgchat-test",
|
|
54
|
+
"localPath": "extensions/dcgchat",
|
|
55
|
+
"defaultChoice": "npm"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/api.ts
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { post } from "./request.js";
|
|
2
|
-
import type { IStsToken, IStsTokenReq } from "./types.js";
|
|
3
|
-
import { getUserTokenCache, setUserTokenCache } from "./userInfo.js";
|
|
4
|
-
|
|
5
|
-
export const getStsToken = async (name: string, botToken: string) => {
|
|
6
|
-
// 确保 userToken 已缓存(如果未缓存会自动获取并缓存)
|
|
7
|
-
await getUserToken(botToken);
|
|
8
|
-
|
|
9
|
-
const response = await post<IStsTokenReq, IStsToken>(
|
|
10
|
-
"/user/getStsToken",
|
|
11
|
-
{
|
|
12
|
-
sourceFileName: name,
|
|
13
|
-
isPrivate: 0,
|
|
14
|
-
},
|
|
15
|
-
{ botToken },
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
if (!response || !response.data || !response.data.bucket) {
|
|
19
|
-
throw new Error("获取 OSS 临时凭证失败");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return response.data;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 通过 botToken 查询 userToken
|
|
27
|
-
* @param botToken 机器人 token
|
|
28
|
-
* @returns userToken
|
|
29
|
-
*/
|
|
30
|
-
export const queryUserTokenByBotToken = async (botToken: string): Promise<string> => {
|
|
31
|
-
const response = await post<{botToken: string}, {token: string}>("/organization/queryUserTokenByBotToken", {
|
|
32
|
-
botToken,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (!response || !response.data || !response.data.token) {
|
|
36
|
-
throw new Error("获取绑定的用户信息失败");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return response.data.token;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 获取 userToken(优先从缓存获取,缓存未命中则调用 API)
|
|
44
|
-
* @param botToken 机器人 token
|
|
45
|
-
* @returns userToken
|
|
46
|
-
*/
|
|
47
|
-
export const getUserToken = async (botToken: string): Promise<string> => {
|
|
48
|
-
// 1. 尝试从缓存获取
|
|
49
|
-
const cachedToken = getUserTokenCache(botToken);
|
|
50
|
-
if (cachedToken) {
|
|
51
|
-
return cachedToken;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 2. 缓存未命中,调用 API 获取
|
|
55
|
-
console.log(`[api] cache miss, fetching userToken for botToken=${botToken.slice(0, 10)}...`);
|
|
56
|
-
const userToken = await queryUserTokenByBotToken(botToken);
|
|
57
|
-
|
|
58
|
-
// 3. 缓存新获取的 token
|
|
59
|
-
setUserTokenCache(botToken, userToken);
|
|
60
|
-
|
|
61
|
-
return userToken;
|
|
62
|
-
};
|
|
1
|
+
import { post } from "./request.js";
|
|
2
|
+
import type { IStsToken, IStsTokenReq } from "./types.js";
|
|
3
|
+
import { getUserTokenCache, setUserTokenCache } from "./userInfo.js";
|
|
4
|
+
|
|
5
|
+
export const getStsToken = async (name: string, botToken: string) => {
|
|
6
|
+
// 确保 userToken 已缓存(如果未缓存会自动获取并缓存)
|
|
7
|
+
await getUserToken(botToken);
|
|
8
|
+
|
|
9
|
+
const response = await post<IStsTokenReq, IStsToken>(
|
|
10
|
+
"/user/getStsToken",
|
|
11
|
+
{
|
|
12
|
+
sourceFileName: name,
|
|
13
|
+
isPrivate: 0,
|
|
14
|
+
},
|
|
15
|
+
{ botToken },
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (!response || !response.data || !response.data.bucket) {
|
|
19
|
+
throw new Error("获取 OSS 临时凭证失败");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return response.data;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 通过 botToken 查询 userToken
|
|
27
|
+
* @param botToken 机器人 token
|
|
28
|
+
* @returns userToken
|
|
29
|
+
*/
|
|
30
|
+
export const queryUserTokenByBotToken = async (botToken: string): Promise<string> => {
|
|
31
|
+
const response = await post<{botToken: string}, {token: string}>("/organization/queryUserTokenByBotToken", {
|
|
32
|
+
botToken,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response || !response.data || !response.data.token) {
|
|
36
|
+
throw new Error("获取绑定的用户信息失败");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return response.data.token;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 获取 userToken(优先从缓存获取,缓存未命中则调用 API)
|
|
44
|
+
* @param botToken 机器人 token
|
|
45
|
+
* @returns userToken
|
|
46
|
+
*/
|
|
47
|
+
export const getUserToken = async (botToken: string): Promise<string> => {
|
|
48
|
+
// 1. 尝试从缓存获取
|
|
49
|
+
const cachedToken = getUserTokenCache(botToken);
|
|
50
|
+
if (cachedToken) {
|
|
51
|
+
return cachedToken;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. 缓存未命中,调用 API 获取
|
|
55
|
+
console.log(`[api] cache miss, fetching userToken for botToken=${botToken.slice(0, 10)}...`);
|
|
56
|
+
const userToken = await queryUserTokenByBotToken(botToken);
|
|
57
|
+
|
|
58
|
+
// 3. 缓存新获取的 token
|
|
59
|
+
setUserTokenCache(botToken, userToken);
|
|
60
|
+
|
|
61
|
+
return userToken;
|
|
62
|
+
};
|