@dcrays/dcgchat-test 0.2.32 → 0.2.34

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 CHANGED
@@ -12,6 +12,7 @@ const plugin = {
12
12
  configSchema: emptyPluginConfigSchema(),
13
13
  register(api: OpenClawPluginApi) {
14
14
  setDcgchatRuntime(api.runtime)
15
+
15
16
  monitoringToolMessage(api)
16
17
  setOpenClawConfig(api.config)
17
18
  api.registerChannel({ plugin: dcgchatPlugin })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcrays/dcgchat-test",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin for 书灵墨宝 (WebSocket)",
6
6
  "main": "index.ts",
package/src/bot.ts CHANGED
@@ -3,14 +3,7 @@ import path from 'node:path'
3
3
  import type { ReplyPayload } from 'openclaw/plugin-sdk'
4
4
  import { createReplyPrefixContext } from 'openclaw/plugin-sdk'
5
5
  import type { InboundMessage } from './types.js'
6
- import {
7
- clearSentMediaKeys,
8
- getDcgchatRuntime,
9
- getOpenClawConfig,
10
- getWorkspaceDir,
11
- getWsConnection,
12
- setMsgStatus
13
- } from './utils/global.js'
6
+ import { clearSentMediaKeys, getDcgchatRuntime, getOpenClawConfig, getWorkspaceDir, setMsgStatus } from './utils/global.js'
14
7
  import { resolveAccount, sendDcgchatMedia } from './channel.js'
15
8
  import { generateSignUrl } from './request/api.js'
16
9
  import { extractMobookFiles } from './utils/searchFile.js'
@@ -133,6 +126,14 @@ function resolveReplyMediaList(payload: ReplyPayload): string[] {
133
126
  export async function handleDcgchatMessage(msg: InboundMessage, accountId: string): Promise<void> {
134
127
  const msgCtx = createMsgContext(msg)
135
128
 
129
+ let finalSent = false
130
+
131
+ const safeSendFinal = () => {
132
+ if (finalSent) return
133
+ finalSent = true
134
+ sendFinal(msgCtx)
135
+ }
136
+
136
137
  let completeText = ''
137
138
  const config = getOpenClawConfig()
138
139
  if (!config) {
@@ -145,7 +146,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
145
146
 
146
147
  if (!text) {
147
148
  sendTextMsg(msgCtx, '你需要我帮你做什么呢?')
148
- sendFinal(msgCtx)
149
+ safeSendFinal()
149
150
  return
150
151
  }
151
152
 
@@ -245,10 +246,11 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
245
246
  }
246
247
  },
247
248
  onError: (err: unknown, info: { kind: string }) => {
249
+ safeSendFinal()
248
250
  dcgLogger(`${info.kind} reply failed: ${String(err)}`, 'error')
249
251
  },
250
252
  onIdle: () => {
251
- sendFinal(msgCtx)
253
+ safeSendFinal()
252
254
  }
253
255
  })
254
256
 
@@ -268,7 +270,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
268
270
  } else if (interruptCommand.includes(text?.trim())) {
269
271
  dcgLogger(`interrupt command: ${text}`)
270
272
  abortMobookappGeneration(conversationId)
271
- sendFinal(msgCtx)
273
+ safeSendFinal()
272
274
  return
273
275
  } else {
274
276
  dcgLogger(`dispatching to agent (session=${route.sessionKey})`)
@@ -325,21 +327,31 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
325
327
  }
326
328
  try {
327
329
  markRunComplete()
330
+ markDispatchIdle()
328
331
  } catch (err) {
329
- dcgLogger(` markRunComplete error: ${String(err)}`, 'error')
332
+ dcgLogger(` markRunComplete||markRunComplete error: ${String(err)}`, 'error')
330
333
  }
