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