@dcrays/dcgchat-test 0.4.26 → 0.4.28
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 +1 -2
- package/openclaw.plugin.json +4 -2
- package/package.json +3 -13
- package/src/bot.ts +7 -7
- package/src/channel.ts +13 -14
- package/src/cronToolCall.ts +2 -2
- package/src/request/oss.ts +81 -13
- package/src/utils/constant.ts +0 -4
- package/src/utils/global.ts +3 -3
- package/src/utils/log.ts +1 -2
- package/src/utils/params.ts +1 -1
- package/README.md +0 -83
package/index.ts
CHANGED
|
@@ -3,12 +3,11 @@ import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk'
|
|
|
3
3
|
import { dcgchatPlugin } from './src/channel.js'
|
|
4
4
|
import { setDcgchatRuntime, setWorkspaceDir } from './src/utils/global.js'
|
|
5
5
|
import { monitoringToolMessage } from './src/tool.js'
|
|
6
|
-
import { channelInfo, ENV } from './src/utils/constant.js'
|
|
7
6
|
import { setOpenClawConfig } from './src/utils/global.js'
|
|
8
7
|
import { createDcgchatMessageTool } from './src/tools/messageTool.js'
|
|
9
8
|
|
|
10
9
|
const plugin = {
|
|
11
|
-
id:
|
|
10
|
+
id: "dcgchat-test",
|
|
12
11
|
name: '书灵墨宝',
|
|
13
12
|
description: '连接 OpenClaw 与 书灵墨宝 产品(WebSocket)',
|
|
14
13
|
configSchema: emptyPluginConfigSchema(),
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcrays/dcgchat-test",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw channel plugin for 书灵墨宝 (WebSocket)",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -16,12 +16,6 @@
|
|
|
16
16
|
"websocket",
|
|
17
17
|
"ai"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"typecheck": "tsc --noEmit",
|
|
21
|
-
"build:production": "npx tsx scripts/build.ts production",
|
|
22
|
-
"build:prod": "npx tsx scripts/build.ts production",
|
|
23
|
-
"build:test": "npx tsx scripts/build.ts test"
|
|
24
|
-
},
|
|
25
19
|
"dependencies": {
|
|
26
20
|
"ali-oss": "file:src/libs/ali-oss-6.23.0.tgz",
|
|
27
21
|
"axios": "file:src/libs/axios-1.13.6.tgz",
|
|
@@ -37,19 +31,15 @@
|
|
|
37
31
|
"id": "dcgchat-test",
|
|
38
32
|
"label": "书灵墨宝",
|
|
39
33
|
"selectionLabel": "书灵墨宝",
|
|
40
|
-
"docsPath": "/channels/dcgchat",
|
|
34
|
+
"docsPath": "/channels/dcgchat-test",
|
|
41
35
|
"docsLabel": "dcgchat-test",
|
|
42
36
|
"blurb": "连接 OpenClaw 与 书灵墨宝 产品",
|
|
43
37
|
"order": 80
|
|
44
38
|
},
|
|
45
39
|
"install": {
|
|
46
40
|
"npmSpec": "@dcrays/dcgchat-test",
|
|
47
|
-
"localPath": "extensions/dcgchat",
|
|
41
|
+
"localPath": "extensions/dcgchat-test",
|
|
48
42
|
"defaultChoice": "npm"
|
|
49
43
|
}
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"openclaw": "^2026.3.13",
|
|
53
|
-
"typescript": "~5.8.0"
|
|
54
44
|
}
|
|
55
45
|
}
|
package/src/bot.ts
CHANGED
|
@@ -190,7 +190,7 @@ async function handleDcgchatMessageInboundTurn(msg: InboundMessage, accountId: s
|
|
|
190
190
|
|
|
191
191
|
const route = core.channel.routing.resolveAgentRoute({
|
|
192
192
|
cfg: config,
|
|
193
|
-
channel:
|
|
193
|
+
channel: "dcgchat-test",
|
|
194
194
|
accountId: account.accountId,
|
|
195
195
|
peer: { kind: 'direct', id: conversationId }
|
|
196
196
|
})
|
|
@@ -207,7 +207,7 @@ async function handleDcgchatMessageInboundTurn(msg: InboundMessage, accountId: s
|
|
|
207
207
|
sessionId: conversationId,
|
|
208
208
|
messageId: msg.content.message_id,
|
|
209
209
|
domainId: msg.content.domain_id,
|
|
210
|
-
appId: config.channels?.[
|
|
210
|
+
appId: config.channels?.["dcgchat-test"]?.appId || 100,
|
|
211
211
|
botId: msg.content.bot_id ?? '',
|
|
212
212
|
agentId: msg.content.agent_id ?? '',
|
|
213
213
|
sessionKey: dcgSessionKey,
|
|
@@ -269,13 +269,13 @@ async function handleDcgchatMessageInboundTurn(msg: InboundMessage, accountId: s
|
|
|
269
269
|
ChatType: 'direct',
|
|
270
270
|
SenderName: agentDisplayName,
|
|
271
271
|
SenderId: userId,
|
|
272
|
-
Provider:
|
|
273
|
-
Surface:
|
|
272
|
+
Provider: "dcgchat-test",
|
|
273
|
+
Surface: "dcgchat-test",
|
|
274
274
|
MessageSid: msg.content.message_id,
|
|
275
275
|
Timestamp: Date.now(),
|
|
276
276
|
WasMentioned: true,
|
|
277
277
|
CommandAuthorized: true,
|
|
278
|
-
OriginatingChannel:
|
|
278
|
+
OriginatingChannel: "dcgchat-test",
|
|
279
279
|
OriginatingTo: dcgSessionKey,
|
|
280
280
|
Target: dcgSessionKey,
|
|
281
281
|
SourceTarget: dcgSessionKey,
|
|
@@ -298,7 +298,7 @@ async function handleDcgchatMessageInboundTurn(msg: InboundMessage, accountId: s
|
|
|
298
298
|
const prefixContext = createReplyPrefixContext({
|
|
299
299
|
cfg: config,
|
|
300
300
|
agentId: effectiveAgentId ?? '',
|
|
301
|
-
channel:
|
|
301
|
+
channel: "dcgchat-test",
|
|
302
302
|
accountId: account.accountId
|
|
303
303
|
})
|
|
304
304
|
|
|
@@ -479,7 +479,7 @@ async function handleDcgchatMessageInboundTurn(msg: InboundMessage, accountId: s
|
|
|
479
479
|
ctx: ctxPayload,
|
|
480
480
|
updateLastRoute: {
|
|
481
481
|
sessionKey: dcgSessionKey,
|
|
482
|
-
channel:
|
|
482
|
+
channel: "dcgchat-test",
|
|
483
483
|
to: dcgSessionKey,
|
|
484
484
|
accountId: route.accountId
|
|
485
485
|
},
|
package/src/channel.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
setCronMessageId
|
|
14
14
|
} from './utils/global.js'
|
|
15
15
|
import { isWsOpen, mergeDefaultParams, mergeSessionParams, sendFinal, wsSendRaw } from './transport.js'
|
|
16
|
-
import { channelInfo, ENV } from './utils/constant.js'
|
|
17
16
|
import { dcgLogger, setLogger } from './utils/log.js'
|
|
18
17
|
import { getOutboundMsgParams, getParamsMessage } from './utils/params.js'
|
|
19
18
|
import { isSessionActiveForTool } from './tool.js'
|
|
@@ -21,7 +20,7 @@ import { startDcgchatGatewaySocket } from './gateway/socket.js'
|
|
|
21
20
|
import { getCronJobsPath, readCronJob } from './cron.js'
|
|
22
21
|
|
|
23
22
|
function dcgchatChannelCfg(): DcgchatConfig {
|
|
24
|
-
return (getOpenClawConfig()?.channels?.[
|
|
23
|
+
return (getOpenClawConfig()?.channels?.["dcgchat-test"] as DcgchatConfig | undefined) ?? {}
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
/** `agent:<code>:mobook:direct:<agentId>:<sessionId>`(与 getSessionKey 非 real_mobook 分支一致) */
|
|
@@ -222,8 +221,8 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
222
221
|
const fileName = mediaUrl?.split(/[\\/]/).pop() || ''
|
|
223
222
|
const notMessageId = `${msgCtx?.messageId}`?.length === 13 || !msgCtx?.messageId
|
|
224
223
|
try {
|
|
225
|
-
const botToken = msgCtx.botToken ?? getOpenClawConfig()?.channels?.[
|
|
226
|
-
const url =
|
|
224
|
+
const botToken = msgCtx.botToken ?? getOpenClawConfig()?.channels?.["dcgchat-test"]?.botToken ?? ''
|
|
225
|
+
const url = mediaUrl ? await ossUpload(mediaUrl, botToken, 1) : ''
|
|
227
226
|
if (!msgCtx.agentId) {
|
|
228
227
|
msgCtx.agentId = agentId
|
|
229
228
|
}
|
|
@@ -247,7 +246,7 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
247
246
|
|
|
248
247
|
export function resolveAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedDcgchatAccount {
|
|
249
248
|
const id = accountId ?? DEFAULT_ACCOUNT_ID
|
|
250
|
-
const raw = (cfg.channels?.[
|
|
249
|
+
const raw = (cfg.channels?.["dcgchat-test"] as DcgchatConfig | undefined) ?? {}
|
|
251
250
|
return {
|
|
252
251
|
accountId: id,
|
|
253
252
|
enabled: raw.enabled !== false,
|
|
@@ -261,13 +260,13 @@ export function resolveAccount(cfg: OpenClawConfig, accountId?: string | null):
|
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
264
|
-
id:
|
|
263
|
+
id: "dcgchat-test",
|
|
265
264
|
meta: {
|
|
266
|
-
id:
|
|
265
|
+
id: "dcgchat-test",
|
|
267
266
|
label: '书灵墨宝',
|
|
268
267
|
selectionLabel: '书灵墨宝',
|
|
269
268
|
docsPath: '/channels/dcgchat',
|
|
270
|
-
docsLabel:
|
|
269
|
+
docsLabel: "dcgchat-test",
|
|
271
270
|
blurb: '连接 OpenClaw 与 书灵墨宝 产品',
|
|
272
271
|
order: 80
|
|
273
272
|
},
|
|
@@ -284,7 +283,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
284
283
|
// blockStreaming: true,
|
|
285
284
|
},
|
|
286
285
|
/** 当前构建的 channel id + 兼容旧配置键 `channels.dcgchat` */
|
|
287
|
-
reload: { configPrefixes: [`channels.${
|
|
286
|
+
reload: { configPrefixes: [`channels.${"dcgchat-test"}`, 'channels.dcgchat'] },
|
|
288
287
|
configSchema: {
|
|
289
288
|
schema: {
|
|
290
289
|
type: 'object',
|
|
@@ -320,7 +319,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
320
319
|
resolveAccount: (cfg, accountId) => resolveAccount(cfg, accountId),
|
|
321
320
|
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
322
321
|
setAccountEnabled: ({ cfg, enabled }) => {
|
|
323
|
-
const channelKey =
|
|
322
|
+
const channelKey = "dcgchat-test"
|
|
324
323
|
const prev = (cfg.channels?.[channelKey as keyof NonNullable<typeof cfg.channels>] as Record<string, unknown> | undefined) ?? {}
|
|
325
324
|
return {
|
|
326
325
|
...cfg,
|
|
@@ -392,7 +391,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
392
391
|
if (!isSessionActiveForTool(to)) {
|
|
393
392
|
dcgLogger(`channel sendText dropped (session not active): to=${to}`)
|
|
394
393
|
return {
|
|
395
|
-
channel:
|
|
394
|
+
channel: "dcgchat-test",
|
|
396
395
|
messageId: '',
|
|
397
396
|
chatId: outboundChatId(ctx.to, to)
|
|
398
397
|
}
|
|
@@ -404,7 +403,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
404
403
|
}
|
|
405
404
|
}
|
|
406
405
|
return {
|
|
407
|
-
channel:
|
|
406
|
+
channel: "dcgchat-test",
|
|
408
407
|
messageId: `${messageId}`,
|
|
409
408
|
chatId: outboundChatId(ctx.to, to)
|
|
410
409
|
}
|
|
@@ -425,7 +424,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
425
424
|
if (!sessionId) {
|
|
426
425
|
dcgLogger(`channel sendMedia to ${ctx.to} -> sessionId not found`, 'error')
|
|
427
426
|
return {
|
|
428
|
-
channel:
|
|
427
|
+
channel: "dcgchat-test",
|
|
429
428
|
messageId,
|
|
430
429
|
chatId: outboundChatId(ctx.to, to || '')
|
|
431
430
|
}
|
|
@@ -444,7 +443,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
444
443
|
})
|
|
445
444
|
}
|
|
446
445
|
return {
|
|
447
|
-
channel:
|
|
446
|
+
channel: "dcgchat-test",
|
|
448
447
|
messageId,
|
|
449
448
|
chatId: outboundChatId(ctx.to, to || '')
|
|
450
449
|
}
|
package/src/cronToolCall.ts
CHANGED
|
@@ -137,7 +137,7 @@ function patchCronDeliveryInParams(
|
|
|
137
137
|
if (agentId) d.accountId = agentId
|
|
138
138
|
if (announceNoChannel) {
|
|
139
139
|
d.bestEffort = true
|
|
140
|
-
d.channel =
|
|
140
|
+
d.channel = "dcgchat-test"
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -191,7 +191,7 @@ export function cronToolCall(event: { toolName: any; params: any; toolCallId: an
|
|
|
191
191
|
if (params.command.indexOf('cron create') > -1 || params.command.indexOf('cron add') > -1) {
|
|
192
192
|
const newParams = JSON.parse(JSON.stringify(params)) as Record<string, unknown>
|
|
193
193
|
newParams.command =
|
|
194
|
-
params.command.replace('--json', '') + ` --session-key ${sk} --channel ${
|
|
194
|
+
params.command.replace('--json', '') + ` --session-key ${sk} --channel ${"dcgchat-test"} --to dcg-cron:${sk} --json`
|
|
195
195
|
return { params: newParams }
|
|
196
196
|
} else {
|
|
197
197
|
return undefined
|
package/src/request/oss.ts
CHANGED
|
@@ -1,32 +1,89 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
2
|
// @ts-ignore
|
|
3
3
|
import OSS from 'ali-oss'
|
|
4
4
|
import { getStsToken, getUserToken } from './api.js'
|
|
5
5
|
import { dcgLogger } from '../utils/log.js'
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/** 分片大小:OSS 要求每片 ≥100 KB(最后一片可更小) */
|
|
8
|
+
const MULTIPART_PART_SIZE = 1024 * 1024
|
|
9
|
+
|
|
10
|
+
/** ali-oss 默认 timeout 为 60s,大文件单 PUT 或慢网易触发 ResponseTimeoutError */
|
|
11
|
+
const OSS_HTTP_TIMEOUT_MS = 10 * 60 * 1000
|
|
12
|
+
|
|
13
|
+
/** 归一化入参,避免 file://、包装对象、TypedArray 等导致 SDK 识别失败 */
|
|
14
|
+
function coerceOssFileInput(input: File | string | Buffer): File | string | Buffer {
|
|
15
|
+
if (typeof input === 'string') {
|
|
16
|
+
const t = input.trim()
|
|
17
|
+
if (t.startsWith('file:')) {
|
|
18
|
+
try {
|
|
19
|
+
return fileURLToPath(t)
|
|
20
|
+
} catch {
|
|
21
|
+
return input
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return input
|
|
25
|
+
}
|
|
26
|
+
if (Buffer.isBuffer(input)) {
|
|
27
|
+
return input
|
|
28
|
+
}
|
|
29
|
+
if (input && typeof input === 'object') {
|
|
30
|
+
if (ArrayBuffer.isView(input) && !(input instanceof DataView) && !Buffer.isBuffer(input)) {
|
|
31
|
+
const v = input as ArrayBufferView
|
|
32
|
+
return Buffer.from(v.buffer, v.byteOffset, v.byteLength)
|
|
33
|
+
}
|
|
34
|
+
const o = input as unknown as Record<string, unknown>
|
|
35
|
+
const p = o.path ?? o.filePath
|
|
36
|
+
if (typeof p === 'string' && p.trim()) return p.trim()
|
|
37
|
+
}
|
|
38
|
+
return input
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveMime(input: File | string | Buffer): string {
|
|
42
|
+
if (typeof input !== 'string' && !Buffer.isBuffer(input) && input.type) {
|
|
43
|
+
return input.type
|
|
44
|
+
}
|
|
45
|
+
return 'application/octet-stream'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 将 File/路径/Buffer 转为 ali-oss 接受的类型。
|
|
50
|
+
* 本地路径保持为字符串:put 内部用 contentLength + ReadStream,大文件也稳定。
|
|
51
|
+
*/
|
|
52
|
+
async function toUploadContent(input: File | string | Buffer): Promise<{ content: Buffer | string; fileName: string }> {
|
|
11
53
|
if (Buffer.isBuffer(input)) {
|
|
12
54
|
return { content: input, fileName: 'file' }
|
|
13
55
|
}
|
|
14
56
|
if (typeof input === 'string') {
|
|
15
57
|
return {
|
|
16
|
-
content:
|
|
17
|
-
fileName: input.split(
|
|
58
|
+
content: input,
|
|
59
|
+
fileName: input.split(/[/\\]/).pop() ?? 'file'
|
|
18
60
|
}
|
|
19
61
|
}
|
|
20
|
-
// File: ali-oss 需要 Buffer/Stream,用 arrayBuffer 转 Buffer
|
|
21
62
|
const buf = Buffer.from(await input.arrayBuffer())
|
|
22
|
-
|
|
63
|
+
const n = (input as { name?: string }).name
|
|
64
|
+
return { content: buf, fileName: typeof n === 'string' && n ? n : 'file' }
|
|
23
65
|
}
|
|
24
66
|
|
|
25
|
-
export
|
|
67
|
+
export type OssUploadOptions = {
|
|
68
|
+
/** 分片上传进度,p 为 0~1(仅大 Buffer 分片时触发) */
|
|
69
|
+
onProgress?: (p: number) => void
|
|
70
|
+
/** HTTP 超时(毫秒),覆盖默认 15 分钟;可传 `30 * 60 * 1000` 等 */
|
|
71
|
+
timeoutMs?: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const ossUpload = async (
|
|
75
|
+
rawFile: File | string | Buffer,
|
|
76
|
+
botToken: string,
|
|
77
|
+
isPrivate: 0 | 1 = 1,
|
|
78
|
+
uploadOptions?: OssUploadOptions
|
|
79
|
+
) => {
|
|
26
80
|
await getUserToken(botToken)
|
|
27
81
|
|
|
82
|
+
const file = coerceOssFileInput(rawFile)
|
|
28
83
|
const { content, fileName } = await toUploadContent(file)
|
|
29
84
|
const data = await getStsToken(fileName, botToken, isPrivate)
|
|
85
|
+
const mime = resolveMime(file)
|
|
86
|
+
const onProgress = uploadOptions?.onProgress
|
|
30
87
|
|
|
31
88
|
const options: OSS.Options = {
|
|
32
89
|
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
|
|
@@ -40,7 +97,8 @@ export const ossUpload = async (file: File | string | Buffer, botToken: string,
|
|
|
40
97
|
region: data.region,
|
|
41
98
|
secure: true,
|
|
42
99
|
cname: true,
|
|
43
|
-
authorizationV4: true
|
|
100
|
+
authorizationV4: true,
|
|
101
|
+
timeout: uploadOptions?.timeoutMs ?? OSS_HTTP_TIMEOUT_MS
|
|
44
102
|
}
|
|
45
103
|
|
|
46
104
|
const client = new OSS(options)
|
|
@@ -48,12 +106,22 @@ export const ossUpload = async (file: File | string | Buffer, botToken: string,
|
|
|
48
106
|
const name = `${data.uploadDir}${data.ossFileKey}`
|
|
49
107
|
|
|
50
108
|
try {
|
|
51
|
-
|
|
109
|
+
let objectResult: OSS.PutObjectResult | OSS.CompleteMultipartUploadResult
|
|
110
|
+
|
|
111
|
+
const multipartUploadOptions: OSS.MultipartUploadOptions = {
|
|
112
|
+
progress: (p: number) => {
|
|
113
|
+
onProgress?.(p)
|
|
114
|
+
},
|
|
115
|
+
parallel: 4,
|
|
116
|
+
partSize: MULTIPART_PART_SIZE,
|
|
117
|
+
mime
|
|
118
|
+
}
|
|
119
|
+
objectResult = await client.multipartUpload(name, content, multipartUploadOptions)
|
|
120
|
+
|
|
52
121
|
if (objectResult?.res?.status !== 200) {
|
|
53
122
|
dcgLogger(`OSS 上传失败, ${objectResult?.res?.status}`)
|
|
54
123
|
}
|
|
55
124
|
dcgLogger(`OSS 上传成功, ${objectResult.name || objectResult.url}`)
|
|
56
|
-
// const url = `${data.protocol || 'http'}://${data.bucket}.${data.endPoint}/${data.uploadDir}${data.ossFileKey}`
|
|
57
125
|
return isPrivate === 1 ? objectResult.name || objectResult.url : objectResult.url
|
|
58
126
|
} catch (error) {
|
|
59
127
|
dcgLogger(`OSS 上传失败: ${error}`, 'error')
|
package/src/utils/constant.ts
CHANGED
package/src/utils/global.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function getOpenClawConfig(): OpenClawConfig | null {
|
|
|
30
30
|
function getWorkspacePath(): string | null {
|
|
31
31
|
const workspacePath = path.join(
|
|
32
32
|
os.homedir(),
|
|
33
|
-
config?.channels?.[
|
|
33
|
+
config?.channels?.["dcgchat-test"]?.appId == 110 ? '.mobook' : '.openclaw',
|
|
34
34
|
'workspace'
|
|
35
35
|
)
|
|
36
36
|
if (fs.existsSync(workspacePath)) {
|
|
@@ -55,7 +55,7 @@ export function getWorkspaceDir(): string {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const { setRuntime: setDcgchatRuntime, getRuntime: getDcgchatRuntime } = createPluginRuntimeStore<PluginRuntime>(
|
|
58
|
-
`${
|
|
58
|
+
`${"dcgchat-test"} runtime not initialized`
|
|
59
59
|
)
|
|
60
60
|
export { setDcgchatRuntime, getDcgchatRuntime }
|
|
61
61
|
|
|
@@ -147,7 +147,7 @@ export const getSessionKey = (content: any, accountId: string) => {
|
|
|
147
147
|
|
|
148
148
|
const route = core.channel.routing.resolveAgentRoute({
|
|
149
149
|
cfg: getOpenClawConfig() as OpenClawConfig,
|
|
150
|
-
channel:
|
|
150
|
+
channel: "dcgchat-test",
|
|
151
151
|
accountId: accountId || 'default',
|
|
152
152
|
peer: { kind: 'direct', id: session_id }
|
|
153
153
|
})
|
package/src/utils/log.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RuntimeEnv } from 'openclaw/plugin-sdk'
|
|
2
|
-
import { channelInfo, ENV } from './constant.js'
|
|
3
2
|
|
|
4
3
|
let logger: RuntimeEnv | null = null
|
|
5
4
|
|
|
@@ -11,6 +10,6 @@ export function dcgLogger(message: string, type: 'log' | 'error' = 'log'): void
|
|
|
11
10
|
if (logger) {
|
|
12
11
|
logger[type](`书灵墨宝🚀 ~ [${new Date().toISOString()}] ${message}`)
|
|
13
12
|
} else {
|
|
14
|
-
console[type](`书灵墨宝🚀 ~ ${new Date().toISOString()} [${
|
|
13
|
+
console[type](`书灵墨宝🚀 ~ ${new Date().toISOString()} [${"dcgchat-test"}]: ${message}`)
|
|
15
14
|
}
|
|
16
15
|
}
|
package/src/utils/params.ts
CHANGED
|
@@ -9,7 +9,7 @@ const paramsMessageMap = new Map<string, IMsgParams>()
|
|
|
9
9
|
|
|
10
10
|
/** 从 OpenClaw 配置读取当前 channel 的基础参数(唯一来源,供 transport / resolve 等复用) */
|
|
11
11
|
export function getParamsDefaults(): IMsgParams {
|
|
12
|
-
const ch = (getOpenClawConfig()?.channels?.[
|
|
12
|
+
const ch = (getOpenClawConfig()?.channels?.["dcgchat-test"] as DcgchatConfig | undefined) ?? {}
|
|
13
13
|
return {
|
|
14
14
|
userId: Number(ch.userId ?? 0),
|
|
15
15
|
botToken: ch.botToken ?? '',
|
package/README.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# OpenClaw 书灵墨宝 插件
|
|
2
|
-
|
|
3
|
-
连接 OpenClaw 与 书灵墨宝 产品的通道插件。
|
|
4
|
-
|
|
5
|
-
## 架构
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
┌──────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────────────────┐
|
|
9
|
-
│ Web 前端 │ ←───────────────→ │ 公司后端服务 │ ←───────────────→ │ OpenClaw(工作电脑) │
|
|
10
|
-
└──────────┘ └──────────────┘ (OpenClaw 主动连) └─────────────────────┘
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
- OpenClaw 插件**主动连接**后端的 WebSocket 服务(不需要公网 IP)
|
|
14
|
-
- 后端收到用户消息后转发给 OpenClaw,OpenClaw 回复后发回后端
|
|
15
|
-
|
|
16
|
-
## 快速开始
|
|
17
|
-
|
|
18
|
-
### 1. 安装插件
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
pnpm openclaw plugins install -l /path/to/openclaw-dcgchat
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### 2. 配置
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
openclaw config set channels.dcgchat.enabled true
|
|
28
|
-
openclaw config set channels.dcgchat.wsUrl "ws://your-backend:8080/openclaw/ws"
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 3. 启动
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pnpm openclaw gateway
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 消息协议(MVP)
|
|
38
|
-
|
|
39
|
-
### 下行:后端 → OpenClaw(用户消息)
|
|
40
|
-
|
|
41
|
-
```json
|
|
42
|
-
{ "type": "message", "userId": "user_001", "text": "你好" }
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 上行:OpenClaw → 后端(Agent 回复)
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
{ "type": "reply", "userId": "user_001", "text": "你好!有什么可以帮你的?" }
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## 配置项
|
|
52
|
-
|
|
53
|
-
| 配置键 | 类型 | 说明 |
|
|
54
|
-
|--------|------|------|
|
|
55
|
-
| `channels.dcgchat.enabled` | boolean | 是否启用 |
|
|
56
|
-
| `channels.dcgchat.wsUrl` | string | 后端 WebSocket 地址 |
|
|
57
|
-
|
|
58
|
-
## 开发
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# 安装依赖
|
|
62
|
-
pnpm install
|
|
63
|
-
|
|
64
|
-
# 类型检查
|
|
65
|
-
pnpm typecheck
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 文件结构
|
|
69
|
-
|
|
70
|
-
- `index.ts` - 插件入口
|
|
71
|
-
- `src/channel.ts` - ChannelPlugin 定义
|
|
72
|
-
- `src/runtime.ts` - 插件 runtime
|
|
73
|
-
- `src/types.ts` - 类型定义
|
|
74
|
-
- `src/monitor.ts` - WebSocket 连接与断线重连
|
|
75
|
-
- `src/bot.ts` - 消息处理与 Agent 调用
|
|
76
|
-
|
|
77
|
-
## 后续迭代
|
|
78
|
-
|
|
79
|
-
- [ ] Token 认证
|
|
80
|
-
- [ ] 流式输出
|
|
81
|
-
- [ ] Typing 指示
|
|
82
|
-
- [ ] messageId 去重
|
|
83
|
-
- [ ] 错误消息类型
|