@dcrays/dcgchat 0.2.25 → 0.2.32

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/src/channel.ts CHANGED
@@ -1,126 +1,95 @@
1
- import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
2
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
3
- import type { ResolvedDcgchatAccount, DcgchatConfig } from "./types.js";
4
- import { getWsConnection } from "./connection.js";
5
- import { ossUpload } from "./oss.js";
6
- import { getMsgParams } from "./tool.js";
1
+ import type { ChannelPlugin, OpenClawConfig } from 'openclaw/plugin-sdk'
2
+ import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk'
3
+ import type { ResolvedDcgchatAccount, DcgchatConfig } from './types.js'
4
+ import { ossUpload } from './request/oss.js'
5
+ import { addSentMediaKey, getMsgParams, hasSentMediaKey } from './utils/global.js'
6
+ import { type DcgchatMsgContext, isWsOpen, sendFinal, wsSendRaw } from './transport.js'
7
+ import { dcgLogger, setLogger } from './utils/log.js'
7
8
 
8
- type DcgchatMediaSendContext = {
9
- cfg: OpenClawConfig;
10
- accountId?: string | null;
11
- log?: (message: string) => void;
12
- mediaUrl?: string;
13
- text?: string;
14
- };
9
+ export type DcgchatMediaSendOptions = {
10
+ msgCtx: DcgchatMsgContext
11
+ mediaUrl?: string
12
+ text?: string
13
+ }
15
14
 
