@dcrays/dcgchat-test 0.2.21 → 0.2.23
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/openclaw.plugin.json +4 -2
- package/package.json +7 -8
- package/src/bot.ts +328 -265
- package/src/channel.ts +40 -40
- package/src/monitor.ts +73 -22
- package/src/skill.ts +84 -2
- package/src/tool.ts +11 -10
- package/README.md +0 -83
package/src/channel.ts
CHANGED
|
@@ -114,7 +114,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
114
114
|
id: "dcgchat-test",
|
|
115
115
|
label: "书灵墨宝",
|
|
116
116
|
selectionLabel: "书灵墨宝",
|
|
117
|
-
docsPath: "/channels/dcgchat",
|
|
117
|
+
docsPath: "/channels/dcgchat-test",
|
|
118
118
|
docsLabel: "dcgchat-test",
|
|
119
119
|
blurb: "连接 OpenClaw 与 书灵墨宝 产品",
|
|
120
120
|
order: 80,
|
|
@@ -131,7 +131,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
131
131
|
effects: true,
|
|
132
132
|
// blockStreaming: true,
|
|
133
133
|
},
|
|
134
|
-
reload: { configPrefixes: ["channels.dcgchat"] },
|
|
134
|
+
reload: { configPrefixes: ["channels.dcgchat-test"] },
|
|
135
135
|
configSchema: {
|
|
136
136
|
schema: {
|
|
137
137
|
type: "object",
|
|
@@ -185,47 +185,47 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
185
185
|
// textChunkLimit: 25,
|
|
186
186
|
textChunkLimit: 4000,
|
|
187
187
|
sendText: async (ctx) => {
|
|
188
|
-
|
|
188
|
+
const ws = getWsConnection()
|
|
189
189
|
const params = getMsgParams();
|
|
190
190
|
const log = console.log;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
191
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
192
|
+
const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
|
|
193
|
+
const content = {
|
|
194
|
+
messageType: "openclaw_bot_chat",
|
|
195
|
+
_userId: params.userId,
|
|
196
|
+
source: "client",
|
|
197
|
+
content: {
|
|
198
|
+
bot_token: botToken,
|
|
199
|
+
domain_id: params.domainId,
|
|
200
|
+
app_id: params.appId,
|
|
201
|
+
bot_id: params.botId,
|
|
202
|
+
agent_id: params.agentId,
|
|
203
|
+
response: ctx.text,
|
|
204
|
+
session_id: params.sessionId,
|
|
205
|
+
message_id: params.messageId || Date.now().toString(),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
ws.send(JSON.stringify(content));
|
|
209
|
+
ws.send(JSON.stringify({
|
|
210
|
+
messageType: "openclaw_bot_chat",
|
|
211
|
+
_userId: params.userId,
|
|
212
|
+
source: "client",
|
|
213
|
+
content: {
|
|
214
|
+
bot_token: botToken,
|
|
215
|
+
domain_id: params.domainId,
|
|
216
|
+
app_id: params.appId,
|
|
217
|
+
bot_id: params.botId,
|
|
218
|
+
agent_id: params.agentId,
|
|
219
|
+
ssession_id: params.sessionId,
|
|
220
|
+
message_id: params.messageId || Date.now().toString(),
|
|
221
|
+
response: '',
|
|
222
|
+
state: 'final',
|
|
223
|
+
},
|
|
224
|
+
}));
|
|
225
|
+
log(`dcgchat[${ctx.accountId}]: channel sendText to ${params.userId}, ${JSON.stringify(content)}`);
|
|
226
|
+
} else {
|
|
227
227
|
log(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> : ${ctx.text}`);
|
|
228
|
-
|
|
228
|
+
}
|
|
229
229
|
return {
|
|
230
230
|
channel: "dcgchat-test",
|
|
231
231
|
messageId: `dcg-${Date.now()}`,
|
package/src/monitor.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
-
import { handleDcgchatMessage } from "./bot.js";
|
|
3
|
+
import { abortMobookappGeneration, handleDcgchatMessage } from "./bot.js";
|
|
4
4
|
import { resolveAccount } from "./channel.js";
|
|
5
5
|
import { setWsConnection } from "./connection.js";
|
|
6
6
|
import type { InboundMessage } from "./types.js";
|
|
7
|
-
import { setMsgParams,setMsgStatus } from "./tool.js";
|
|
7
|
+
import { setMsgParams, setMsgStatus } from "./tool.js";
|
|
8
8
|
import { installSkill, uninstallSkill } from "./skill.js";
|
|
9
9
|
|
|
10
10
|
export type MonitorDcgchatOpts = {
|
|
@@ -16,7 +16,13 @@ export type MonitorDcgchatOpts = {
|
|
|
16
16
|
|
|
17
17
|
const RECONNECT_DELAY_MS = 3000;
|
|
18
18
|
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
19
|
-
const emptyToolText = [
|
|
19
|
+
const emptyToolText = [
|
|
20
|
+
"/new",
|
|
21
|
+
"/search",
|
|
22
|
+
"/stop",
|
|
23
|
+
"/abort",
|
|
24
|
+
"/queue interrupt",
|
|
25
|
+
];
|
|
20
26
|
|
|
21
27
|
function buildConnectUrl(account: Record<string, string>): string {
|
|
22
28
|
const { wsUrl, botToken, userId, domainId, appId } = account;
|
|
@@ -28,7 +34,9 @@ function buildConnectUrl(account: Record<string, string>): string {
|
|
|
28
34
|
return url.toString();
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
export async function monitorDcgchatProvider(
|
|
37
|
+
export async function monitorDcgchatProvider(
|
|
38
|
+
opts: MonitorDcgchatOpts,
|
|
39
|
+
): Promise<void> {
|
|
32
40
|
const { config, runtime, abortSignal, accountId } = opts;
|
|
33
41
|
// @ts-ignore
|
|
34
42
|
const cfg = config ?? (runtime?.config?.() as ClawdbotConfig | undefined);
|
|
@@ -100,7 +108,9 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
if (parsed.messageType === "openclaw_bot_heartbeat") {
|
|
103
|
-
log(
|
|
111
|
+
log(
|
|
112
|
+
`dcgchat[${account.accountId}]: heartbeat ack received, ${data.toString()}`,
|
|
113
|
+
);
|
|
104
114
|
return;
|
|
105
115
|
}
|
|
106
116
|
try {
|
|
@@ -109,25 +119,36 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
109
119
|
err(`dcgchat[${account.accountId}]: invalid JSON received`);
|
|
110
120
|
return;
|
|
111
121
|
}
|
|
112
|
-
|
|
122
|
+
|
|
113
123
|
if (parsed.messageType == "openclaw_bot_chat") {
|
|
114
124
|
const msg = parsed as unknown as InboundMessage;
|
|
115
125
|
if (!emptyToolText.includes(msg.content.text?.trim())) {
|
|
116
|
-
setMsgStatus(
|
|
126
|
+
setMsgStatus("running");
|
|
127
|
+
}
|
|
128
|
+
log(
|
|
129
|
+
`dcgchat[${account.accountId}]: openclaw_bot_chat received, ${JSON.stringify(msg)}`,
|
|
130
|
+
);
|
|
131
|
+
if (msg.content.text === "/stop") {
|
|
132
|
+
const rawConvId = msg.content.session_id as string | undefined;
|
|
133
|
+
const conversationId =
|
|
134
|
+
rawConvId || `${accountId}:${account.botToken}`;
|
|
135
|
+
console.log("🚀 ~ connect ~ conversationId:", conversationId)
|
|
136
|
+
abortMobookappGeneration(conversationId);
|
|
137
|
+
log(`[dcgchat][in] abort conversationId=${conversationId}`);
|
|
138
|
+
return;
|
|
117
139
|
}
|
|
118
|
-
log(`dcgchat[${account.accountId}]: openclaw_bot_chat received, ${JSON.stringify(msg)}`);
|
|
119
140
|
setMsgParams({
|
|
120
141
|
userId: msg._userId,
|
|
121
142
|
token: msg.content.bot_token,
|
|
122
143
|
sessionId: msg.content.session_id,
|
|
123
144
|
messageId: msg.content.message_id,
|
|
124
|
-
domainId: account.domainId ||
|
|
125
|
-
appId: account.appId ||
|
|
145
|
+
domainId: account.domainId || 1000,
|
|
146
|
+
appId: account.appId || "100",
|
|
126
147
|
botId: msg.content.bot_id,
|
|
127
148
|
agentId: msg.content.agent_id,
|
|
128
149
|
});
|
|
129
|
-
msg.content.app_id = account.appId ||
|
|
130
|
-
msg.content.domain_id = account.domainId ||
|
|
150
|
+
msg.content.app_id = account.appId || "100";
|
|
151
|
+
msg.content.domain_id = account.domainId || "1000";
|
|
131
152
|
await handleDcgchatMessage({
|
|
132
153
|
cfg,
|
|
133
154
|
msg,
|
|
@@ -140,24 +161,52 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
140
161
|
}
|
|
141
162
|
},
|
|
142
163
|
});
|
|
143
|
-
}
|
|
144
|
-
const {
|
|
145
|
-
|
|
164
|
+
} else if (parsed.messageType == "openclaw_bot_event") {
|
|
165
|
+
const {
|
|
166
|
+
event_type,
|
|
167
|
+
operation_type,
|
|
168
|
+
skill_url,
|
|
169
|
+
skill_code,
|
|
170
|
+
skill_id,
|
|
171
|
+
bot_token,
|
|
172
|
+
websocket_trace_id,
|
|
173
|
+
} = parsed.content ? parsed.content : ({} as Record<string, any>);
|
|
174
|
+
const content = {
|
|
175
|
+
event_type,
|
|
176
|
+
operation_type,
|
|
177
|
+
skill_url,
|
|
178
|
+
skill_code,
|
|
179
|
+
skill_id,
|
|
180
|
+
bot_token,
|
|
181
|
+
websocket_trace_id,
|
|
182
|
+
};
|
|
146
183
|
if (event_type === "skill") {
|
|
147
|
-
if (
|
|
184
|
+
if (
|
|
185
|
+
operation_type === "install" ||
|
|
186
|
+
operation_type === "enable" ||
|
|
187
|
+
operation_type === "update"
|
|
188
|
+
) {
|
|
148
189
|
installSkill({ path: skill_url, code: skill_code }, content);
|
|
149
|
-
} else if (
|
|
190
|
+
} else if (
|
|
191
|
+
operation_type === "remove" ||
|
|
192
|
+
operation_type === "disable"
|
|
193
|
+
) {
|
|
150
194
|
uninstallSkill({ code: skill_code }, content);
|
|
151
195
|
} else {
|
|
152
|
-
log(
|
|
196
|
+
log(
|
|
197
|
+
`dcgchat[${account.accountId}]: openclaw_bot_event unknown event_type: ${event_type}, ${data.toString()}`,
|
|
198
|
+
);
|
|
153
199
|
}
|
|
154
200
|
} else {
|
|
155
|
-
log(
|
|
201
|
+
log(
|
|
202
|
+
`dcgchat[${account.accountId}]: openclaw_bot_event unknown operation_type: ${operation_type}, ${data.toString()}`,
|
|
203
|
+
);
|
|
156
204
|
}
|
|
157
205
|
} else {
|
|
158
|
-
log(
|
|
206
|
+
log(
|
|
207
|
+
`dcgchat[${account.accountId}]: ignoring unknown messageType: ${parsed.messageType}`,
|
|
208
|
+
);
|
|
159
209
|
}
|
|
160
|
-
|
|
161
210
|
});
|
|
162
211
|
|
|
163
212
|
ws.on("close", (code, reason) => {
|
|
@@ -167,7 +216,9 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
167
216
|
`dcgchat[${account.accountId}]: disconnected (code=${code}, reason=${reason?.toString() || ""})`,
|
|
168
217
|
);
|
|
169
218
|
if (shouldReconnect) {
|
|
170
|
-
log(
|
|
219
|
+
log(
|
|
220
|
+
`dcgchat[${account.accountId}]: reconnecting in ${RECONNECT_DELAY_MS}ms...`,
|
|
221
|
+
);
|
|
171
222
|
setTimeout(connect, RECONNECT_DELAY_MS);
|
|
172
223
|
}
|
|
173
224
|
});
|
package/src/skill.ts
CHANGED
|
@@ -5,15 +5,24 @@ import unzipper from 'unzipper';
|
|
|
5
5
|
import { pipeline } from "stream/promises";
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
+
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
8
9
|
import { logDcgchat } from './log.js';
|
|
9
|
-
import { getWorkspaceDir } from './runtime.js';
|
|
10
|
+
import { getDcgchatRuntime, getWorkspaceDir } from './runtime.js';
|
|
10
11
|
import { getWsConnection } from './connection.js';
|
|
12
|
+
import { resolveAccount } from './channel.js';
|
|
13
|
+
import { getMsgParams } from './tool.js';
|
|
11
14
|
|
|
12
15
|
type ISkillParams = {
|
|
13
16
|
path: string;
|
|
14
17
|
code: string;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
type SkillContext = {
|
|
21
|
+
cfg: ClawdbotConfig;
|
|
22
|
+
accountId: string;
|
|
23
|
+
runtime?: RuntimeEnv;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
function sendEvent(msgContent: Record<string, any>) {
|
|
18
27
|
const ws = getWsConnection()
|
|
19
28
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
@@ -27,7 +36,77 @@ function sendEvent(msgContent: Record<string, any>) {
|
|
|
27
36
|
}
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
|
|
39
|
+
// async function sendNewSessionCommand(ctx: SkillContext) {
|
|
40
|
+
// try {
|
|
41
|
+
// const core = getDcgchatRuntime();
|
|
42
|
+
// const log = ctx.runtime?.log ?? console.log;
|
|
43
|
+
// const params = getMsgParams();
|
|
44
|
+
// const account = resolveAccount(ctx.cfg, ctx.accountId);
|
|
45
|
+
// const userId = String(params.userId);
|
|
46
|
+
|
|
47
|
+
// const route = core.channel.routing.resolveAgentRoute({
|
|
48
|
+
// cfg: ctx.cfg,
|
|
49
|
+
// channel: "dcgchat-test",
|
|
50
|
+
// accountId: account.accountId,
|
|
51
|
+
// peer: { kind: "direct", id: userId },
|
|
52
|
+
// });
|
|
53
|
+
|
|
54
|
+
// const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(ctx.cfg);
|
|
55
|
+
// const bodyFormatted = core.channel.reply.formatAgentEnvelope({
|
|
56
|
+
// channel: "书灵墨宝",
|
|
57
|
+
// from: userId,
|
|
58
|
+
// timestamp: new Date(),
|
|
59
|
+
// envelope: envelopeOptions,
|
|
60
|
+
// body: "/new",
|
|
61
|
+
// });
|
|
62
|
+
|
|
63
|
+
// const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
64
|
+
// Body: bodyFormatted,
|
|
65
|
+
// RawBody: "/new",
|
|
66
|
+
// CommandBody: "/new",
|
|
67
|
+
// From: userId,
|
|
68
|
+
// To: userId,
|
|
69
|
+
// SessionKey: route.sessionKey,
|
|
70
|
+
// AccountId: params.sessionId,
|
|
71
|
+
// ChatType: "direct",
|
|
72
|
+
// SenderName: userId,
|
|
73
|
+
// SenderId: userId,
|
|
74
|
+
// Provider: "dcgchat-test" as const,
|
|
75
|
+
// Surface: "dcgchat-test" as const,
|
|
76
|
+
// MessageSid: Date.now().toString(),
|
|
77
|
+
// Timestamp: Date.now(),
|
|
78
|
+
// WasMentioned: true,
|
|
79
|
+
// CommandAuthorized: true,
|
|
80
|
+
// OriginatingChannel: "dcgchat-test" as const,
|
|
81
|
+
// OriginatingTo: `user:${userId}`,
|
|
82
|
+
// });
|
|
83
|
+
|
|
84
|
+
// const noopDispatcher = {
|
|
85
|
+
// sendToolResult: () => false,
|
|
86
|
+
// sendBlockReply: () => false,
|
|
87
|
+
// sendFinalReply: () => false,
|
|
88
|
+
// waitForIdle: async () => {},
|
|
89
|
+
// getQueuedCounts: () => ({ tool: 0, block: 0, final: 0 }),
|
|
90
|
+
// markComplete: () => {},
|
|
91
|
+
// };
|
|
92
|
+
|
|
93
|
+
// await core.channel.reply.withReplyDispatcher({
|
|
94
|
+
// dispatcher: noopDispatcher,
|
|
95
|
+
// run: () =>
|
|
96
|
+
// core.channel.reply.dispatchReplyFromConfig({
|
|
97
|
+
// ctx: ctxPayload,
|
|
98
|
+
// cfg: ctx.cfg,
|
|
99
|
+
// dispatcher: noopDispatcher,
|
|
100
|
+
// }),
|
|
101
|
+
// });
|
|
102
|
+
|
|
103
|
+
// log(`dcgchat: /new command dispatched silently after skill install`);
|
|
104
|
+
// } catch (err) {
|
|
105
|
+
// logDcgchat.error(`sendNewSessionCommand failed: ${err}`);
|
|
106
|
+
// }
|
|
107
|
+
// }
|
|
108
|
+
|
|
109
|
+
export async function installSkill(params: ISkillParams, msgContent: Record<string, any>, ctx?: SkillContext) {
|
|
31
110
|
const { path: cdnUrl, code } = params;
|
|
32
111
|
const workspacePath = getWorkspaceDir();
|
|
33
112
|
|
|
@@ -129,6 +208,9 @@ export async function installSkill(params: ISkillParams, msgContent: Record<stri
|
|
|
129
208
|
});
|
|
130
209
|
});
|
|
131
210
|
sendEvent({ ...msgContent, status: 'ok' })
|
|
211
|
+
// if (ctx) {
|
|
212
|
+
// await sendNewSessionCommand(ctx);
|
|
213
|
+
// }
|
|
132
214
|
} catch (error) {
|
|
133
215
|
// 如果安装失败,清理目录
|
|
134
216
|
if (fs.existsSync(skillDir)) {
|
package/src/tool.ts
CHANGED
|
@@ -32,19 +32,20 @@ let toolCallId = '';
|
|
|
32
32
|
let toolName = '';
|
|
33
33
|
type PluginHookName = "before_model_resolve" | "before_prompt_build" | "before_agent_start" | "llm_input" | "llm_output" | "agent_end" | "before_compaction" | "after_compaction" | "before_reset" | "message_received" | "message_sending" | "message_sent" | "before_tool_call" | "after_tool_call" | "tool_result_persist" | "before_message_write" | "session_start" | "session_end" | "subagent_spawning" | "subagent_delivery_target" | "subagent_spawned" | "subagent_ended" | "gateway_start" | "gateway_stop";
|
|
34
34
|
const eventList = [
|
|
35
|
-
{event: 'message_received', message: '
|
|
35
|
+
{event: 'message_received', message: ''},
|
|
36
|
+
{event: 'before_model_resolve', message: ''},
|
|
36
37
|
// {event: 'before_prompt_build', message: '正在查阅背景资料,构建思考逻辑'},
|
|
37
38
|
// {event: 'before_agent_start', message: '书灵墨宝已就位,准备开始执行任务'},
|
|
38
|
-
{event: 'subagent_spawning', message: '
|
|
39
|
-
{event: 'subagent_spawned', message: '
|
|
40
|
-
{event: 'subagent_delivery_target', message: '
|
|
41
|
-
{event: 'llm_input', message: '
|
|
42
|
-
{event: 'llm_output', message: '
|
|
43
|
-
{event: 'agent_end', message: '核心任务已处理完毕...'},
|
|
44
|
-
{event: 'subagent_ended', message: '
|
|
39
|
+
{event: 'subagent_spawning', message: ''},
|
|
40
|
+
{event: 'subagent_spawned', message: ''},
|
|
41
|
+
{event: 'subagent_delivery_target', message: ''},
|
|
42
|
+
{event: 'llm_input', message: ''},
|
|
43
|
+
{event: 'llm_output', message: ''},
|
|
44
|
+
// {event: 'agent_end', message: '核心任务已处理完毕...'},
|
|
45
|
+
{event: 'subagent_ended', message: ''},
|
|
45
46
|
// {event: 'before_message_write', message: '正在将本次对话存入记忆库...'},
|
|
46
|
-
{event: 'message_sending', message: ''},
|
|
47
|
-
{event: 'message_send', message: ''},
|
|
47
|
+
// {event: 'message_sending', message: ''},
|
|
48
|
+
// {event: 'message_send', message: ''},
|
|
48
49
|
{event: 'before_tool_call', message: ''},
|
|
49
50
|
{event: 'after_tool_call', message: ''},
|
|
50
51
|
];
|
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
|
-
- [ ] 错误消息类型
|