@dcrays/dcgchat 0.4.27 → 0.5.0
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/index.js +292 -0
- package/openclaw.plugin.json +17 -1
- package/package.json +18 -13
- package/schemas/gateway-cron-finished.payload.json +39 -0
- package/index.ts +0 -26
- package/src/agent.ts +0 -128
- package/src/bot.ts +0 -500
- package/src/channel.ts +0 -470
- package/src/cron.ts +0 -194
- package/src/cronToolCall.ts +0 -202
- package/src/gateway/index.ts +0 -447
- package/src/gateway/security.ts +0 -95
- package/src/gateway/socket.ts +0 -285
- 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/mime-types-3.0.2.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/monitor.ts +0 -165
- package/src/request/api.ts +0 -70
- package/src/request/oss.ts +0 -61
- package/src/request/request.ts +0 -192
- package/src/request/userInfo.ts +0 -99
- package/src/session.ts +0 -19
- package/src/sessionTermination.ts +0 -154
- package/src/skill.ts +0 -151
- package/src/tool.ts +0 -422
- package/src/tools/messageTool.ts +0 -224
- package/src/transport.ts +0 -203
- package/src/types.ts +0 -139
- package/src/utils/constant.ts +0 -7
- package/src/utils/gatewayMsgHanlder.ts +0 -55
- package/src/utils/global.ts +0 -160
- package/src/utils/log.ts +0 -15
- package/src/utils/params.ts +0 -88
- package/src/utils/searchFile.ts +0 -228
- package/src/utils/wsMessageHandler.ts +0 -64
- package/src/utils/zipExtract.ts +0 -97
- package/src/utils/zipPath.ts +0 -24
package/src/transport.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { clearSentMediaKeys, getWsConnection } from './utils/global.js'
|
|
2
|
-
import { dcgLogger } from './utils/log.js'
|
|
3
|
-
import type { IMsgParams } from './types.js'
|
|
4
|
-
import { getEffectiveMsgParams, getParamsDefaults } from './utils/params.js'
|
|
5
|
-
|
|
6
|
-
/** 用 sessionKey 从 map 取参,再合并 overrides(channel 出站、媒体等) */
|
|
7
|
-
export function mergeSessionParams(sessionKey: string, overrides?: Partial<IMsgParams>): IMsgParams {
|
|
8
|
-
const base = getEffectiveMsgParams(sessionKey)
|
|
9
|
-
if (!overrides) return base
|
|
10
|
-
return { ...base, ...overrides }
|
|
11
|
-
}
|
|
12
|
-
export function mergeDefaultParams(overrides?: Partial<IMsgParams>): IMsgParams {
|
|
13
|
-
const base = getParamsDefaults()
|
|
14
|
-
if (!overrides) return base
|
|
15
|
-
return { ...base, ...overrides }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type InboundMsgForContext = {
|
|
19
|
-
_userId: number | string
|
|
20
|
-
content: {
|
|
21
|
-
bot_token: string
|
|
22
|
-
domain_id?: string
|
|
23
|
-
app_id?: string
|
|
24
|
-
bot_id?: string
|
|
25
|
-
agent_id?: string
|
|
26
|
-
session_id: string
|
|
27
|
-
message_id: string
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type OpenclawBotChatEnvelope = {
|
|
32
|
-
messageType: 'openclaw_bot_chat'
|
|
33
|
-
_userId: number | undefined
|
|
34
|
-
source: 'client'
|
|
35
|
-
content: Record<string, unknown>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function isInboundWire(arg: unknown): arg is InboundMsgForContext {
|
|
39
|
-
return Boolean(arg && typeof arg === 'object' && '_userId' in arg && 'content' in arg)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** 下行 WebSocket 帧 → 内部上下文(字段缺省用 channel 配置补) */
|
|
43
|
-
function inboundToCtx(msg: InboundMsgForContext, d: IMsgParams): IMsgParams {
|
|
44
|
-
const c = msg.content
|
|
45
|
-
return {
|
|
46
|
-
userId: Number(msg._userId ?? d.userId),
|
|
47
|
-
botToken: c.bot_token ?? d.botToken,
|
|
48
|
-
domainId: String(c.domain_id ?? d.domainId),
|
|
49
|
-
appId: String(c.app_id ?? d.appId),
|
|
50
|
-
botId: c.bot_id,
|
|
51
|
-
agentId: c.agent_id,
|
|
52
|
-
sessionId: c.session_id,
|
|
53
|
-
messageId: c.message_id
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** 上行:与配置合并缺省后再 `...ctx` 覆盖(原 wsSendRaw) */
|
|
58
|
-
function mergeOutboundWithDefaults(ctx: IMsgParams, d: IMsgParams): IMsgParams {
|
|
59
|
-
return {
|
|
60
|
-
userId: Number(ctx.userId ?? d.userId),
|
|
61
|
-
botToken: ctx.botToken ?? d.botToken,
|
|
62
|
-
domainId: String(ctx.domainId ?? d.domainId),
|
|
63
|
-
appId: String(ctx.appId ?? d.appId),
|
|
64
|
-
...ctx
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 组装完整 wire `content` 对象:先写会话/机器人基础字段(回落到 d),再合并调用方传入的 payload。
|
|
70
|
-
* `content` 在使用处构造(如 response、state、files),同名键可覆盖基础字段。
|
|
71
|
-
*/
|
|
72
|
-
export function buildWireContent(base: IMsgParams, d: IMsgParams, content: Record<string, unknown>): Record<string, unknown> {
|
|
73
|
-
const resolvedBotToken = base.botToken ?? d.botToken
|
|
74
|
-
const domain = base.domainId ?? d.domainId
|
|
75
|
-
const app = base.appId ?? d.appId
|
|
76
|
-
return {
|
|
77
|
-
bot_token: base.botToken || resolvedBotToken,
|
|
78
|
-
domain_id: base.domainId || domain,
|
|
79
|
-
app_id: base.appId || app,
|
|
80
|
-
bot_id: base.botId,
|
|
81
|
-
agent_id: base.agentId,
|
|
82
|
-
session_id: base.sessionId,
|
|
83
|
-
message_id: base.messageId || Date.now().toString(),
|
|
84
|
-
...content
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** 上行:在已合并的 ctx 上套 openclaw_bot_chat 信封(messageType / _userId / source + content) */
|
|
89
|
-
function buildOutboundOpenclawBotChatEnvelope(
|
|
90
|
-
ctx: IMsgParams,
|
|
91
|
-
content: Record<string, unknown>,
|
|
92
|
-
opts?: { mergeChannelDefaults?: boolean }
|
|
93
|
-
): OpenclawBotChatEnvelope {
|
|
94
|
-
const d = getParamsDefaults()
|
|
95
|
-
const base = opts?.mergeChannelDefaults ? mergeOutboundWithDefaults(ctx, d) : ctx
|
|
96
|
-
return {
|
|
97
|
-
messageType: 'openclaw_bot_chat',
|
|
98
|
-
_userId: base.userId,
|
|
99
|
-
source: 'client',
|
|
100
|
-
content: buildWireContent(base, d, content)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* 下行解析为 DcgchatMsgContext,或上行组装 openclaw_bot_chat 信封。
|
|
106
|
-
* 上行时 `content` 由调用方传入;基础参数来自 `ctx` 与 `getParamsDefaults()`(可选 mergeChannelDefaults,同原 wsSendRaw)。
|
|
107
|
-
*/
|
|
108
|
-
export function buildOpenclawBotChat(msg: InboundMsgForContext): IMsgParams
|
|
109
|
-
export function buildOpenclawBotChat(
|
|
110
|
-
ctx: IMsgParams,
|
|
111
|
-
content: Record<string, unknown>,
|
|
112
|
-
opts?: { mergeChannelDefaults?: boolean }
|
|
113
|
-
): OpenclawBotChatEnvelope
|
|
114
|
-
export function buildOpenclawBotChat(
|
|
115
|
-
arg1: InboundMsgForContext | IMsgParams,
|
|
116
|
-
arg2?: Record<string, unknown>,
|
|
117
|
-
opts?: { mergeChannelDefaults?: boolean }
|
|
118
|
-
): IMsgParams | OpenclawBotChatEnvelope {
|
|
119
|
-
const d = getParamsDefaults()
|
|
120
|
-
|
|
121
|
-
if (arg2 === undefined && isInboundWire(arg1)) {
|
|
122
|
-
return inboundToCtx(arg1, d)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const ctx = arg1 as IMsgParams
|
|
126
|
-
return buildOutboundOpenclawBotChatEnvelope(ctx, arg2 ?? {}, opts)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function isWsOpen(): boolean {
|
|
130
|
-
const isOpen = getWsConnection()?.readyState === WebSocket.OPEN
|
|
131
|
-
if (!isOpen) {
|
|
132
|
-
dcgLogger(`server socket not ready ${getWsConnection()?.readyState}`, 'error')
|
|
133
|
-
}
|
|
134
|
-
return isOpen
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 聊天流路径:content 单独 JSON.stringify(双重编码),符合 dcgchat 协议。
|
|
139
|
-
* `ctx` 须由调用方用 getEffectiveMsgParams(sessionKey) 等解析好;`content` 为完整业务 payload。
|
|
140
|
-
*/
|
|
141
|
-
export function wsSend(ctx: IMsgParams, content: Record<string, unknown>): boolean {
|
|
142
|
-
const ws = getWsConnection()
|
|
143
|
-
if (ws?.readyState !== WebSocket.OPEN) return false
|
|
144
|
-
const envelope = buildOpenclawBotChat(ctx, content)
|
|
145
|
-
ws.send(JSON.stringify({ ...envelope, content: JSON.stringify(envelope.content) }))
|
|
146
|
-
return true
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* 媒体 / channel 出站:content 保持嵌套对象(单次编码)。
|
|
151
|
-
* `ctx` 须由调用方解析(如需合并覆盖可先 mergeSessionParams)。
|
|
152
|
-
*/
|
|
153
|
-
export function wsSendRaw(ctx: IMsgParams, content: Record<string, unknown>, isLog = true): boolean {
|
|
154
|
-
const ws = getWsConnection()
|
|
155
|
-
if (ws?.readyState !== WebSocket.OPEN) {
|
|
156
|
-
dcgLogger(`server socket not ready ${ws?.readyState}`, 'error')
|
|
157
|
-
return false
|
|
158
|
-
}
|
|
159
|
-
const envelope = buildOpenclawBotChat(ctx, content, { mergeChannelDefaults: true })
|
|
160
|
-
ws.send(JSON.stringify(envelope))
|
|
161
|
-
if (isLog) {
|
|
162
|
-
dcgLogger('已发送:' + JSON.stringify(envelope))
|
|
163
|
-
}
|
|
164
|
-
return true
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function sendChunk(text: string, ctx: IMsgParams, chunkIdx: number): boolean {
|
|
168
|
-
return wsSend(ctx, { response: text, state: 'chunk', chunk_idx: chunkIdx })
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function sendFinal(ctx: IMsgParams, tag: string): boolean {
|
|
172
|
-
dcgLogger(` message handling complete state: to=${ctx.sessionId} final tag:${tag}`)
|
|
173
|
-
clearSentMediaKeys(ctx.sessionId)
|
|
174
|
-
return wsSend(ctx, { response: '', state: 'final' })
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function sendText(text: string, ctx: IMsgParams, event?: Record<string, unknown>): boolean {
|
|
178
|
-
return wsSend(ctx, { response: text, ...event })
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function sendError(errorMsg: string, ctx: IMsgParams): boolean {
|
|
182
|
-
return wsSend(ctx, { response: `[错误] ${errorMsg}`, state: 'final' })
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export function sendEventMessage(params: Record<string, string> = {}) {
|
|
186
|
-
const ctx = getParamsDefaults()
|
|
187
|
-
const ws = getWsConnection()
|
|
188
|
-
if (isWsOpen()) {
|
|
189
|
-
ws?.send(
|
|
190
|
-
JSON.stringify({
|
|
191
|
-
messageType: 'openclaw_bot_event',
|
|
192
|
-
source: 'client',
|
|
193
|
-
content: {
|
|
194
|
-
bot_token: ctx.botToken,
|
|
195
|
-
domain_id: ctx.domainId,
|
|
196
|
-
app_id: ctx.appId,
|
|
197
|
-
bot_id: ctx.botId,
|
|
198
|
-
...params
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
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
|
-
* 内置 `message` 工具走 OpenClaw 目标解析:`true`(默认)时仅将符合 sessionKey 形态的字符串视为合法 target,
|
|
16
|
-
* 纯数字(WS userId 等)会解析失败;设为 `false` 恢复旧版宽松行为(不推荐)。
|
|
17
|
-
*/
|
|
18
|
-
strictMessageToolTarget?: boolean
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type ResolvedDcgchatAccount = {
|
|
22
|
-
accountId: string
|
|
23
|
-
enabled: boolean
|
|
24
|
-
configured: boolean
|
|
25
|
-
wsUrl: string
|
|
26
|
-
botToken: string
|
|
27
|
-
userId: string
|
|
28
|
-
domainId?: string
|
|
29
|
-
appId?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 下行消息:后端 → OpenClaw(用户发的消息)
|
|
34
|
-
*/
|
|
35
|
-
// export type InboundMessage = {
|
|
36
|
-
// type: "message";
|
|
37
|
-
// userId: string;
|
|
38
|
-
// text: string;
|
|
39
|
-
// };
|
|
40
|
-
export type InboundMessage = {
|
|
41
|
-
messageType: string // "openclaw_bot_chat",
|
|
42
|
-
_userId: number
|
|
43
|
-
source: string // 'server',
|
|
44
|
-
// content: string;
|
|
45
|
-
content: {
|
|
46
|
-
skills_scope: Record<string, any>[]
|
|
47
|
-
bot_token: string
|
|
48
|
-
agent_clone_code?: string
|
|
49
|
-
domain_id?: string
|
|
50
|
-
app_id?: string
|
|
51
|
-
bot_id?: string
|
|
52
|
-
agent_id?: string
|
|
53
|
-
session_id: string
|
|
54
|
-
real_mobook: string | number
|
|
55
|
-
message_id: string
|
|
56
|
-
text: string
|
|
57
|
-
files?: {
|
|
58
|
-
url: string
|
|
59
|
-
name: string
|
|
60
|
-
}[]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// {"_userId":40,"content":"{\"bot_token\":\"sk_b7f8a3e1c5d24e6f8a1b3c4d5e6f7a8b\",\"session_id\":\"1\",\"message_id\":\"1\",\"text\":\"你好\"}","messageType":"openclaw_bot_chat","msgId":398599,"source":"server","title":"OPENCLAW机器人对话"}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 上行消息:OpenClaw → 后端(Agent 回复)
|
|
68
|
-
*/
|
|
69
|
-
// export type OutboundReply = {
|
|
70
|
-
// type: "reply";
|
|
71
|
-
// userId: string;
|
|
72
|
-
// text: string;
|
|
73
|
-
// };
|
|
74
|
-
|
|
75
|
-
export type OutboundReply = {
|
|
76
|
-
messageType: string // "openclaw_bot_chat",
|
|
77
|
-
_userId: number // 100
|
|
78
|
-
source: string // 'client',
|
|
79
|
-
// content: string;
|
|
80
|
-
content: {
|
|
81
|
-
bot_token: string // ""
|
|
82
|
-
session_id: string // ""
|
|
83
|
-
message_id: string // ""
|
|
84
|
-
response: string // ""
|
|
85
|
-
state: string // final, chunk
|
|
86
|
-
domain_id?: string
|
|
87
|
-
app_id?: string
|
|
88
|
-
bot_id?: string
|
|
89
|
-
agent_id?: string
|
|
90
|
-
files?: { url: string; name: string }[]
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface IResponse<T = unknown> {
|
|
95
|
-
/** 响应状态码 */
|
|
96
|
-
code?: number | string
|
|
97
|
-
/** 响应数据 */
|
|
98
|
-
data?: T
|
|
99
|
-
/** 响应消息 */
|
|
100
|
-
message?: string
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface IStsToken {
|
|
104
|
-
bucket: string
|
|
105
|
-
endPoint: string
|
|
106
|
-
expiration: string
|
|
107
|
-
ossFileKey: string
|
|
108
|
-
policy: string
|
|
109
|
-
region: string
|
|
110
|
-
signature: string
|
|
111
|
-
sourceFileName: string
|
|
112
|
-
stsEndPoint: string
|
|
113
|
-
tempAccessKeyId: string
|
|
114
|
-
tempAccessKeySecret: string
|
|
115
|
-
tempSecurityToken: string
|
|
116
|
-
uploadDir: string
|
|
117
|
-
protocol: string
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface IStsTokenReq {
|
|
121
|
-
sourceFileName: string
|
|
122
|
-
isPrivate: number
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export interface IMsgParams {
|
|
126
|
-
userId?: number
|
|
127
|
-
botToken?: string
|
|
128
|
-
sessionId?: string
|
|
129
|
-
messageId?: string
|
|
130
|
-
domainId?: string
|
|
131
|
-
appId?: string
|
|
132
|
-
botId?: string
|
|
133
|
-
agentId?: string
|
|
134
|
-
/** 与 OpenClaw 路由一致,用于 map 与异步链路(工具 / HTTP / cron)对齐当前会话 */
|
|
135
|
-
sessionKey?: string
|
|
136
|
-
real_mobook?: string | number
|
|
137
|
-
is_finish?: number
|
|
138
|
-
message_tags?: Record<string, string>
|
|
139
|
-
}
|
package/src/utils/constant.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export const ENV: 'production' | 'test' | 'develop' = 'production'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const systemCommand = ['/new', '/status']
|
|
5
|
-
export const stopCommand = ['/stop']
|
|
6
|
-
|
|
7
|
-
export const ignoreToolCommand = ['/search', '/abort', '/queue interrupt', ...systemCommand, ...stopCommand]
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { GatewayEvent } from '../gateway/index.js'
|
|
2
|
-
import { finishedDcgchatCron, sendDcgchatCron } from '../cron.js'
|
|
3
|
-
import { dcgLogger } from './log.js'
|
|
4
|
-
import { clearParamsMessage, getEffectiveMsgParams, getSessionKeyBySubAgentRunId } from './params.js'
|
|
5
|
-
import { sendChunk, sendFinal, sendText } from '../transport.js'
|
|
6
|
-
import { resetSubagentStateForRequesterSession } from '../tool.js'
|
|
7
|
-
import { setMsgStatus } from './global.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 处理网关 event 帧的副作用(agent 流式输出、cron 同步),并构造供上层分发的 GatewayEvent。
|
|
11
|
-
*/
|
|
12
|
-
export function handleGatewayEventMessage(msg: { event?: string; payload?: Record<string, unknown>; seq?: number }): GatewayEvent {
|
|
13
|
-
try {
|
|
14
|
-
// 子agent消息输出
|
|
15
|
-
if (msg.event === 'agent') {
|
|
16
|
-
const pl = msg.payload as { runId: string; data?: { delta?: unknown } }
|
|
17
|
-
const sessionKey = getSessionKeyBySubAgentRunId(pl.runId)
|
|
18
|
-
const outboundCtx = getEffectiveMsgParams(sessionKey)
|
|
19
|
-
if (pl.data?.delta) {
|
|
20
|
-
if (outboundCtx.sessionId) {
|
|
21
|
-
sendChunk(pl.data.delta as string, outboundCtx, 0)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
// 定时任务
|
|
26
|
-
if (msg.event === 'cron') {
|
|
27
|
-
const p = msg.payload
|
|
28
|
-
dcgLogger(`[Gateway] 收到定时任务事件: ${JSON.stringify(p)}`)
|
|
29
|
-
if (p?.action === 'added') {
|
|
30
|
-
sendDcgchatCron(p?.jobId as string)
|
|
31
|
-
}
|
|
32
|
-
if (p?.action === 'updated') {
|
|
33
|
-
sendDcgchatCron(p?.jobId as string)
|
|
34
|
-
}
|
|
35
|
-
if (p?.action === 'removed') {
|
|
36
|
-
sendDcgchatCron(p?.jobId as string)
|
|
37
|
-
}
|
|
38
|
-
if (p?.action === 'finished' && p?.status === 'ok') {
|
|
39
|
-
const hasFileOutput = p.delivered === true && p.deliveryStatus === 'delivered'
|
|
40
|
-
let summary = p?.summary as string
|
|
41
|
-
if (summary.indexOf('HEARTBEAT_OK') >= 0 && summary !== 'HEARTBEAT_OK') {
|
|
42
|
-
summary = summary.replace('HEARTBEAT_OK', '')
|
|
43
|
-
}
|
|
44
|
-
finishedDcgchatCron(p?.jobId as string, summary, hasFileOutput)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
} catch (error) {
|
|
48
|
-
dcgLogger(`[Gateway] 处理事件失败: ${error}`, 'error')
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
type: msg.event as string,
|
|
52
|
-
payload: msg.payload,
|
|
53
|
-
seq: msg.seq as number | undefined
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/utils/global.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import type WebSocket from 'ws'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import os from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { createPluginRuntimeStore, type OpenClawConfig, type PluginRuntime } from 'openclaw/plugin-sdk'
|
|
6
|
-
import { channelInfo, ENV } from './constant.js'
|
|
7
|
-
import { dcgLogger } from './log.js'
|
|
8
|
-
|
|
9
|
-
/** socket connection */
|
|
10
|
-
let ws: WebSocket | null = null
|
|
11
|
-
|
|
12
|
-
export function setWsConnection(next: WebSocket | null) {
|
|
13
|
-
ws = next
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getWsConnection(): WebSocket | null {
|
|
17
|
-
return ws
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let config: OpenClawConfig | null = null
|
|
21
|
-
|
|
22
|
-
export function setOpenClawConfig(next: OpenClawConfig | null) {
|
|
23
|
-
config = next
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getOpenClawConfig(): OpenClawConfig | null {
|
|
27
|
-
return config
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getWorkspacePath(): string | null {
|
|
31
|
-
const workspacePath = path.join(
|
|
32
|
-
os.homedir(),
|
|
33
|
-
config?.channels?.["dcgchat"]?.appId == 110 ? '.mobook' : '.openclaw',
|
|
34
|
-
'workspace'
|
|
35
|
-
)
|
|
36
|
-
if (fs.existsSync(workspacePath)) {
|
|
37
|
-
return workspacePath
|
|
38
|
-
}
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let workspaceDir: string = getWorkspacePath() ?? ''
|
|
43
|
-
|
|
44
|
-
export function setWorkspaceDir(dir?: string) {
|
|
45
|
-
if (dir) {
|
|
46
|
-
workspaceDir = dir
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getWorkspaceDir(): string {
|
|
51
|
-
if (!workspaceDir) {
|
|
52
|
-
dcgLogger?.('Workspace directory not initialized', 'error')
|
|
53
|
-
}
|
|
54
|
-
return workspaceDir
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const { setRuntime: setDcgchatRuntime, getRuntime: getDcgchatRuntime } = createPluginRuntimeStore<PluginRuntime>(
|
|
58
|
-
`${"dcgchat"} runtime not initialized`
|
|
59
|
-
)
|
|
60
|
-
export { setDcgchatRuntime, getDcgchatRuntime }
|
|
61
|
-
|
|
62
|
-
export type MsgSessionStatus = 'running' | 'finished' | ''
|
|
63
|
-
|
|
64
|
-
const msgStatusBySessionKey = new Map<string, MsgSessionStatus>()
|
|
65
|
-
|
|
66
|
-
export function setMsgStatus(sessionKey: string, status: MsgSessionStatus) {
|
|
67
|
-
if (!sessionKey?.trim()) return
|
|
68
|
-
if (status === '') {
|
|
69
|
-
msgStatusBySessionKey.delete(sessionKey)
|
|
70
|
-
} else {
|
|
71
|
-
msgStatusBySessionKey.set(sessionKey, status)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function getMsgStatus(sessionKey: string): MsgSessionStatus {
|
|
76
|
-
if (!sessionKey?.trim()) return ''
|
|
77
|
-
return msgStatusBySessionKey.get(sessionKey) ?? ''
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const getMediaKey = (url: string) => url.split(/[\\/]/).pop() ?? url
|
|
81
|
-
|
|
82
|
-
/** 已发送媒体去重:外层 messageId → 内层该会话下已发送的媒体 key(文件名) */
|
|
83
|
-
const sentMediaKeysBySession = new Map<string, Set<string>>()
|
|
84
|
-
|
|
85
|
-
function getSessionMediaSet(sessionId: string): Set<string> {
|
|
86
|
-
let set = sentMediaKeysBySession.get(sessionId)
|
|
87
|
-
if (!set) {
|
|
88
|
-
set = new Set<string>()
|
|
89
|
-
sentMediaKeysBySession.set(sessionId, set)
|
|
90
|
-
}
|
|
91
|
-
return set
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function addSentMediaKey(messageId: string, url: string) {
|
|
95
|
-
getSessionMediaSet(messageId).add(getMediaKey(url))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function hasSentMediaKey(sessionId: string, url: string): boolean {
|
|
99
|
-
return sentMediaKeysBySession.get(sessionId)?.has(getMediaKey(url)) ?? false
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** 不传 messageId 时清空全部会话;传入则只清空该会话 */
|
|
103
|
-
export function clearSentMediaKeys(sessionId?: string) {
|
|
104
|
-
if (sessionId) {
|
|
105
|
-
sentMediaKeysBySession.delete(sessionId)
|
|
106
|
-
} else {
|
|
107
|
-
sentMediaKeysBySession.clear()
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** 每个 sessionKey 下多个定时任务 messageId,入队在尾、出队在头(FIFO) */
|
|
112
|
-
const cronMessageIdMap = new Map<string, string[]>()
|
|
113
|
-
|
|
114
|
-
export function setCronMessageId(sk: string, messageId: string | number | null | undefined) {
|
|
115
|
-
const mid = messageId != null && messageId !== '' ? String(messageId).trim() : ''
|
|
116
|
-
if (!sk?.trim() || !mid) return
|
|
117
|
-
let q = cronMessageIdMap.get(sk)
|
|
118
|
-
if (!q) {
|
|
119
|
-
q = []
|
|
120
|
-
cronMessageIdMap.set(sk, q)
|
|
121
|
-
}
|
|
122
|
-
q.push(mid)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** 窥视队首 messageId(不移除),供发送中与 finished 配对使用 */
|
|
126
|
-
export function getCronMessageId(sk: string): string {
|
|
127
|
-
if (!sk?.trim()) return ''
|
|
128
|
-
return cronMessageIdMap.get(sk)?.[0] ?? ''
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** 弹出队首一条,与一次 finished 对应;队列为空时移除 key */
|
|
132
|
-
export function removeCronMessageId(sk: string) {
|
|
133
|
-
if (!sk?.trim()) return
|
|
134
|
-
const q = cronMessageIdMap.get(sk)
|
|
135
|
-
if (!q?.length) return
|
|
136
|
-
q.shift()
|
|
137
|
-
if (q.length === 0) {
|
|
138
|
-
cronMessageIdMap.delete(sk)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export const getSessionKey = (content: any, accountId: string) => {
|
|
143
|
-
const { real_mobook, agent_id, agent_clone_code, session_id } = content
|
|
144
|
-
const core = getDcgchatRuntime()
|
|
145
|
-
|
|
146
|
-
const agentCode = agent_clone_code || 'main'
|
|
147
|
-
|
|
148
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
149
|
-
cfg: getOpenClawConfig() as OpenClawConfig,
|
|
150
|
-
channel: "dcgchat",
|
|
151
|
-
accountId: accountId || 'default',
|
|
152
|
-
peer: { kind: 'direct', id: session_id }
|
|
153
|
-
})
|
|
154
|
-
return real_mobook == '1' ? route.sessionKey : `agent:${agentCode}:mobook:direct:${agent_id}:${session_id}`.toLowerCase()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function getInfoBySessionKey(sk: string): { sessionId: string; agentId: string } {
|
|
158
|
-
const sessionInfo = sk.split(':')
|
|
159
|
-
return { sessionId: sessionInfo.at(-1) ?? '', agentId: sessionInfo.at(-2) ?? '' }
|
|
160
|
-
}
|
package/src/utils/log.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { RuntimeEnv } from 'openclaw/plugin-sdk'
|
|
2
|
-
|
|
3
|
-
let logger: RuntimeEnv | null = null
|
|
4
|
-
|
|
5
|
-
export function setLogger(next: RuntimeEnv | null) {
|
|
6
|
-
logger = next
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function dcgLogger(message: string, type: 'log' | 'error' = 'log'): void {
|
|
10
|
-
if (logger) {
|
|
11
|
-
logger[type](`书灵墨宝🚀 ~ [${new Date().toISOString()}] ${message}`)
|
|
12
|
-
} else {
|
|
13
|
-
console[type](`书灵墨宝🚀 ~ ${new Date().toISOString()} [${"dcgchat"}]: ${message}`)
|
|
14
|
-
}
|
|
15
|
-
}
|
package/src/utils/params.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { channelInfo, ENV } from './constant.js'
|
|
2
|
-
import { getOpenClawConfig } from './global.js'
|
|
3
|
-
import type { DcgchatConfig, IMsgParams } from '../types.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* map key 是 session_key,value 为该会话下已 merge 后的消息参数。
|
|
7
|
-
*/
|
|
8
|
-
const paramsMessageMap = new Map<string, IMsgParams>()
|
|
9
|
-
|
|
10
|
-
/** 从 OpenClaw 配置读取当前 channel 的基础参数(唯一来源,供 transport / resolve 等复用) */
|
|
11
|
-
export function getParamsDefaults(): IMsgParams {
|
|
12
|
-
const ch = (getOpenClawConfig()?.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {}
|
|
13
|
-
return {
|
|
14
|
-
userId: Number(ch.userId ?? 0),
|
|
15
|
-
botToken: ch.botToken ?? '',
|
|
16
|
-
sessionId: '',
|
|
17
|
-
messageId: '',
|
|
18
|
-
domainId: String(ch.domainId ?? '1000'),
|
|
19
|
-
appId: String(ch.appId ?? '100'),
|
|
20
|
-
botId: '',
|
|
21
|
-
agentId: '',
|
|
22
|
-
sessionKey: ''
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function resolveParamsMessage(params: Partial<IMsgParams>): IMsgParams {
|
|
27
|
-
const defaults = getParamsDefaults()
|
|
28
|
-
return {
|
|
29
|
-
userId: Number(params.userId ?? defaults.userId),
|
|
30
|
-
botToken: params.botToken ?? defaults.botToken,
|
|
31
|
-
sessionId: params.sessionId ?? defaults.sessionId,
|
|
32
|
-
messageId: params.messageId ?? defaults.messageId,
|
|
33
|
-
domainId: String(params.domainId ?? defaults.domainId),
|
|
34
|
-
appId: String(params.appId ?? defaults.appId),
|
|
35
|
-
botId: params.botId ?? defaults.botId,
|
|
36
|
-
agentId: params.agentId ?? defaults.agentId,
|
|
37
|
-
sessionKey: params.sessionKey ?? defaults.sessionKey
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 统一取值入口:显式 sessionKey,或回落到当前会话;再与配置缺省 merge,保证字段完整。
|
|
43
|
-
*/
|
|
44
|
-
export function getEffectiveMsgParams(sessionKey?: string): IMsgParams {
|
|
45
|
-
const stored = sessionKey ? paramsMessageMap.get(sessionKey) : undefined
|
|
46
|
-
return stored ? resolveParamsMessage(stored) : getParamsDefaults()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Agent `dcgchat_message` / 出站 `target` 应为 `dcgSessionKey`(如 `agent:main:mobook:direct:...`)。
|
|
51
|
-
* `setParamsMessage` 的 key 与此一致;查不到 map 时回落到配置缺省(无会话级 messageId/sessionId)。
|
|
52
|
-
*/
|
|
53
|
-
export function getOutboundMsgParams(preferredKey: string): IMsgParams {
|
|
54
|
-
const k = preferredKey?.trim()
|
|
55
|
-
if (k && paramsMessageMap.has(k)) {
|
|
56
|
-
return getEffectiveMsgParams(k)
|
|
57
|
-
}
|
|
58
|
-
return getEffectiveMsgParams()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function setParamsMessage(sessionKey: string, params: Partial<IMsgParams>) {
|
|
62
|
-
if (!sessionKey) return
|
|
63
|
-
const previous = paramsMessageMap.get(sessionKey)
|
|
64
|
-
const base = previous ? resolveParamsMessage(previous) : getParamsDefaults()
|
|
65
|
-
paramsMessageMap.set(sessionKey, resolveParamsMessage({ ...base, ...params, sessionKey }))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function getParamsMessage(sessionKey: string): IMsgParams | undefined {
|
|
69
|
-
return paramsMessageMap.get(sessionKey)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function clearParamsMessage(sessionKey: string): void {
|
|
73
|
-
const k = sessionKey?.trim()
|
|
74
|
-
if (!k) return
|
|
75
|
-
paramsMessageMap.delete(k)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// sessionKey 对应的 子agent的runId
|
|
79
|
-
const subagentRunIdMap = new Map<string, string>()
|
|
80
|
-
export function getSessionKeyBySubAgentRunId(runId: string): string | undefined {
|
|
81
|
-
return subagentRunIdMap.get(runId)
|
|
82
|
-
}
|
|
83
|
-
export function setSessionKeyBySubAgentRunId(runId: string, sessionKey: string) {
|
|
84
|
-
subagentRunIdMap.set(runId, sessionKey)
|
|
85
|
-
}
|
|
86
|
-
export function deleteSessionKeyBySubAgentRunId(runId: string) {
|
|
87
|
-
subagentRunIdMap.delete(runId)
|
|
88
|
-
}
|