16
- export async function sendDcgchatMedia(ctx: DcgchatMediaSendContext): Promise<void> {
17
- const ws = getWsConnection();
18
- const params = getMsgParams();
19
- const log = ctx.log ?? console.log;
15
+ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
16
+ const { msgCtx } = opts
17
+ if (!isWsOpen()) {
18
+ dcgLogger(`outbound media skipped -> ws not open: ${opts.mediaUrl ?? ''}`)
19
+ return
20
+ }
20
21
 
21
- if (ws?.readyState !== WebSocket.OPEN) {
22
- log(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound media skipped -> ${ws?.readyState}: ${ctx.mediaUrl ?? ""}`);
23
- return;
22
+ const mediaUrl = opts.mediaUrl
23
+ if (mediaUrl && hasSentMediaKey(msgCtx.messageId, mediaUrl)) {
24
+ dcgLogger(`dcgchat: sendMedia skipped (duplicate in session): ${mediaUrl}`)
25
+ return
26
+ }
27
+ if (mediaUrl) {
28
+ addSentMediaKey(msgCtx.messageId, mediaUrl)
24
29
  }
25
30
 
26
- const fileName = ctx.mediaUrl?.split(/[\\/]/).pop() || "";
27
- const { botToken } = resolveAccount(ctx.cfg, ctx.accountId);
31
+ const fileName = mediaUrl?.split(/[\\/]/).pop() || ''
28
32
 
29
33
  try {
30
- const url = ctx.mediaUrl ? await ossUpload(ctx.mediaUrl, botToken) : "";
31
- console.log("🚀 ~ sendDcgchatMedia ~ ctx.mediaUrl:", ctx.mediaUrl)
32
- const content = {
33
- messageType: "openclaw_bot_chat",
34
- _userId: params.userId,
35
- source: "client",
36
- content: {
37
- bot_token: botToken,
38
- domain_id: params.domainId,
39
- app_id: params.appId,
40
- bot_id: params.botId,
41
- agent_id: params.agentId,
42
- response: ctx.text ?? "",
43
- files: [{
44
- url,
45
- name: fileName,
46
- }],
47
- session_id: params.sessionId,
48
- message_id: params.messageId || Date.now().toString(),
49
- },
50
- };
51
- ws.send(JSON.stringify(content));
52
- log(`dcgchat[${ctx.accountId}]: sendMedia alioss to ${params.userId}, ${JSON.stringify(content)}`);
34
+ const url = opts.mediaUrl ? await ossUpload(opts.mediaUrl, msgCtx.botToken) : ''
35
+ wsSendRaw(msgCtx, {
36
+ response: opts.text ?? '',
37
+ files: [{ url, name: fileName }]
38
+ })
39
+ dcgLogger(`dcgchat: sendMedia to user ${msgCtx.userId}, file=${fileName}`)
53
40
  } catch (error) {
54
- const content = {
55
- messageType: "openclaw_bot_chat",
56
- _userId: params.userId,
57
- source: "client",
58
- content: {
59
- bot_token: botToken,
60
- domain_id: params.domainId,
61
- app_id: params.appId,
62
- bot_id: params.botId,
63
- agent_id: params.agentId,
64
- response: ctx.text ?? "",
65
- files: [{
66
- url: ctx.mediaUrl,
67
- name: fileName,
68
- }],
69
- session_id: params.sessionId || Date.now().toString(),
70
- message_id: Date.now().toString(),
71
- },
72
- };
73
- ws.send(JSON.stringify(content));
74
- log(`dcgchat[${ctx.accountId}]: error sendMedia to ${params.userId}, ${JSON.stringify(content)}`);
75
- } finally {
76
- ws.send(JSON.stringify({
77
- messageType: "openclaw_bot_chat",
78
- _userId: params.userId,
79
- source: "client",
80
- content: {
81
- bot_token: botToken,
82
- domain_id: params.domainId,
83
- app_id: params.appId,
84
- bot_id: params.botId,
85
- agent_id: params.agentId,
86
- ssession_id: params.sessionId,
87
- message_id: Date.now().toString(),
88
- response: "",
89
- state: "final",
90
- },
91
- }));
41
+ wsSendRaw(msgCtx, {
42
+ response: opts.text ?? '',
43
+ files: [{ url: opts.mediaUrl ?? '', name: fileName }]
44
+ })
45
+ dcgLogger(`dcgchat: error sendMedia to user ${msgCtx.userId}: ${String(error)}`, 'error')
92
46
  }
93
47
  }
94
48
 
95
-
96
49
  export function resolveAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedDcgchatAccount {
97
- const id = accountId ?? DEFAULT_ACCOUNT_ID;
98
- const raw = (cfg.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {};
50
+ const id = accountId ?? DEFAULT_ACCOUNT_ID
51
+ const raw = (cfg.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {}
99
52
  return {
100
53
  accountId: id,
101
54
  enabled: raw.enabled !== false,
102
55
  configured: Boolean(raw.wsUrl),
103
- wsUrl: raw.wsUrl ?? "",
104
- botToken: raw.botToken ?? "",
105
- userId: raw.userId ?? "",
106
- domainId: raw.domainId ?? "",
107
- appId: raw.appId ?? "",
108
- };
56
+ wsUrl: raw.wsUrl ?? '',
57
+ botToken: raw.botToken ?? '',
58
+ userId: raw.userId ?? '',
59
+ domainId: raw.domainId ?? '',
60
+ appId: raw.appId ?? ''
61
+ }
62
+ }
63
+
64
+ /** Build a DcgchatMsgContext for the outbound pipeline (uses global msgParams). */
65
+ function createOutboundMsgContext(cfg: OpenClawConfig, accountId?: string | null): DcgchatMsgContext {
66
+ const params = getMsgParams()
67
+ const { botToken } = resolveAccount(cfg, accountId)
68
+ return {
69
+ userId: params.userId,
70
+ botToken,
71
+ domainId: params.domainId,
72
+ appId: params.appId,
73
+ botId: params.botId,
74
+ agentId: params.agentId,
75
+ sessionId: params.sessionId,
76
+ messageId: params.messageId
77
+ }
109
78
  }
110
79
 
111
80
  export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
112
81
  id: "dcgchat",
113
82
  meta: {
114
83
  id: "dcgchat",
115
- label: "书灵墨宝",
116
- selectionLabel: "书灵墨宝",
117
- docsPath: "/channels/dcgchat",
84
+ label: '书灵墨宝',
85
+ selectionLabel: '书灵墨宝',
86
+ docsPath: '/channels/dcgchat',
118
87
  docsLabel: "dcgchat",
119
- blurb: "连接 OpenClaw 与 书灵墨宝 产品",
120
- order: 80,
88
+ blurb: '连接 OpenClaw 与 书灵墨宝 产品',
89
+ order: 80
121
90
  },
122
91
  capabilities: {
123
- chatTypes: ["direct"],
92
+ chatTypes: ['direct'],
124
93
  polls: false,
125
94
  threads: true,
126
95
  media: true,
@@ -128,24 +97,24 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
128
97
  reactions: true,
129
98
  edit: false,
130
99
  reply: true,
131
- effects: true,
100
+ effects: true
132
101
  // blockStreaming: true,
133
102
  },
134
- reload: { configPrefixes: ["channels.dcgchat"] },
103
+ reload: { configPrefixes: ['channels.dcgchat'] },
135
104
  configSchema: {
136
105
  schema: {
137
- type: "object",
106
+ type: 'object',
138
107
  additionalProperties: false,
139
108
  properties: {
140
- enabled: { type: "boolean" },
141
- wsUrl: { type: "string" },
142
- botToken: { type: "string" },
143
- userId: { type: "string" },
144
- appId: { type: "string" },
145
- domainId: { type: "string" },
146
- capabilities: { type: "array", items: { type: "string" } },
147
- },
148
- },
109
+ enabled: { type: 'boolean' },
110
+ wsUrl: { type: 'string' },
111
+ botToken: { type: 'string' },
112
+ userId: { type: 'string' },
113
+ appId: { type: 'string' },
114
+ domainId: { type: 'string' },
115
+ capabilities: { type: 'array', items: { type: 'string' } }
116
+ }
117
+ }
149
118
  },
150
119
  config: {
151
120
  listAccountIds: () => [DEFAULT_ACCOUNT_ID],
@@ -155,11 +124,11 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
155
124
  ...cfg,
156
125
  channels: {
157
126
  ...cfg.channels,
158
- "dcgchat": {
127
+ dcgchat: {
159
128
  ...(cfg.channels?.["dcgchat"] as Record<string, unknown> | undefined),
160
- enabled,
161
- },
162
- },
129
+ enabled
130
+ }
131
+ }
163
132
  }),
164
133
  isConfigured: (account) => account.configured,
165
134
  describeAccount: (account) => ({
@@ -167,96 +136,59 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
167
136
  enabled: account.enabled,
168
137
  configured: account.configured,
169
138
  wsUrl: account.wsUrl,
170
- botToken: account.botToken ? "***" : "",
139
+ botToken: account.botToken ? '***' : '',
171
140
  userId: account.userId,
172
141
  domainId: account.domainId,
173
- appId: account.appId,
174
- }),
142
+ appId: account.appId
143
+ })
175
144
  },
176
145
  messaging: {
177
146
  normalizeTarget: (raw) => raw?.trim() || undefined,
178
147
  targetResolver: {
179
148
  looksLikeId: (raw) => Boolean(raw?.trim()),
180
- hint: "userId",
181
- },
149
+ hint: 'userId'
150
+ }
182
151
  },
183
152
  outbound: {
184
- deliveryMode: "direct",
185
- // textChunkLimit: 25,
153
+ deliveryMode: 'direct',
186
154
  textChunkLimit: 4000,
187
155
  sendText: async (ctx) => {
188
- const ws = getWsConnection()
189
- const params = getMsgParams();
190
- const log = console.log;
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
- log(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> : ${ctx.text}`);
156
+ const msgCtx = createOutboundMsgContext(ctx.cfg, ctx.accountId)
157
+ if (isWsOpen()) {
158
+ wsSendRaw(msgCtx, { response: ctx.text })
159
+ dcgLogger(`channel sendText to ${msgCtx.userId} ${ctx.text?.slice(0, 50)}`)
228
160
  }
229
161
  return {
230
162
  channel: "dcgchat",
231
163
  messageId: `dcg-${Date.now()}`,
232
- chatId: params.userId.toString(),
233
- };
164
+ chatId: msgCtx.userId.toString()
165
+ }
234
166
  },
