@dcrays/dcgchat-test 0.2.27 → 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/index.ts +19 -17
- package/package.json +2 -1
- package/src/bot.ts +214 -592
- package/src/channel.ts +113 -178
- package/src/monitor.ts +129 -178
- 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 +119 -197
- package/src/tool.ts +109 -113
- package/src/transport.ts +108 -0
- package/src/types.ts +75 -64
- package/src/utils/constant.ts +4 -0
- package/src/utils/global.ts +112 -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,97 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
if (!isWsOpen()) {
|
|
20
|
+
dcgLogger(`outbound media skipped -> ws not open: ${opts.mediaUrl ?? ''}`)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 =
|
|
27
|
-
const { botToken } = resolveAccount(ctx.cfg, ctx.accountId);
|
|
33
|
+
const fileName = mediaUrl?.split(/[\\/]/).pop() || ''
|
|
28
34
|
|
|
29
35
|
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)}`);
|
|
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
|
-
|
|
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
|
-
}));
|
|
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-test"] 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
83
|
id: "dcgchat-test",
|
|
113
84
|
meta: {
|
|
114
85
|
id: "dcgchat-test",
|
|
115
|
-
label:
|
|
116
|
-
selectionLabel:
|
|
117
|
-
docsPath:
|
|
86
|
+
label: '书灵墨宝',
|
|
87
|
+
selectionLabel: '书灵墨宝',
|
|
88
|
+
docsPath: '/channels/dcgchat',
|
|
118
89
|
docsLabel: "dcgchat-test",
|
|
119
|
-
blurb:
|
|
120
|
-
order: 80
|
|
90
|
+
blurb: '连接 OpenClaw 与 书灵墨宝 产品',
|
|
91
|
+
order: 80
|
|
121
92
|
},
|
|
122
93
|
capabilities: {
|
|
123
|
-
chatTypes: [
|
|
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: [
|
|
105
|
+
reload: { configPrefixes: ['channels.dcgchat'] },
|
|
135
106
|
configSchema: {
|
|
136
107
|
schema: {
|
|
137
|
-
type:
|
|
108
|
+
type: 'object',
|
|
138
109
|
additionalProperties: false,
|
|
139
110
|
properties: {
|
|
140
|
-
enabled: { type:
|
|
141
|
-
wsUrl: { type:
|
|
142
|
-
botToken: { type:
|
|
143
|
-
userId: { type:
|
|
144
|
-
appId: { type:
|
|
145
|
-
domainId: { type:
|
|
146
|
-
capabilities: { type:
|
|
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
|
-
|
|
129
|
+
dcgchat: {
|
|
159
130
|
...(cfg.channels?.["dcgchat-test"] as Record<string, unknown> | undefined),
|
|
160
|
-
enabled
|
|
161
|
-
}
|
|
162
|
-
}
|
|
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:
|
|
181
|
-
}
|
|
151
|
+
hint: 'userId'
|
|
152
|
+
}
|
|
182
153
|
},
|
|
183
154
|
outbound: {
|
|
184
|
-
deliveryMode:
|
|
185
|
-
// textChunkLimit: 25,
|
|
155
|
+
deliveryMode: 'direct',
|
|
186
156
|
textChunkLimit: 4000,
|
|
187
157
|
sendText: async (ctx) => {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
165
|
channel: "dcgchat-test",
|
|
231
166
|
messageId: `dcg-${Date.now()}`,
|
|
232
|
-
chatId:
|
|
233
|
-
}
|
|
167
|
+
chatId: msgCtx.userId.toString()
|
|
168
|
+
}
|
|
234
169
|
},
|
|
235
170
|
sendMedia: async (ctx) => {
|
|
236
|
-
const
|
|
237
|
-
await sendDcgchatMedia(ctx)
|
|
171
|
+
const msgCtx = createOutboundMsgContext(ctx.cfg, ctx.accountId)
|
|
172
|
+
await sendDcgchatMedia({ msgCtx, mediaUrl: ctx.mediaUrl })
|
|
238
173
|
return {
|
|
239
174
|
channel: "dcgchat-test",
|
|
240
175
|
messageId: `dcg-${Date.now()}`,
|
|
241
|
-
chatId:
|
|
242
|
-
}
|
|
243
|
-
}
|
|
176
|
+
chatId: msgCtx.userId.toString()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
244
179
|
},
|
|
245
180
|
gateway: {
|
|
246
181
|
startAccount: async (ctx) => {
|
|
247
|
-
const { monitorDcgchatProvider } = await import(
|
|
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
|
-
|
|
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
|
+
}
|