@dcrays/dcgchat 0.4.24 → 0.4.25
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 +86 -6
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,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
import type { ChannelPlugin, OpenClawConfig, PluginRuntime } from 'openclaw/plugin-sdk'
|
|
3
4
|
import { createPluginRuntimeStore, DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk'
|
|
4
5
|
import type { ResolvedDcgchatAccount, DcgchatConfig } from './types.js'
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
getDcgchatRuntime,
|
|
10
11
|
getInfoBySessionKey,
|
|
11
12
|
getOpenClawConfig,
|
|
13
|
+
getWorkspaceDir,
|
|
12
14
|
hasSentMediaKey,
|
|
13
15
|
setCronMessageId
|
|
14
16
|
} from './utils/global.js'
|
|
@@ -104,6 +106,64 @@ function outboundChatId(rawTo: string | undefined, normalizedTo: string): string
|
|
|
104
106
|
return raw.indexOf('dcg-cron:') >= 0 ? raw : normalizedTo
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Core / message 工具可能传入:
|
|
111
|
+
* - 本地绝对路径字符串
|
|
112
|
+
* - 工作区虚拟路径(如 `/mobook/xxx`,需落到 `getWorkspaceDir()`)
|
|
113
|
+
* - `mediaUrls` 为 `{ file: string }[]` 或整段 JSON 字符串
|
|
114
|
+
*/
|
|
115
|
+
function resolveWorkspaceMediaPath(p: string): string {
|
|
116
|
+
const s = p.trim()
|
|
117
|
+
if (!s) return ''
|
|
118
|
+
if (fs.existsSync(s)) return path.normalize(s)
|
|
119
|
+
const rel = s.replace(/^[\\/]+/, '')
|
|
120
|
+
return path.normalize(path.join(getWorkspaceDir(), rel))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function collectOutboundMediaPaths(item: unknown, out: string[]): void {
|
|
124
|
+
if (item == null) return
|
|
125
|
+
if (typeof item === 'string') {
|
|
126
|
+
const t = item.trim()
|
|
127
|
+
if (!t) return
|
|
128
|
+
if (t.startsWith('[')) {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(t) as unknown
|
|
131
|
+
collectOutboundMediaPaths(parsed, out)
|
|
132
|
+
return
|
|
133
|
+
} catch {
|
|
134
|
+
/* 非 JSON,按普通路径处理 */
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
out.push(resolveWorkspaceMediaPath(t))
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(item)) {
|
|
141
|
+
for (const el of item) collectOutboundMediaPaths(el, out)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
if (typeof item === 'object') {
|
|
145
|
+
const o = item as Record<string, unknown>
|
|
146
|
+
const raw = o.file ?? o.path ?? o.url
|
|
147
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
148
|
+
out.push(resolveWorkspaceMediaPath(raw))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 将出站 media 载荷统一为可 `fs` 访问的本地路径列表(去重保序) */
|
|
154
|
+
export function normalizeOutboundMediaPaths(raw: unknown): string[] {
|
|
155
|
+
const acc: string[] = []
|
|
156
|
+
collectOutboundMediaPaths(raw, acc)
|
|
157
|
+
const seen = new Set<string>()
|
|
158
|
+
const deduped: string[] = []
|
|
159
|
+
for (const p of acc) {
|
|
160
|
+
if (!p || seen.has(p)) continue
|
|
161
|
+
seen.add(p)
|
|
162
|
+
deduped.push(p)
|
|
163
|
+
}
|
|
164
|
+
return deduped
|
|
165
|
+
}
|
|
166
|
+
|
|
107
167
|
export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
|
|
108
168
|
const rawOpt = (opts.sessionKey ?? '').trim()
|
|
109
169
|
const strippedForCron = rawOpt.replace(/^dcg-cron:/i, '').trim()
|
|
@@ -130,7 +190,21 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
130
190
|
return
|
|
131
191
|
}
|
|
132
192
|
|
|
133
|
-
const
|
|
193
|
+
const expanded = normalizeOutboundMediaPaths(opts.mediaUrl)
|
|
194
|
+
if (expanded.length === 0) {
|
|
195
|
+
dcgLogger(
|
|
196
|
+
`dcgchat: sendMedia skipped (no resolvable path): ${typeof opts.mediaUrl === 'string' ? opts.mediaUrl : JSON.stringify(opts.mediaUrl)} sessionKey=${sessionKey}`,
|
|
197
|
+
'error'
|
|
198
|
+
)
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
if (expanded.length > 1) {
|
|
202
|
+
for (const single of expanded) {
|
|
203
|
+
await sendDcgchatMedia({ ...opts, mediaUrl: single })
|
|
204
|
+
}
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
const mediaUrl = expanded[0]
|
|
134
208
|
if (!mediaUrl || !msgCtx.sessionId) {
|
|
135
209
|
dcgLogger(`dcgchat: sendMedia skipped (duplicate in session): ${mediaUrl} sessionId=${msgCtx.sessionId} sessionKey=${sessionKey}`)
|
|
136
210
|
return
|
|
@@ -370,11 +444,17 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
370
444
|
}
|
|
371
445
|
|
|
372
446
|
dcgLogger(`channel sendMedia to ${ctx.to}`)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
447
|
+
|
|
448
|
+
const ctxExt = ctx as { mediaUrls?: unknown; mediaUrl?: string }
|
|
449
|
+
const rawMedia = ctxExt.mediaUrls ?? ctxExt.mediaUrl
|
|
450
|
+
const paths = normalizeOutboundMediaPaths(rawMedia)
|
|
451
|
+
for (const mediaUrl of paths) {
|
|
452
|
+
await sendDcgchatMedia({
|
|
453
|
+
sessionKey: to || '',
|
|
454
|
+
mediaUrl,
|
|
455
|
+
...(isCron ? { messageId } : {})
|
|
456
|
+
})
|
|
457
|
+
}
|
|
378
458
|
return {
|
|
379
459
|
channel: "dcgchat",
|
|
380
460
|
messageId,
|