@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/index.ts +20 -17
- package/package.json +3 -8
- package/src/bot.ts +230 -593
- package/src/channel.ts +110 -178
- package/src/monitor.ts +124 -180
- package/src/{api.ts → request/api.ts} +34 -35
- package/src/request/oss.ts +58 -0
- package/src/request/request.ts +198 -0
- package/src/{userInfo.ts → request/userInfo.ts} +36 -34
- package/src/skill.ts +110 -194
- package/src/tool.ts +102 -113
- package/src/transport.ts +108 -0
- package/src/types.ts +75 -64
- package/src/utils/constant.ts +7 -0
- package/src/utils/global.ts +117 -0
- package/src/utils/log.ts +15 -0
- package/src/utils/searchFile.ts +212 -0
- package/src/connection.ts +0 -11
- package/src/log.ts +0 -46
- package/src/oss.ts +0 -72
- package/src/request.ts +0 -201
- package/src/runtime.ts +0 -40
package/src/channel.ts
CHANGED
|
@@ -1,126 +1,95 @@
|
|
|
1
|
-
import type { ChannelPlugin, OpenClawConfig } from
|
|
2
|
-
import { DEFAULT_ACCOUNT_ID } from
|
|
3
|
-
import type { ResolvedDcgchatAccount, DcgchatConfig } from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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(
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 =
|
|
27
|
-
const { botToken } = resolveAccount(ctx.cfg, ctx.accountId);
|
|
31
|
+
const fileName = mediaUrl?.split(/[\\/]/).pop() || ''
|
|
28
32
|
|
|
29
33
|
try {
|
|
30
|
-
const url =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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:
|
|
84
|
+
label: '书灵墨宝',
|
|
85
|
+
selectionLabel: '书灵墨宝',
|
|
86
|
+
docsPath: '/channels/dcgchat',
|
|
118
87
|
docsLabel: "dcgchat",
|
|
119
|
-
blurb:
|
|
120
|
-
order: 80
|
|
88
|
+
blurb: '连接 OpenClaw 与 书灵墨宝 产品',
|
|
89
|
+
order: 80
|
|
121
90
|
},
|
|
122
91
|
capabilities: {
|
|
123
|
-
chatTypes: [
|
|
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: [
|
|
103
|
+
reload: { configPrefixes: ['channels.dcgchat'] },
|
|
135
104
|
configSchema: {
|
|
136
105
|
schema: {
|
|
137
|
-
type:
|
|
106
|
+
type: 'object',
|
|
138
107
|
additionalProperties: false,
|
|
139
108
|
properties: {
|
|
140
|
-
enabled: { type:
|
|
141
|
-
wsUrl: { type:
|
|
142
|
-
botToken: { type:
|
|
143
|
-
userId: { type:
|
|
144
|
-
appId: { type:
|
|
145
|
-
domainId: { type:
|
|
146
|
-
capabilities: { type:
|
|
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
|
-
|
|
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:
|
|
181
|
-
}
|
|
149
|
+
hint: 'userId'
|
|
150
|
+
}
|
|
182
151
|
},
|
|
183
152
|
outbound: {
|
|
184
|
-
deliveryMode:
|
|
185
|
-
// textChunkLimit: 25,
|
|
153
|
+
deliveryMode: 'direct',
|
|
186
154
|
textChunkLimit: 4000,
|
|
187
155
|
sendText: async (ctx) => {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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:
|
|
233
|
-
}
|
|
164
|
+
chatId: msgCtx.userId.toString()
|
|
165
|
+
}
|
|
234
166
|
},
|
|
235
167
|
sendMedia: async (ctx) => {
|
|
236
|
-
const
|
|
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:
|
|
242
|
-
}
|
|
243
|
-
}
|
|
173
|
+
chatId: msgCtx.userId.toString()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
244
176
|
},
|
|
245
177
|
gateway: {
|
|
246
178
|
startAccount: async (ctx) => {
|
|
247
|
-
const { monitorDcgchatProvider } = await import(
|
|
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
|
-
|
|
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
|
+
}
|