@dcrays/dcgchat-test 0.3.28 → 0.3.31
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/package.json +1 -1
- package/src/bot.ts +22 -2
- package/src/channel.ts +53 -16
- package/src/utils/params.ts +2 -2
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -166,7 +166,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
166
166
|
const effectiveAgentId = embeddedAgentId ?? route.agentId
|
|
167
167
|
const effectiveSessionKey = getSessionKey(msg.content, account.accountId)
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
const mergedParams = {
|
|
170
170
|
userId: msg._userId,
|
|
171
171
|
botToken: msg.content.bot_token,
|
|
172
172
|
sessionId: conversationId,
|
|
@@ -177,7 +177,14 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
177
177
|
agentId: msg.content.agent_id ?? '',
|
|
178
178
|
sessionKey: effectiveSessionKey,
|
|
179
179
|
real_mobook
|
|
180
|
-
}
|
|
180
|
+
}
|
|
181
|
+
setParamsMessage(effectiveSessionKey, mergedParams)
|
|
182
|
+
// 与 OpenClaw 会话投递里仍可能出现的 ctx.to=SenderId(userId)对齐,便于 getOutboundMsgParams 命中
|
|
183
|
+
dcgLogger(
|
|
184
|
+
`target normalize: rawTarget=${userId}, normalizedTarget=${effectiveSessionKey}, conversationId=${conversationId ?? ''}, messageId=${msg.content.message_id}`
|
|
185
|
+
)
|
|
186
|
+
setParamsMessage(userId, mergedParams)
|
|
187
|
+
dcgLogger(`target alias bound: aliasTarget=${userId} -> sessionKey=${effectiveSessionKey}`)
|
|
181
188
|
const outboundCtx = getEffectiveMsgParams(effectiveSessionKey)
|
|
182
189
|
const agentEntry =
|
|
183
190
|
effectiveAgentId && effectiveAgentId !== 'main' ? config.agents?.list?.find((a) => a.id === effectiveAgentId) : undefined
|
|
@@ -245,6 +252,9 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
245
252
|
OriginatingTo: effectiveSessionKey,
|
|
246
253
|
...mediaPayload
|
|
247
254
|
})
|
|
255
|
+
dcgLogger(
|
|
256
|
+
`inbound context target: rawTarget=${userId}, normalizedTarget=${effectiveSessionKey}, ctx.To=${String(ctxPayload.To ?? '')}, ctx.SessionKey=${String(ctxPayload.SessionKey ?? '')}, ctx.OriginatingTo=${String(ctxPayload.OriginatingTo ?? '')}`
|
|
257
|
+
)
|
|
248
258
|
|
|
249
259
|
const sentMediaKeys = new Set<string>()
|
|
250
260
|
const getMediaKey = (url: string) => url.split(/[\\/]/).pop() ?? url
|
|
@@ -438,11 +448,21 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
438
448
|
|
|
439
449
|
// Record session metadata
|
|
440
450
|
const storePath = core.channel.session.resolveStorePath(config.session?.store)
|
|
451
|
+
dcgLogger(
|
|
452
|
+
`record session route: rawTarget=${userId}, normalizedTarget=${effectiveSessionKey}, updateLastRoute.to=${effectiveSessionKey}, accountId=${route.accountId}`
|
|
453
|
+
)
|
|
441
454
|
core.channel.session
|
|
442
455
|
.recordInboundSession({
|
|
443
456
|
storePath,
|
|
444
457
|
sessionKey: effectiveSessionKey,
|
|
445
458
|
ctx: ctxPayload,
|
|
459
|
+
// 与 Telegram/Discord 等一致:写入 deliveryContext.to,否则投递可能回退为 From(userId),channel sendMedia 里 ctx.to 会变成数字 userId
|
|
460
|
+
updateLastRoute: {
|
|
461
|
+
sessionKey: effectiveSessionKey,
|
|
462
|
+
channel: "dcgchat-test",
|
|
463
|
+
to: effectiveSessionKey,
|
|
464
|
+
accountId: route.accountId
|
|
465
|
+
},
|
|
446
466
|
onRecordError: (err) => {
|
|
447
467
|
dcgLogger(` session record error: ${String(err)}`, 'error')
|
|
448
468
|
}
|
package/src/channel.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ossUpload } from './request/oss.js'
|
|
|
5
5
|
import { addSentMediaKey, getCronMessageId, getOpenClawConfig, hasSentMediaKey } from './utils/global.js'
|
|
6
6
|
import { isWsOpen, mergeDefaultParams, mergeSessionParams, sendFinal, wsSendRaw } from './transport.js'
|
|
7
7
|
import { dcgLogger, setLogger } from './utils/log.js'
|
|
8
|
-
import { getOutboundMsgParams } from './utils/params.js'
|
|
8
|
+
import { getOutboundMsgParams, getParamsMessage } from './utils/params.js'
|
|
9
9
|
import { startDcgchatGatewaySocket } from './gateway/socket.js'
|
|
10
10
|
|
|
11
11
|
export type DcgchatMediaSendOptions = {
|
|
@@ -15,8 +15,15 @@ export type DcgchatMediaSendOptions = {
|
|
|
15
15
|
text?: string
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function normalizeSessionTarget(rawTo: string): string {
|
|
19
|
+
const cleaned = rawTo.replace('dcg-cron:', '').trim()
|
|
20
|
+
if (!cleaned) return ''
|
|
21
|
+
return getParamsMessage(cleaned)?.sessionKey?.trim() || cleaned
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
|
|
19
|
-
const
|
|
25
|
+
const sessionKey = normalizeSessionTarget(opts.sessionKey ?? '')
|
|
26
|
+
const msgCtx = getOutboundMsgParams(sessionKey)
|
|
20
27
|
if (!isWsOpen()) {
|
|
21
28
|
dcgLogger(`outbound media skipped -> ws not open: ${opts.mediaUrl ?? ''}`)
|
|
22
29
|
return
|
|
@@ -36,21 +43,20 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
36
43
|
|
|
37
44
|
try {
|
|
38
45
|
const botToken = msgCtx.botToken ?? getOpenClawConfig()?.channels?.["dcgchat-test"]?.botToken ?? ''
|
|
39
|
-
console.log('🚀 ~ sendDcgchatMedia ~ botToken:', botToken)
|
|
40
46
|
const url = opts.mediaUrl ? await ossUpload(opts.mediaUrl, botToken, 1) : ''
|
|
41
47
|
wsSendRaw(msgCtx, {
|
|
42
48
|
response: opts.text ?? '',
|
|
43
49
|
message_tags: { source: 'file' },
|
|
44
50
|
files: [{ url, name: fileName }]
|
|
45
51
|
})
|
|
46
|
-
dcgLogger(`dcgchat: sendMedia
|
|
52
|
+
dcgLogger(`dcgchat: sendMedia session=${sessionKey}, file=${fileName}`)
|
|
47
53
|
} catch (error) {
|
|
48
54
|
wsSendRaw(msgCtx, {
|
|
49
55
|
response: opts.text ?? '',
|
|
50
56
|
message_tags: { source: 'file' },
|
|
51
57
|
files: [{ url: opts.mediaUrl ?? '', name: fileName }]
|
|
52
58
|
})
|
|
53
|
-
dcgLogger(`dcgchat: error sendMedia
|
|
59
|
+
dcgLogger(`dcgchat: error sendMedia session=${sessionKey}: ${String(error)}`, 'error')
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -101,11 +107,17 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
101
107
|
enabled: { type: 'boolean' },
|
|
102
108
|
wsUrl: { type: 'string' },
|
|
103
109
|
botToken: { type: 'string' },
|
|
104
|
-
userId: { type: 'string' },
|
|
110
|
+
userId: { type: 'string', description: 'WebSocket 连接参数 _userId,与 message 工具的 target(effectiveSessionKey)无关' },
|
|
105
111
|
appId: { type: 'string' },
|
|
106
112
|
domainId: { type: 'string' },
|
|
107
113
|
capabilities: { type: 'array', items: { type: 'string' } }
|
|
108
114
|
}
|
|
115
|
+
},
|
|
116
|
+
uiHints: {
|
|
117
|
+
userId: {
|
|
118
|
+
label: 'WS 连接 _userId',
|
|
119
|
+
help: '仅用于拼接网关 WebSocket URL 的查询参数,不是 Agent 发消息时的 target。发消息请使用 effectiveSessionKey(与入站上下文 SessionKey 相同,格式如 agent:main:mobook:direct:…)。'
|
|
120
|
+
}
|
|
109
121
|
}
|
|
110
122
|
},
|
|
111
123
|
config: {
|
|
@@ -135,18 +147,31 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
135
147
|
})
|
|
136
148
|
},
|
|
137
149
|
messaging: {
|
|
138
|
-
normalizeTarget: (raw) => raw
|
|
150
|
+
normalizeTarget: (raw) => normalizeSessionTarget(raw ?? '') || undefined,
|
|
139
151
|
targetResolver: {
|
|
140
152
|
looksLikeId: (raw) => Boolean(raw?.trim()),
|
|
141
|
-
hint: 'userId'
|
|
153
|
+
hint: 'effectiveSessionKey(与 SessionKey 一致;勿填配置里的 WS userId)'
|
|
142
154
|
}
|
|
143
155
|
},
|
|
156
|
+
agentPrompt: {
|
|
157
|
+
messageToolHints: () => [
|
|
158
|
+
'书灵墨宝:message 工具的 target 必须填 effectiveSessionKey(与当前会话 SessionKey / OriginatingTo 相同),形如 agent:main:mobook:direct:<agent_id>:<session_id>;不要填 channels.dcgchat.userId 或纯数字 userId。',
|
|
159
|
+
'OpenClaw 自带的 target 字段说明里仍可能出现 “user id”,在本频道请忽略该字样,一律按 effectiveSessionKey 理解。'
|
|
160
|
+
]
|
|
161
|
+
},
|
|
144
162
|
outbound: {
|
|
145
163
|
deliveryMode: 'direct',
|
|
164
|
+
resolveTarget: ({ to }) => {
|
|
165
|
+
const normalized = normalizeSessionTarget(to ?? '')
|
|
166
|
+
if (!normalized) {
|
|
167
|
+
return { ok: false, error: new Error('target is empty') }
|
|
168
|
+
}
|
|
169
|
+
return { ok: true, to: normalized }
|
|
170
|
+
},
|
|
146
171
|
textChunkLimit: 4000,
|
|
147
172
|
sendText: async (ctx) => {
|
|
148
173
|
const isCron = ctx.to.indexOf('dcg-cron:') >= 0
|
|
149
|
-
const to = ctx.to
|
|
174
|
+
const to = normalizeSessionTarget(ctx.to)
|
|
150
175
|
dcgLogger(`channel sendText to ${ctx.to} `)
|
|
151
176
|
const outboundCtx = getOutboundMsgParams(to)
|
|
152
177
|
const cronMsgId = getCronMessageId(to)
|
|
@@ -176,17 +201,29 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
176
201
|
}
|
|
177
202
|
},
|
|
178
203
|
sendMedia: async (ctx) => {
|
|
179
|
-
const to = ctx.to.replace('dcg-cron:', '')
|
|
180
|
-
const msgCtx = getOutboundMsgParams(to)
|
|
181
|
-
const cronMsgId = getCronMessageId(to)
|
|
182
204
|
const isCron = ctx.to.indexOf('dcg-cron:') >= 0
|
|
183
|
-
const
|
|
205
|
+
const to = normalizeSessionTarget(ctx.to)
|
|
206
|
+
const outboundCtx = getOutboundMsgParams(to)
|
|
207
|
+
const msgCtx = getParamsMessage(to) ?? outboundCtx
|
|
208
|
+
const cronMsgId = getCronMessageId(to)
|
|
209
|
+
const fallbackMessageId = `${Date.now()}`
|
|
210
|
+
const messageId = cronMsgId || (isCron ? fallbackMessageId : msgCtx?.messageId || fallbackMessageId)
|
|
211
|
+
|
|
212
|
+
if (!outboundCtx?.sessionId) {
|
|
213
|
+
dcgLogger(`channel sendMedia to ${ctx.to} -> sessionId not found`, 'error')
|
|
214
|
+
return {
|
|
215
|
+
channel: "dcgchat-test",
|
|
216
|
+
messageId,
|
|
217
|
+
chatId: to || ''
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
184
221
|
dcgLogger(`channel sendMedia to ${ctx.to}`)
|
|
185
|
-
await sendDcgchatMedia({ sessionKey: to
|
|
222
|
+
await sendDcgchatMedia({ sessionKey: to || '', mediaUrl: ctx.mediaUrl || '' })
|
|
186
223
|
return {
|
|
187
224
|
channel: "dcgchat-test",
|
|
188
|
-
messageId
|
|
189
|
-
chatId:
|
|
225
|
+
messageId,
|
|
226
|
+
chatId: to || ''
|
|
190
227
|
}
|
|
191
228
|
}
|
|
192
229
|
},
|
package/src/utils/params.ts
CHANGED
|
@@ -47,8 +47,8 @@ export function getEffectiveMsgParams(sessionKey?: string): IMsgParams {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Agent `message`
|
|
51
|
-
* `
|
|
50
|
+
* Agent `message` 工具的 `target` 应为 `effectiveSessionKey`(如 `agent:main:mobook:direct:...`)。
|
|
51
|
+
* `setParamsMessage` 使用的 key 与此一致。若按 preferredKey 查不到 map,
|
|
52
52
|
* 则回落到当前会话 `currentSessionKey`,避免拿到空 `messageId` / `sessionId` 导致无文件卡片、WS 上下文错误。
|
|
53
53
|
*/
|
|
54
54
|
export function getOutboundMsgParams(preferredKey: string): IMsgParams {
|