@dcrays/dcgchat 0.4.24 → 0.4.26
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 +73 -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
|
@@ -104,6 +104,53 @@ function outboundChatId(rawTo: string | undefined, normalizedTo: string): string
|
|
|
104
104
|
return raw.indexOf('dcg-cron:') >= 0 ? raw : normalizedTo
|
|
105
105
|
}
|
|
106
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
|
+
|
|
107
154
|
export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<void> {
|
|
108
155
|
const rawOpt = (opts.sessionKey ?? '').trim()
|
|
109
156
|
const strippedForCron = rawOpt.replace(/^dcg-cron:/i, '').trim()
|
|
@@ -130,7 +177,21 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
130
177
|
return
|
|
131
178
|
}
|
|
132
179
|
|
|
133
|
-
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]
|
|
134
195
|
if (!mediaUrl || !msgCtx.sessionId) {
|
|
135
196
|
dcgLogger(`dcgchat: sendMedia skipped (duplicate in session): ${mediaUrl} sessionId=${msgCtx.sessionId} sessionKey=${sessionKey}`)
|
|
136
197
|
return
|
|
@@ -370,11 +431,17 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
370
431
|
}
|
|
371
432
|
|
|
372
433
|
dcgLogger(`channel sendMedia to ${ctx.to}`)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
+
}
|
|
378
445
|
return {
|
|
379
446
|
channel: "dcgchat",
|
|
380
447
|
messageId,
|