@dcrays/dcgchat-test 0.4.24 → 0.4.27
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/package.json +1 -1
- package/src/bot.ts +6 -3
- package/src/channel.ts +87 -10
- package/src/cronToolCall.ts +2 -2
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
getWorkspaceDir,
|
|
12
12
|
setMsgStatus
|
|
13
13
|
} from './utils/global.js'
|
|
14
|
-
import { resolveAccount, sendDcgchatMedia } from './channel.js'
|
|
14
|
+
import { normalizeOutboundMediaPaths, resolveAccount, sendDcgchatMedia } from './channel.js'
|
|
15
15
|
import { generateSignUrl } from './request/api.js'
|
|
16
16
|
import { sendChunk, sendFinal, sendText as sendTextMsg, sendError, wsSendRaw, sendText } from './transport.js'
|
|
17
17
|
import { dcgLogger } from './utils/log.js'
|
|
@@ -143,8 +143,11 @@ function buildMediaPayload(mediaList: MediaInfo[]): MediaPayload {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
function resolveReplyMediaList(payload: ReplyPayload): string[] {
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
const p = payload as { mediaUrls?: unknown[]; mediaUrl?: unknown }
|
|
147
|
+
if (p.mediaUrls != null && Array.isArray(p.mediaUrls) && p.mediaUrls.length > 0) {
|
|
148
|
+
return normalizeOutboundMediaPaths(p.mediaUrls)
|
|
149
|
+
}
|
|
150
|
+
return normalizeOutboundMediaPaths(p.mediaUrl ?? null)
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
const typingCallbacks = createTypingCallbacks({
|
package/src/channel.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
1
2
|
import type { ChannelPlugin, OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk'
|
|
2
3
|
import { createPluginRuntimeStore, DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk'
|
|
3
4
|
import type { ResolvedDcgchatAccount, DcgchatConfig } from './types.js'
|
|
@@ -103,6 +104,53 @@ function outboundChatId(rawTo: string | undefined, normalizedTo: string): string
|
|
|
103
104
|
return raw.indexOf('dcg-cron:') >= 0 ? raw : normalizedTo
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* 仅从 JSON / `{ file | path | url }` 等结构里取出路径字符串,不做改写(不拼 workspace、不 normalize)。
|
|
109
|
+
*/
|
|
110
|
+
function collectOutboundMediaPaths(item: unknown, out: string[]): void {
|
|
111
|
+
if (item == null) return
|
|
112
|
+
if (typeof item === 'string') {
|
|
113
|
+
const t = item.trim()
|
|
114
|
+
if (!t) return
|
|
115
|
+
if (t.startsWith('[')) {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = JSON.parse(t) as unknown
|
|
118
|
+
collectOutboundMediaPaths(parsed, out)
|
|
119
|
+
return
|
|
120
|
+
} catch {
|
|
121
|
+
/* 非 JSON,按普通路径处理 */
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
out.push(t)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(item)) {
|
|
128
|
+
for (const el of item) collectOutboundMediaPaths(el, out)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
if (typeof item === 'object') {
|
|
132
|
+
const o = item as Record<string, unknown>
|
|
133
|
+
const raw = o.file ?? o.path ?? o.url
|
|
134
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
135
|
+
out.push(raw.trim())
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 将出站 media 展平为路径字符串列表(去重保序;路径保持 Core 原样) */
|
|
141
|
+
export function normalizeOutboundMediaPaths(raw: unknown): string[] {
|
|
142
|
+
const acc: string[] = []
|
|
143
|
+
collectOutboundMediaPaths(raw, acc)
|
|
144
|
+
const seen = new Set<string>()
|
|
145
|
+
const deduped: string[] = []
|
|
146
|
+
for (const p of acc) {
|
|
147
|
+
if (!p || seen.has(p)) continue
|
|
148
|
+
seen.add(p)
|
|
149
|
+
deduped.push(p)
|
|
150
|
+
}
|
|
151
|
+
return deduped
|
|
152
|
+
}
|
|
153
|
+
|
|
106
154
|
export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
|
|
107
155
|
const rawOpt = (opts.sessionKey ?? '').trim()
|
|
108
156
|
const strippedForCron = rawOpt.replace(/^dcg-cron:/i, '').trim()
|
|
@@ -129,7 +177,34 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
129
177
|
return
|
|
130
178
|
}
|
|
131
179
|
|
|
132
|
-
const
|
|
180
|
+
const expanded = normalizeOutboundMediaPaths(opts.mediaUrl)
|
|
181
|
+
if (expanded.length === 0) {
|
|
182
|
+
dcgLogger(
|
|
183
|
+
`dcgchat: sendMedia skipped (no resolvable path): ${typeof opts.mediaUrl === 'string' ? opts.mediaUrl : JSON.stringify(opts.mediaUrl)} sessionKey=${sessionKey}`,
|
|
184
|
+
'error'
|
|
185
|
+
)
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
if (expanded.length > 1) {
|
|
189
|
+
for (const single of expanded) {
|
|
190
|
+
await sendDcgchatMedia({ ...opts, mediaUrl: single })
|
|
191
|
+
}
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
const mediaUrl = expanded[0]
|
|
195
|
+
if (!mediaUrl || !msgCtx.sessionId) {
|
|
196
|
+
dcgLogger(`dcgchat: sendMedia skipped (duplicate in session): ${mediaUrl} sessionId=${msgCtx.sessionId} sessionKey=${sessionKey}`)
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
// 判断文件存在
|
|
200
|
+
try {
|
|
201
|
+
if (!fs.existsSync(mediaUrl)) {
|
|
202
|
+
dcgLogger(`dcgchat: sendMedia skipped (file not found): ${mediaUrl} sessionKey=${sessionKey}`, 'error')
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
dcgLogger(`dcgchat: sendMedia skipped (cannot stat path): ${mediaUrl} ${String(err)} sessionKey=${sessionKey}`, 'error')
|
|
207
|
+
}
|
|
133
208
|
|
|
134
209
|
if (mediaUrl && msgCtx.sessionId) {
|
|
135
210
|
if (hasSentMediaKey(msgCtx.sessionId, mediaUrl)) {
|
|
@@ -143,10 +218,6 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
143
218
|
if (!msgCtx.sessionId) {
|
|
144
219
|
msgCtx.sessionId = sessionId
|
|
145
220
|
}
|
|
146
|
-
if (!mediaUrl || !msgCtx.sessionId) {
|
|
147
|
-
dcgLogger(`dcgchat: sendMedia skipped (duplicate in session): ${mediaUrl} sessionId=${msgCtx.sessionId} sessionKey=${sessionKey}`)
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
221
|
const fileName = mediaUrl?.split(/[\\/]/).pop() || ''
|
|
151
222
|
const notMessageId = `${msgCtx?.messageId}`?.length === 13 || !msgCtx?.messageId
|
|
152
223
|
try {
|
|
@@ -360,11 +431,17 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
360
431
|
}
|
|
361
432
|
|
|
362
433
|
dcgLogger(`channel sendMedia to ${ctx.to}`)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
434
|
+
|
|
435
|
+
const ctxExt = ctx as { mediaUrls?: unknown; mediaUrl?: string }
|
|
436
|
+
const rawMedia = ctxExt.mediaUrls ?? ctxExt.mediaUrl
|
|
437
|
+
const paths = normalizeOutboundMediaPaths(rawMedia)
|
|
438
|
+
for (const mediaUrl of paths) {
|
|
439
|
+
await sendDcgchatMedia({
|
|
440
|
+
sessionKey: to || '',
|
|
441
|
+
mediaUrl,
|
|
442
|
+
...(isCron ? { messageId } : {})
|
|
443
|
+
})
|
|
444
|
+
}
|
|
368
445
|
return {
|
|
369
446
|
channel: "dcgchat-test",
|
|
370
447
|
messageId,
|
package/src/cronToolCall.ts
CHANGED
|
@@ -137,7 +137,7 @@ function patchCronDeliveryInParams(
|
|
|
137
137
|
if (agentId) d.accountId = agentId
|
|
138
138
|
if (announceNoChannel) {
|
|
139
139
|
d.bestEffort = true
|
|
140
|
-
d.channel =
|
|
140
|
+
d.channel = "dcgchat-test"
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -191,7 +191,7 @@ export function cronToolCall(event: { toolName: any; params: any; toolCallId: an
|
|
|
191
191
|
if (params.command.indexOf('cron create') > -1 || params.command.indexOf('cron add') > -1) {
|
|
192
192
|
const newParams = JSON.parse(JSON.stringify(params)) as Record<string, unknown>
|
|
193
193
|
newParams.command =
|
|
194
|
-
params.command.replace('--json', '') + ` --session-key ${sk} --channel ${
|
|
194
|
+
params.command.replace('--json', '') + ` --session-key ${sk} --channel ${"dcgchat-test"} --to dcg-cron:${sk} --json`
|
|
195
195
|
return { params: newParams }
|
|
196
196
|
} else {
|
|
197
197
|
return undefined
|