@dcrays/dcgchat-test 0.1.9
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 -0
- package/index.ts +24 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +58 -0
- package/src/api.ts +62 -0
- package/src/bot.ts +272 -0
- package/src/channel.ts +192 -0
- package/src/connection.ts +11 -0
- package/src/libs/ali-oss-6.23.0.tgz +0 -0
- package/src/libs/axios-1.13.6.tgz +0 -0
- package/src/libs/md5-2.3.0.tgz +0 -0
- package/src/libs/unzipper-0.12.3.tgz +0 -0
- package/src/libs/ws-8.19.0.tgz +0 -0
- package/src/log.ts +46 -0
- package/src/monitor.ts +190 -0
- package/src/oss.ts +72 -0
- package/src/request.ts +194 -0
- package/src/runtime.ts +38 -0
- package/src/skill.ts +111 -0
- package/src/tool.ts +65 -0
- package/src/types.ts +103 -0
- package/src/userInfo.ts +97 -0
package/src/tool.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
3
|
+
import { getWsConnection } from "./connection.js";
|
|
4
|
+
import { logDcgchat } from "./log.js";
|
|
5
|
+
|
|
6
|
+
let msgParams = {} as {
|
|
7
|
+
userId: number;
|
|
8
|
+
token: string
|
|
9
|
+
sessionId: string
|
|
10
|
+
messageId: string
|
|
11
|
+
}
|
|
12
|
+
export function setMsgParams(params: any) {
|
|
13
|
+
msgParams = params;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getMsgParams() {
|
|
17
|
+
return msgParams;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function monitoringToolMessage(api: OpenClawPluginApi) {
|
|
21
|
+
api.on("after_tool_call", (event, payload) => {
|
|
22
|
+
const ws = getWsConnection()
|
|
23
|
+
const params = getMsgParams();
|
|
24
|
+
//
|
|
25
|
+
if (ws?.readyState === WebSocket.OPEN && params?.sessionId) {
|
|
26
|
+
ws.send(JSON.stringify({
|
|
27
|
+
messageType: "openclaw_bot_chat",
|
|
28
|
+
_userId: params?.userId,
|
|
29
|
+
source: "client",
|
|
30
|
+
content: {
|
|
31
|
+
bot_token: params?.token,
|
|
32
|
+
response: 'all_finished',
|
|
33
|
+
session_id:params?.sessionId,
|
|
34
|
+
message_id: params?.messageId || Date.now().toString()
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
ws.send(JSON.stringify({
|
|
38
|
+
messageType: "openclaw_bot_chat",
|
|
39
|
+
_userId: params?.userId,
|
|
40
|
+
source: "client",
|
|
41
|
+
content: {
|
|
42
|
+
bot_token: params?.token,
|
|
43
|
+
response: JSON.stringify({
|
|
44
|
+
type: 'tool_call',
|
|
45
|
+
...event
|
|
46
|
+
}),
|
|
47
|
+
session_id:params?.sessionId,
|
|
48
|
+
message_id: params?.messageId || Date.now().toString()
|
|
49
|
+
},
|
|
50
|
+
}));
|
|
51
|
+
ws.send(JSON.stringify({
|
|
52
|
+
messageType: "openclaw_bot_chat",
|
|
53
|
+
_userId: params?.userId,
|
|
54
|
+
source: "client",
|
|
55
|
+
content: {
|
|
56
|
+
bot_token: params?.token,
|
|
57
|
+
response: 'all_finished',
|
|
58
|
+
session_id:params?.sessionId,
|
|
59
|
+
message_id: params?.messageId || Date.now().toString()
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
logDcgchat.info(`dcgchat: tool message to ${params?.sessionId}, ${JSON.stringify(event)}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件配置(channels.dcgchat 下的字段)
|
|
3
|
+
*/
|
|
4
|
+
export type DcgchatConfig = {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
/** 后端 WebSocket 地址,例如 ws://localhost:8080/openclaw/ws */
|
|
7
|
+
wsUrl?: string;
|
|
8
|
+
/** 连接认证 token */
|
|
9
|
+
botToken?: string;
|
|
10
|
+
/** 用户标识 */
|
|
11
|
+
userId?: string;
|
|
12
|
+
domainId?: string;
|
|
13
|
+
appId?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ResolvedDcgchatAccount = {
|
|
17
|
+
accountId: string;
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
configured: boolean;
|
|
20
|
+
wsUrl: string;
|
|
21
|
+
botToken: string;
|
|
22
|
+
userId: string;
|
|
23
|
+
domainId?: string;
|
|
24
|
+
appId?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 下行消息:后端 → OpenClaw(用户发的消息)
|
|
29
|
+
*/
|
|
30
|
+
// export type InboundMessage = {
|
|
31
|
+
// type: "message";
|
|
32
|
+
// userId: string;
|
|
33
|
+
// text: string;
|
|
34
|
+
// };
|
|
35
|
+
export type InboundMessage = {
|
|
36
|
+
messageType: string; // "openclaw_bot_chat",
|
|
37
|
+
_userId: number;
|
|
38
|
+
source: string; // 'server',
|
|
39
|
+
// content: string;
|
|
40
|
+
content: {
|
|
41
|
+
bot_token: string;
|
|
42
|
+
session_id: string;
|
|
43
|
+
message_id: string;
|
|
44
|
+
text: string;
|
|
45
|
+
file_urls?: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// {"_userId":40,"content":"{\"bot_token\":\"sk_b7f8a3e1c5d24e6f8a1b3c4d5e6f7a8b\",\"session_id\":\"1\",\"message_id\":\"1\",\"text\":\"你好\"}","messageType":"openclaw_bot_chat","msgId":398599,"source":"server","title":"OPENCLAW机器人对话"}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 上行消息:OpenClaw → 后端(Agent 回复)
|
|
53
|
+
*/
|
|
54
|
+
// export type OutboundReply = {
|
|
55
|
+
// type: "reply";
|
|
56
|
+
// userId: string;
|
|
57
|
+
// text: string;
|
|
58
|
+
// };
|
|
59
|
+
|
|
60
|
+
export type OutboundReply = {
|
|
61
|
+
messageType: string; // "openclaw_bot_chat",
|
|
62
|
+
_userId: number; // 100
|
|
63
|
+
source: string; // 'client',
|
|
64
|
+
// content: string;
|
|
65
|
+
content: {
|
|
66
|
+
bot_token: string; // ""
|
|
67
|
+
session_id: string; // ""
|
|
68
|
+
message_id: string; // ""
|
|
69
|
+
response: string; // ""
|
|
70
|
+
state: string; // final, chunk
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export interface IResponse<T = unknown> {
|
|
75
|
+
/** 响应状态码 */
|
|
76
|
+
code?: number | string;
|
|
77
|
+
/** 响应数据 */
|
|
78
|
+
data?: T;
|
|
79
|
+
/** 响应消息 */
|
|
80
|
+
message?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface IStsToken {
|
|
84
|
+
bucket: string;
|
|
85
|
+
endPoint: string;
|
|
86
|
+
expiration: string;
|
|
87
|
+
ossFileKey: string;
|
|
88
|
+
policy: string;
|
|
89
|
+
region: string;
|
|
90
|
+
signature: string;
|
|
91
|
+
sourceFileName: string;
|
|
92
|
+
stsEndPoint: string;
|
|
93
|
+
tempAccessKeyId: string;
|
|
94
|
+
tempAccessKeySecret: string;
|
|
95
|
+
tempSecurityToken: string;
|
|
96
|
+
uploadDir: string;
|
|
97
|
+
protocol: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface IStsTokenReq {
|
|
101
|
+
sourceFileName: string
|
|
102
|
+
isPrivate: number
|
|
103
|
+
}
|
package/src/userInfo.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* userToken 缓存管理模块
|
|
3
|
+
* 负责维护 botToken -> userToken 的映射关系,支持自动过期
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// userToken 缓存配置
|
|
7
|
+
const TOKEN_CACHE_DURATION = 60 * 60 * 1000; // 1小时
|
|
8
|
+
|
|
9
|
+
type TokenCacheEntry = {
|
|
10
|
+
token: string;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// 内存缓存:botToken -> { token, expiresAt }
|
|
15
|
+
const tokenCache = new Map<string, TokenCacheEntry>();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 设置 userToken 缓存
|
|
19
|
+
* @param botToken 机器人 token
|
|
20
|
+
* @param userToken 用户 token
|
|
21
|
+
*/
|
|
22
|
+
export function setUserTokenCache(botToken: string, userToken: string): void {
|
|
23
|
+
const expiresAt = Date.now() + TOKEN_CACHE_DURATION;
|
|
24
|
+
tokenCache.set(botToken, { token: userToken, expiresAt });
|
|
25
|
+
console.log(
|
|
26
|
+
`[token-cache] cached userToken for botToken=${botToken.slice(0, 10)}..., expires at ${new Date(expiresAt).toISOString()}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 获取 userToken 缓存(自动检查过期)
|
|
32
|
+
* @param botToken 机器人 token
|
|
33
|
+
* @returns userToken 或 null(未找到或已过期)
|
|
34
|
+
*/
|
|
35
|
+
export function getUserTokenCache(botToken: string): string | null {
|
|
36
|
+
const entry = tokenCache.get(botToken);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
console.log(`[token-cache] no cache found for botToken=${botToken.slice(0, 10)}...`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 检查是否过期
|
|
43
|
+
if (Date.now() >= entry.expiresAt) {
|
|
44
|
+
console.log(`[token-cache] cache expired for botToken=${botToken.slice(0, 10)}..., removing`);
|
|
45
|
+
tokenCache.delete(botToken);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(
|
|
50
|
+
`[token-cache] cache hit for botToken=${botToken.slice(0, 10)}..., valid until ${new Date(entry.expiresAt).toISOString()}`,
|
|
51
|
+
);
|
|
52
|
+
return entry.token;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 清除指定 botToken 的缓存
|
|
57
|
+
* @param botToken 机器人 token
|
|
58
|
+
*/
|
|
59
|
+
export function clearUserTokenCache(botToken: string): void {
|
|
60
|
+
tokenCache.delete(botToken);
|
|
61
|
+
console.log(`[token-cache] cleared cache for botToken=${botToken.slice(0, 10)}...`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 清除所有缓存
|
|
66
|
+
*/
|
|
67
|
+
export function clearAllUserTokenCache(): void {
|
|
68
|
+
tokenCache.clear();
|
|
69
|
+
console.log(`[token-cache] cleared all token cache`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 获取缓存统计信息(用于调试)
|
|
74
|
+
*/
|
|
75
|
+
export function getTokenCacheStats(): {
|
|
76
|
+
total: number;
|
|
77
|
+
valid: number;
|
|
78
|
+
expired: number;
|
|
79
|
+
} {
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
let valid = 0;
|
|
82
|
+
let expired = 0;
|
|
83
|
+
|
|
84
|
+
for (const entry of tokenCache.values()) {
|
|
85
|
+
if (now < entry.expiresAt) {
|
|
86
|
+
valid++;
|
|
87
|
+
} else {
|
|
88
|
+
expired++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
total: tokenCache.size,
|
|
94
|
+
valid,
|
|
95
|
+
expired,
|
|
96
|
+
};
|
|
97
|
+
}
|