@dcrays/dcgchat-test 0.2.34 → 0.3.1
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.ts +2 -0
- package/package.json +1 -1
- package/src/bot.ts +77 -67
- package/src/channel.ts +25 -31
- package/src/cron.ts +138 -0
- package/src/gateway/index.ts +468 -0
- package/src/gateway/security.ts +95 -0
- package/src/gateway/socket.ts +285 -0
- package/src/monitor.ts +24 -27
- package/src/request/api.ts +4 -18
- package/src/request/oss.ts +5 -4
- package/src/request/request.ts +2 -2
- package/src/tool.ts +15 -18
- package/src/transport.ts +140 -47
- package/src/types.ts +12 -8
- package/src/utils/global.ts +0 -9
- package/src/utils/params.ts +65 -0
package/src/transport.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { getWsConnection } from './utils/global.js'
|
|
2
2
|
import { dcgLogger } from './utils/log.js'
|
|
3
|
+
import type { IMsgParams } from './types.js'
|
|
4
|
+
import { getEffectiveMsgParams, getParamsDefaults } from './utils/params.js'
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 }
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export
|
|
16
|
-
_userId: number
|
|
18
|
+
export type InboundMsgForContext = {
|
|
19
|
+
_userId: number | string
|
|
17
20
|
content: {
|
|
18
21
|
bot_token: string
|
|
19
22
|
domain_id?: string
|
|
@@ -23,86 +26,176 @@ export function createMsgContext(msg: {
|
|
|
23
26
|
session_id: string
|
|
24
27
|
message_id: string
|
|
25
28
|
}
|
|
26
|
-
}
|
|
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
|
|
27
45
|
return {
|
|
28
|
-
userId: msg._userId,
|
|
29
|
-
botToken:
|
|
30
|
-
domainId:
|
|
31
|
-
appId:
|
|
32
|
-
botId:
|
|
33
|
-
agentId:
|
|
34
|
-
sessionId:
|
|
35
|
-
messageId:
|
|
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
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
|
|
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
|
|
40
76
|
return {
|
|
41
|
-
bot_token:
|
|
42
|
-
domain_id:
|
|
43
|
-
app_id:
|
|
44
|
-
bot_id:
|
|
45
|
-
agent_id:
|
|
46
|
-
session_id:
|
|
47
|
-
message_id:
|
|
48
|
-
...
|
|
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
|
|
49
85
|
}
|
|
50
86
|
}
|
|
51
87
|
|
|
52
|
-
|
|
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
|
|
53
96
|
return {
|
|
54
|
-
messageType: 'openclaw_bot_chat'
|
|
55
|
-
_userId:
|
|
56
|
-
source: 'client'
|
|
57
|
-
content:
|
|
97
|
+
messageType: 'openclaw_bot_chat',
|
|
98
|
+
_userId: base.userId,
|
|
99
|
+
source: 'client',
|
|
100
|
+
content: buildWireContent(base, d, content)
|
|
58
101
|
}
|
|
59
102
|
}
|
|
60
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
|
+
|
|
61
129
|
export function isWsOpen(): boolean {
|
|
62
130
|
const isOpen = getWsConnection()?.readyState === WebSocket.OPEN
|
|
63
131
|
if (!isOpen) {
|
|
64
|
-
dcgLogger(`socket not ready ${getWsConnection()?.readyState}`, 'error')
|
|
132
|
+
dcgLogger(`server socket not ready ${getWsConnection()?.readyState}`, 'error')
|
|
65
133
|
}
|
|
66
134
|
return isOpen
|
|
67
135
|
}
|
|
68
136
|
|
|
69
137
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
138
|
+
* 聊天流路径:content 单独 JSON.stringify(双重编码),符合 dcgchat 协议。
|
|
139
|
+
* `ctx` 须由调用方用 getEffectiveMsgParams(sessionKey) 等解析好;`content` 为完整业务 payload。
|
|
72
140
|
*/
|
|
73
|
-
export function wsSend(ctx:
|
|
141
|
+
export function wsSend(ctx: IMsgParams, content: Record<string, unknown>): boolean {
|
|
74
142
|
const ws = getWsConnection()
|
|
75
143
|
if (ws?.readyState !== WebSocket.OPEN) return false
|
|
76
|
-
const envelope =
|
|
144
|
+
const envelope = buildOpenclawBotChat(ctx, content)
|
|
77
145
|
ws.send(JSON.stringify({ ...envelope, content: JSON.stringify(envelope.content) }))
|
|
78
146
|
return true
|
|
79
147
|
}
|
|
80
148
|
|
|
81
149
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
150
|
+
* 媒体 / channel 出站:content 保持嵌套对象(单次编码)。
|
|
151
|
+
* `ctx` 须由调用方解析(如需合并覆盖可先 mergeSessionParams)。
|
|
84
152
|
*/
|
|
85
|
-
export function wsSendRaw(ctx:
|
|
153
|
+
export function wsSendRaw(ctx: IMsgParams, content: Record<string, unknown>): boolean {
|
|
86
154
|
const ws = getWsConnection()
|
|
87
155
|
if (isWsOpen()) {
|
|
88
|
-
ws?.send(JSON.stringify(
|
|
156
|
+
ws?.send(JSON.stringify(buildOpenclawBotChat(ctx, content, { mergeChannelDefaults: true })))
|
|
89
157
|
}
|
|
90
158
|
return true
|
|
91
159
|
}
|
|
92
160
|
|
|
93
|
-
export function sendChunk(
|
|
161
|
+
export function sendChunk(text: string, ctx: IMsgParams): boolean {
|
|
94
162
|
return wsSend(ctx, { response: text, state: 'chunk' })
|
|
95
163
|
}
|
|
96
164
|
|
|
97
|
-
export function sendFinal(ctx:
|
|
165
|
+
export function sendFinal(ctx: IMsgParams): boolean {
|
|
98
166
|
dcgLogger(` message handling complete state: final`)
|
|
99
167
|
return wsSend(ctx, { response: '', state: 'final' })
|
|
100
168
|
}
|
|
101
169
|
|
|
102
|
-
export function sendText(
|
|
170
|
+
export function sendText(text: string, ctx: IMsgParams, event?: Record<string, unknown>): boolean {
|
|
103
171
|
return wsSend(ctx, { response: text, ...event })
|
|
104
172
|
}
|
|
105
173
|
|
|
106
|
-
export function sendError(
|
|
174
|
+
export function sendError(errorMsg: string, ctx: IMsgParams): boolean {
|
|
107
175
|
return wsSend(ctx, { response: `[错误] ${errorMsg}`, state: 'final' })
|
|
108
176
|
}
|
|
177
|
+
|
|
178
|
+
export function sendEventMessage(url: string, sessionKey: string) {
|
|
179
|
+
const ctx = getEffectiveMsgParams(sessionKey)
|
|
180
|
+
const ws = getWsConnection()
|
|
181
|
+
if (isWsOpen()) {
|
|
182
|
+
ws?.send(
|
|
183
|
+
JSON.stringify({
|
|
184
|
+
messageType: 'openclaw_bot_event',
|
|
185
|
+
source: 'client',
|
|
186
|
+
content: {
|
|
187
|
+
event_type: 'cron',
|
|
188
|
+
operation_type: 'install',
|
|
189
|
+
bot_token: ctx.botToken,
|
|
190
|
+
domain_id: ctx.domainId,
|
|
191
|
+
app_id: ctx.appId,
|
|
192
|
+
oss_url: url,
|
|
193
|
+
bot_id: ctx.botId,
|
|
194
|
+
agent_id: ctx.agentId,
|
|
195
|
+
session_id: ctx.sessionId,
|
|
196
|
+
message_id: ctx.messageId || Date.now().toString()
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -44,6 +44,7 @@ export type InboundMessage = {
|
|
|
44
44
|
bot_id?: string
|
|
45
45
|
agent_id?: string
|
|
46
46
|
session_id: string
|
|
47
|
+
real_mobook: string | number
|
|
47
48
|
message_id: string
|
|
48
49
|
text: string
|
|
49
50
|
files?: {
|
|
@@ -115,12 +116,15 @@ export interface IStsTokenReq {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
export interface IMsgParams {
|
|
118
|
-
userId
|
|
119
|
-
|
|
120
|
-
sessionId
|
|
121
|
-
messageId
|
|
122
|
-
domainId
|
|
123
|
-
appId
|
|
124
|
-
botId
|
|
125
|
-
agentId
|
|
119
|
+
userId?: number
|
|
120
|
+
botToken?: string
|
|
121
|
+
sessionId?: string
|
|
122
|
+
messageId?: string
|
|
123
|
+
domainId?: string
|
|
124
|
+
appId?: string
|
|
125
|
+
botId?: string
|
|
126
|
+
agentId?: string
|
|
127
|
+
/** 与 OpenClaw 路由一致,用于 map 与异步链路(工具 / HTTP / cron)对齐当前会话 */
|
|
128
|
+
sessionKey?: string
|
|
129
|
+
real_mobook?: string | number
|
|
126
130
|
}
|
package/src/utils/global.ts
CHANGED
|
@@ -24,7 +24,6 @@ export function getOpenClawConfig(): OpenClawConfig | null {
|
|
|
24
24
|
|
|
25
25
|
import type { OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk'
|
|
26
26
|
import { dcgLogger } from './log.js'
|
|
27
|
-
import { IMsgParams } from '../types.js'
|
|
28
27
|
import { channelInfo, ENV } from './constant.js'
|
|
29
28
|
|
|
30
29
|
const path = require('path')
|
|
@@ -69,14 +68,6 @@ export function getDcgchatRuntime(): PluginRuntime {
|
|
|
69
68
|
return runtime as PluginRuntime
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
let msgParams = {} as IMsgParams
|
|
73
|
-
export function setMsgParams(params: any) {
|
|
74
|
-
msgParams = params
|
|
75
|
-
}
|
|
76
|
-
export function getMsgParams() {
|
|
77
|
-
return msgParams
|
|
78
|
-
}
|
|
79
|
-
|
|
80
71
|
let msgStatus: 'running' | 'finished' | '' = ''
|
|
81
72
|
export function setMsgStatus(status: 'running' | 'finished' | '') {
|
|
82
73
|
msgStatus = status
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
/** 最近一次 setParamsMessage 的 key,供不传参的 getEffectiveMsgParams() 使用 */
|
|
11
|
+
let currentSessionKey: string | null = null
|
|
12
|
+
|
|
13
|
+
/** 从 OpenClaw 配置读取当前 channel 的基础参数(唯一来源,供 transport / resolve 等复用) */
|
|
14
|
+
export function getParamsDefaults(): IMsgParams {
|
|
15
|
+
const ch = (getOpenClawConfig()?.channels?.["dcgchat-test"] as DcgchatConfig | undefined) ?? {}
|
|
16
|
+
return {
|
|
17
|
+
userId: Number(ch.userId ?? 0),
|
|
18
|
+
botToken: ch.botToken ?? '',
|
|
19
|
+
sessionId: '',
|
|
20
|
+
messageId: '',
|
|
21
|
+
domainId: String(ch.domainId ?? '1000'),
|
|
22
|
+
appId: String(ch.appId ?? '100'),
|
|
23
|
+
botId: '',
|
|
24
|
+
agentId: '',
|
|
25
|
+
sessionKey: ''
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveParamsMessage(params: Partial<IMsgParams>): IMsgParams {
|
|
30
|
+
const defaults = getParamsDefaults()
|
|
31
|
+
return {
|
|
32
|
+
userId: Number(params.userId ?? defaults.userId),
|
|
33
|
+
botToken: params.botToken ?? defaults.botToken,
|
|
34
|
+
sessionId: params.sessionId ?? defaults.sessionId,
|
|
35
|
+
messageId: params.messageId ?? defaults.messageId,
|
|
36
|
+
domainId: String(params.domainId ?? defaults.domainId),
|
|
37
|
+
appId: String(params.appId ?? defaults.appId),
|
|
38
|
+
botId: params.botId ?? defaults.botId,
|
|
39
|
+
agentId: params.agentId ?? defaults.agentId,
|
|
40
|
+
sessionKey: params.sessionKey ?? defaults.sessionKey
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 统一取值入口:显式 sessionKey,或回落到当前会话;再与配置缺省 merge,保证字段完整。
|
|
46
|
+
*/
|
|
47
|
+
export function getEffectiveMsgParams(sessionKey?: string): IMsgParams {
|
|
48
|
+
const key = sessionKey ?? currentSessionKey
|
|
49
|
+
const stored = key ? paramsMessageMap.get(key) : undefined
|
|
50
|
+
return stored ? resolveParamsMessage(stored) : getParamsDefaults()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function setParamsMessage(sessionKey: string, params: Partial<IMsgParams>) {
|
|
54
|
+
if (!sessionKey) return
|
|
55
|
+
currentSessionKey = sessionKey
|
|
56
|
+
paramsMessageMap.set(sessionKey, resolveParamsMessage({ ...params, sessionKey }))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getParamsMessage(sessionKey: string): IMsgParams | undefined {
|
|
60
|
+
return paramsMessageMap.get(sessionKey)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getCurrentSessionKey(): string | null {
|
|
64
|
+
return currentSessionKey
|
|
65
|
+
}
|