331
- markDispatchIdle()
332
334
  if (![...systemCommand, ...interruptCommand].includes(text?.trim())) {
333
335
  for (const file of extractMobookFiles(completeText)) {
334
- let resolved = file
335
- if (!fs.existsSync(resolved)) {
336
- resolved = path.join(getWorkspaceDir(), file)
337
- if (!fs.existsSync(resolved)) return
336
+ const candidates: string[] = [file]
337
+ candidates.push(path.join(getWorkspaceDir(), file))
338
+ candidates.push(path.join(getWorkspaceDir(), file.replace(/^\//, '')))
339
+ if (process.platform === 'win32') {
340
+ const underMobook = file.replace(/^\/mobook\//i, '').replace(/^mobook[\\/]/i, '')
341
+ if (underMobook) {
342
+ candidates.push(path.join('C:\\', 'mobook', underMobook))
343
+ }
344
+ }
345
+ const resolved = candidates.find((p) => fs.existsSync(p))
346
+ if (!resolved) continue
347
+ try {
348
+ await sendDcgchatMedia({ msgCtx, mediaUrl: resolved, text: '' })
349
+ } catch (err) {
350
+ dcgLogger(` sendDcgchatMedia error: ${String(err)}`, 'error')
338
351
  }
339
- await sendDcgchatMedia({ msgCtx, mediaUrl: resolved, text: '' })
340
352
  }
341
353
  }
342
- sendFinal(msgCtx)
354
+ safeSendFinal()
343
355
  clearSentMediaKeys(msg.content.message_id)
344
356
  setMsgStatus('finished')
345
357
 
@@ -360,5 +372,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
360
372
  } catch (err) {
361
373
  dcgLogger(` handle message failed: ${String(err)}`, 'error')
362
374
  sendError(msgCtx, err instanceof Error ? err.message : String(err))
375
+ } finally {
376
+ safeSendFinal()
363
377
  }
364
378
  }
package/src/monitor.ts CHANGED
@@ -48,6 +48,15 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
48
48
  let shouldReconnect = true
49
49
  let ws: WebSocket | null = null
50
50
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null
51
+ let heartbeatLogCounter = 0
52
+
53
+ const isOpenclawBotHeartbeat = (raw: WebSocket.RawData, parsedMsg: { messageType?: string }): boolean => {
54
+ if (parsedMsg?.messageType === 'openclaw_bot_heartbeat') return true
55
+ if (typeof raw === 'object' && raw !== null && !Buffer.isBuffer(raw)) {
56
+ return (raw as { messageType?: string }).messageType === 'openclaw_bot_heartbeat'
57
+ }
58
+ return false
59
+ }
51
60
 
52
61
  const stopHeartbeat = () => {
53
62
  if (heartbeatTimer) {
@@ -96,11 +105,18 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
96
105
  return
97
106
  }
98
107
 
99
- dcgLogger(`${parsed?.messageType}, ${data.toString()}`)
100
- if (parsed.messageType === 'openclaw_bot_heartbeat') {
101
- dcgLogger(`heartbeat ack received, ${data.toString()}`)
108
+ const payloadStr = data.toString()
109
+ const heartbeat = isOpenclawBotHeartbeat(data, parsed)
110
+ if (heartbeat) {
111
+ heartbeatLogCounter += 1
112
+ if (heartbeatLogCounter % 10 === 0) {
113
+ dcgLogger(`${parsed?.messageType}, ${payloadStr}`)
114
+ dcgLogger(`heartbeat ack received, ${payloadStr}`)
115
+ }
102
116
  return
103
117
  }
118
+
119
+ dcgLogger(`${parsed?.messageType}, ${payloadStr}`)
104
120
  try {
105
121
  parsed.content = JSON.parse(parsed.content)
106
122
  } catch {
@@ -1,3 +1,5 @@
1
+ import { dcgLogger } from './log.js'
2
+
1
3
  /**
2
4
  * 从文本中提取 /mobook 目录下的文件
3
5
  * @param {string} text
@@ -182,7 +184,7 @@ export function extractMobookFiles(text = '') {
182
184
  // 6️⃣ 兜底:绝对路径等 `.../mobook/<文件名>.<扩展名>` + 最长后缀匹配 + 去脏字符
183
185
  collectMobookPathsByScan(text, result)
184
186
  } catch (e) {
185
- console.warn('extractMobookFiles error:', e)
187
+ dcgLogger(`extractMobookFiles error:${e}`)
186
188
  }
187
189
  return [...result]
188
190
  }