235
167
  sendMedia: async (ctx) => {
236
- const params = getMsgParams();
237
- await sendDcgchatMedia(ctx);
168
+ const msgCtx = createOutboundMsgContext(ctx.cfg, ctx.accountId)
169
+ await sendDcgchatMedia({ msgCtx, mediaUrl: ctx.mediaUrl })
238
170
  return {
239
171
  channel: "dcgchat",
240
172
  messageId: `dcg-${Date.now()}`,
241
- chatId: params.userId.toString(),
242
- };
243
- },
173
+ chatId: msgCtx.userId.toString()
174
+ }
175
+ }
244
176
  },
245
177
  gateway: {
246
178
  startAccount: async (ctx) => {
247
- const { monitorDcgchatProvider } = await import("./monitor.js");
248
- const account = resolveAccount(ctx.cfg, ctx.accountId);
179
+ const { monitorDcgchatProvider } = await import('./monitor.js')
180
+ const account = resolveAccount(ctx.cfg, ctx.accountId)
181
+ setLogger(ctx.runtime)
249
182
  if (!account.wsUrl) {
250
- ctx.log?.warn(`dcgchat[${account.accountId}]: wsUrl not configured, skipping`);
251
- return;
183
+ dcgLogger(`dcgchat[${account.accountId}]: wsUrl not configured, skipping`, 'error')
184
+ return
252
185
  }
253
- // ctx.log?.info(`dcgchat[${account.accountId}]: connecting to ${account.wsUrl}`);
254
186
  return monitorDcgchatProvider({
255
187
  config: ctx.cfg,
256
188
  runtime: ctx.runtime,
257
189
  abortSignal: ctx.abortSignal,
258
- accountId: ctx.accountId,
259
- });
260
- },
261
- },
262
- };
190
+ accountId: ctx.accountId
191
+ })
192
+ }
193
+ }
194
+ }