@dcrays/dcgchat-test 0.3.26 → 0.3.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/package.json +1 -1
- package/src/bot.ts +23 -3
- package/src/channel.ts +1 -0
- package/src/request/api.ts +1 -1
- package/src/utils/searchFile.ts +57 -142
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
2
4
|
import type { ReplyPayload } from 'openclaw/plugin-sdk'
|
|
3
5
|
import { createReplyPrefixContext } from 'openclaw/plugin-sdk'
|
|
4
6
|
import type { InboundMessage } from './types.js'
|
|
5
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
clearSentMediaKeys,
|
|
9
|
+
getDcgchatRuntime,
|
|
10
|
+
getOpenClawConfig,
|
|
11
|
+
getSessionKey,
|
|
12
|
+
getWorkspaceDir,
|
|
13
|
+
setMsgStatus
|
|
14
|
+
} from './utils/global.js'
|
|
6
15
|
import { resolveAccount, sendDcgchatMedia } from './channel.js'
|
|
7
16
|
import { generateSignUrl } from './request/api.js'
|
|
8
|
-
import { extractMobookFiles
|
|
17
|
+
import { extractMobookFiles } from './utils/searchFile.js'
|
|
9
18
|
import { sendChunk, sendFinal, sendText as sendTextMsg, sendError, wsSendRaw, sendText } from './transport.js'
|
|
10
19
|
import { dcgLogger } from './utils/log.js'
|
|
11
20
|
import { channelInfo, systemCommand, interruptCommand, ENV, ignoreToolCommand } from './utils/constant.js'
|
|
@@ -403,7 +412,18 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
403
412
|
sessionStreamSuppressed.delete(effectiveSessionKey)
|
|
404
413
|
} else {
|
|
405
414
|
for (const file of extractMobookFiles(completeText)) {
|
|
406
|
-
const
|
|
415
|
+
const candidates: string[] = [file]
|
|
416
|
+
candidates.push(path.join(getWorkspaceDir(), file))
|
|
417
|
+
candidates.push(path.join(getWorkspaceDir(), file.replace(/^\//, '')))
|
|
418
|
+
const underMobook = file.replace(/^\/mobook\//i, '').replace(/^mobook[\\/]/i, '')
|
|
419
|
+
if (underMobook) {
|
|
420
|
+
if (process.platform === 'win32') {
|
|
421
|
+
candidates.push(path.join('C:\\', 'mobook', underMobook))
|
|
422
|
+
} else if (process.platform === 'darwin') {
|
|
423
|
+
candidates.push(path.join(os.homedir(), 'mobook', underMobook))
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const resolved = candidates.find((p) => fs.existsSync(p))
|
|
407
427
|
if (!resolved) continue
|
|
408
428
|
try {
|
|
409
429
|
await sendDcgchatMedia({ sessionKey: effectiveSessionKey, mediaUrl: resolved, text: '' })
|
package/src/channel.ts
CHANGED
|
@@ -36,6 +36,7 @@ export async function sendDcgchatMedia(opts: DcgchatMediaSendOptions): Promise<v
|
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
38
|
const botToken = msgCtx.botToken ?? getOpenClawConfig()?.channels?.["dcgchat-test"]?.botToken ?? ''
|
|
39
|
+
console.log('🚀 ~ sendDcgchatMedia ~ botToken:', botToken)
|
|
39
40
|
const url = opts.mediaUrl ? await ossUpload(opts.mediaUrl, botToken, 1) : ''
|
|
40
41
|
wsSendRaw(msgCtx, {
|
|
41
42
|
response: opts.text ?? '',
|
package/src/request/api.ts
CHANGED
|
@@ -40,7 +40,7 @@ export const queryUserTokenByBotToken = async (botToken: string): Promise<string
|
|
|
40
40
|
const response = await post<{ botToken: string }, { token: string }>('/organization/queryUserTokenByBotToken', { botToken })
|
|
41
41
|
|
|
42
42
|
if (!response || !response.data || !response.data.token) {
|
|
43
|
-
dcgLogger('
|
|
43
|
+
dcgLogger('获取绑定的用户信息失败: ' + JSON.stringify(response), 'error')
|
|
44
44
|
return ''
|
|
45
45
|
}
|
|
46
46
|
|
package/src/utils/searchFile.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { getWorkspaceDir } from './global.js'
|
|
2
1
|
import { dcgLogger } from './log.js'
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import os from 'node:os'
|
|
5
|
-
import path from 'node:path'
|
|
6
2
|
|
|
7
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* 从文本中提取 /mobook 目录下的文件
|
|
5
|
+
* @param {string} text
|
|
6
|
+
* @returns {string[]}
|
|
7
|
+
*/
|
|
8
8
|
const EXT_LIST = [
|
|
9
9
|
// 文档类
|
|
10
10
|
'doc',
|
|
@@ -88,47 +88,11 @@ const EXT_LIST = [
|
|
|
88
88
|
*/
|
|
89
89
|
const EXT_SORTED_FOR_REGEX = [...EXT_LIST].sort((a, b) => b.length - a.length)
|
|
90
90
|
|
|
91
|
-
/** 正则交替串(长扩展名优先) */
|
|
92
|
-
const EXT_ALT = `(${EXT_SORTED_FOR_REGEX.join('|')})`
|
|
93
|
-
/** 文件名片段:中文、常见符号、非贪婪 */
|
|
94
|
-
const FILE_NAME_CLASS = `[\\w\\u4e00-\\u9fa5::《》()()\\-\\s]+?`
|
|
95
|
-
|
|
96
|
-
/** 预编译,避免 extractMobookFiles 每次调用重复构建正则 */
|
|
97
|
-
const RX_EXTRACT = {
|
|
98
|
-
backtick: new RegExp(`\`([^\\\`]+?\\.${EXT_ALT})\``, 'gi'),
|
|
99
|
-
fullPath: new RegExp(`/mobook/${FILE_NAME_CLASS}\\.${EXT_ALT}`, 'gi'),
|
|
100
|
-
winMobook: new RegExp(`(?:[a-zA-Z]:)?[/\\\\]mobook[/\\\\]${FILE_NAME_CLASS}\\.${EXT_ALT}`, 'gi'),
|
|
101
|
-
inline: new RegExp(`mobook下的\\s*(${FILE_NAME_CLASS}\\.${EXT_ALT})`, 'gi'),
|
|
102
|
-
bold: new RegExp(`\\*\\*(${FILE_NAME_CLASS}\\.${EXT_ALT})\\*\\*`, 'gi'),
|
|
103
|
-
loose: new RegExp(`(${FILE_NAME_CLASS}\\.${EXT_ALT})\\s*\\(`, 'gi'),
|
|
104
|
-
/** Markdown 列表:`- 文件名.ext` — 用 matchAll 取捕获组 1,避免 FILE_NAME 含 `-`/空格 时误把「- 」并入文件名 */
|
|
105
|
-
markdownList: new RegExp(`[-*•]\\s+(${FILE_NAME_CLASS}\\.${EXT_ALT})`, 'gi'),
|
|
106
|
-
inlineFile: new RegExp(`${FILE_NAME_CLASS}\\.${EXT_ALT}`, 'i')
|
|
107
|
-
}
|
|
108
|
-
|
|
109
91
|
/** 去除控制符、零宽字符等常见脏值 */
|
|
110
92
|
function stripMobookNoise(s: string) {
|
|
111
93
|
return s.replace(/[\u0000-\u001F\u007F\u200B-\u200D\u200E\u200F\uFEFF]/g, '')
|
|
112
94
|
}
|
|
113
95
|
|
|
114
|
-
/**
|
|
115
|
-
* 从路径或 mobook 引用中取出「提到的文件名」:去掉盘符、/mobook、\\mobook\\ 等前缀后取 basename
|
|
116
|
-
*/
|
|
117
|
-
function toMobookReferencedBasename(p: string): string {
|
|
118
|
-
let s = stripMobookNoise(p).trim()
|
|
119
|
-
if (!s) return ''
|
|
120
|
-
s = s.replace(/^(?:[a-zA-Z]:)?[/\\]+mobook[/\\]/i, '')
|
|
121
|
-
s = s.replace(/^\/mobook\//i, '')
|
|
122
|
-
s = s.replace(/\\/g, '/')
|
|
123
|
-
const parts = s.split('/').filter(Boolean)
|
|
124
|
-
return parts.length ? parts[parts.length - 1]! : s
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function addMobookMentionedFile(result: Set<string>, raw: string) {
|
|
128
|
-
const base = toMobookReferencedBasename(raw)
|
|
129
|
-
if (base && isValidFileName(base)) result.add(base)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
96
|
/**
|
|
133
97
|
* 从文本中扫描 `.../mobook/...` 或 `...\mobook\...` 片段,按最长后缀匹配合法扩展名(兜底)
|
|
134
98
|
*/
|
|
@@ -164,7 +128,7 @@ function collectMobookPathsAfterNeedle(text: string, lower: string, needle: stri
|
|
|
164
128
|
const base = raw.slice(0, -(matchedExt.length + 1))
|
|
165
129
|
const fileName = `${base}.${matchedExt}`
|
|
166
130
|
if (isValidFileName(fileName)) {
|
|
167
|
-
|
|
131
|
+
result.add(normalizePath(`/mobook/${fileName}`))
|
|
168
132
|
}
|
|
169
133
|
from = start + 1
|
|
170
134
|
}
|
|
@@ -176,38 +140,62 @@ function collectMobookPathsByScan(text: string, result: Set<string>): void {
|
|
|
176
140
|
collectMobookPathsAfterNeedle(text, lower, '\\mobook\\', result)
|
|
177
141
|
}
|
|
178
142
|
|
|
179
|
-
/**
|
|
180
|
-
* 从文本中提取提到的 mobook 相关文件名(仅 basename,不含目录)
|
|
181
|
-
* @param text 原始文本
|
|
182
|
-
* @returns 去重后的文件名列表,例如 `['报告.pdf', 'data.xlsx']`
|
|
183
|
-
*/
|
|
184
143
|
export function extractMobookFiles(text = '') {
|
|
185
144
|
if (typeof text !== 'string' || !text.trim()) return []
|
|
186
145
|
// 全角冒号(中文输入常见)→ 半角,便于匹配 c:\mobook\
|
|
187
146
|
text = text.replace(/\uFF1A/g, ':')
|
|
188
147
|
const result = new Set<string>()
|
|
148
|
+
// ✅ 扩展名(必须长扩展名优先,见 EXT_SORTED_FOR_REGEX)
|
|
149
|
+
const EXT = `(${EXT_SORTED_FOR_REGEX.join('|')})`
|
|
150
|
+
// ✅ 文件名字符(增强:支持中文、符号)
|
|
151
|
+
const FILE_NAME = `[\\w\\u4e00-\\u9fa5::《》()()\\-\\s]+?`
|
|
189
152
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
153
|
+
// 1️⃣ `xxx.xxx`
|
|
154
|
+
const backtickReg = new RegExp(`\`([^\\\`]+?\\.${EXT})\``, 'gi')
|
|
155
|
+
;(text.match(backtickReg) || []).forEach((item) => {
|
|
156
|
+
const name = item.replace(/`/g, '').trim()
|
|
157
|
+
if (isValidFileName(name)) {
|
|
158
|
+
result.add(`/mobook/${name}`)
|
|
159
|
+
}
|
|
192
160
|
})
|
|
193
|
-
|
|
194
|
-
|
|
161
|
+
// 2️⃣ /mobook/xxx.xxx
|
|
162
|
+
const fullPathReg = new RegExp(`/mobook/${FILE_NAME}\\.${EXT}`, 'gi')
|
|
163
|
+
;(text.match(fullPathReg) || []).forEach((p) => {
|
|
164
|
+
result.add(normalizePath(p))
|
|
165
|
+
})
|
|
166
|
+
// 2️⃣b Windows 实际保存路径:C:\mobook\xxx、c:/mobook/xxx、\mobook\xxx(模型常写反斜杠,原先无法识别)
|
|
167
|
+
const winMobookReg = new RegExp(`(?:[a-zA-Z]:)?[/\\\\]mobook[/\\\\]${FILE_NAME}\\.${EXT}`, 'gi')
|
|
168
|
+
;(text.match(winMobookReg) || []).forEach((full) => {
|
|
195
169
|
const name = full.replace(/^(?:[a-zA-Z]:)?[/\\\\]mobook[/\\\\]/i, '').trim()
|
|
196
|
-
|
|
170
|
+
if (isValidFileName(name)) {
|
|
171
|
+
result.add(normalizePath(`/mobook/${name}`))
|
|
172
|
+
}
|
|
197
173
|
})
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
174
|
+
// 3️⃣ mobook下的 xxx.xxx
|
|
175
|
+
const inlineReg = new RegExp(`mobook下的\\s*(${FILE_NAME}\\.${EXT})`, 'gi')
|
|
176
|
+
;(text.match(inlineReg) || []).forEach((item) => {
|
|
177
|
+
const match = item.match(new RegExp(`${FILE_NAME}\\.${EXT}`, 'i'))
|
|
178
|
+
if (match && isValidFileName(match[0])) {
|
|
179
|
+
result.add(`/mobook/${match[0].trim()}`)
|
|
180
|
+
}
|
|
201
181
|
})
|
|
202
|
-
|
|
203
|
-
|
|
182
|
+
// 🆕 4️⃣ **xxx.xxx**
|
|
183
|
+
const boldReg = new RegExp(`\\*\\*(${FILE_NAME}\\.${EXT})\\*\\*`, 'gi')
|
|
184
|
+
;(text.match(boldReg) || []).forEach((item) => {
|
|
185
|
+
const name = item.replace(/\*\*/g, '').trim()
|
|
186
|
+
if (isValidFileName(name)) {
|
|
187
|
+
result.add(`/mobook/${name}`)
|
|
188
|
+
}
|
|
204
189
|
})
|
|
205
|
-
|
|
206
|
-
|
|
190
|
+
// 🆕 5️⃣ xxx.xxx (123字节)
|
|
191
|
+
const looseReg = new RegExp(`(${FILE_NAME}\\.${EXT})\\s*\\(`, 'gi')
|
|
192
|
+
;(text.match(looseReg) || []).forEach((item) => {
|
|
193
|
+
const name = item.replace(/\s*\(.+$/, '').trim()
|
|
194
|
+
if (isValidFileName(name)) {
|
|
195
|
+
result.add(`/mobook/${name}`)
|
|
196
|
+
}
|
|
207
197
|
})
|
|
208
|
-
|
|
209
|
-
if (m[1]) addMobookMentionedFile(result, m[1].trim())
|
|
210
|
-
}
|
|
198
|
+
// 6️⃣ 兜底:绝对路径等 `.../mobook/<文件名>.<扩展名>` + 最长后缀匹配 + 去脏字符
|
|
211
199
|
collectMobookPathsByScan(text, result)
|
|
212
200
|
} catch (e) {
|
|
213
201
|
dcgLogger(`extractMobookFiles error:${e}`)
|
|
@@ -230,84 +218,11 @@ function isValidFileName(name: string) {
|
|
|
230
218
|
return true
|
|
231
219
|
}
|
|
232
220
|
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const caseInsensitive = process.platform === 'win32' || process.platform === 'darwin'
|
|
241
|
-
const want = caseInsensitive ? basename.toLowerCase() : basename
|
|
242
|
-
|
|
243
|
-
const stack: Array<{ dir: string; depth: number }> = [{ dir: mobookRoot, depth: 0 }]
|
|
244
|
-
let dirsVisited = 0
|
|
245
|
-
|
|
246
|
-
while (stack.length > 0 && dirsVisited < MOBOOK_FIND_MAX_DIRS) {
|
|
247
|
-
const { dir, depth } = stack.pop()!
|
|
248
|
-
if (depth > MOBOOK_FIND_MAX_DEPTH) continue
|
|
249
|
-
dirsVisited += 1
|
|
250
|
-
|
|
251
|
-
let entries: fs.Dirent[]
|
|
252
|
-
try {
|
|
253
|
-
entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
254
|
-
} catch {
|
|
255
|
-
continue
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const subdirs: string[] = []
|
|
259
|
-
for (const ent of entries) {
|
|
260
|
-
const full = path.join(dir, ent.name)
|
|
261
|
-
if (ent.isFile()) {
|
|
262
|
-
const ok = caseInsensitive ? ent.name.toLowerCase() === want : ent.name === basename
|
|
263
|
-
if (ok) return full
|
|
264
|
-
} else if (ent.isDirectory() && depth < MOBOOK_FIND_MAX_DEPTH) {
|
|
265
|
-
if (ent.name.startsWith('.')) continue
|
|
266
|
-
subdirs.push(full)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
for (let i = subdirs.length - 1; i >= 0; i--) {
|
|
270
|
-
stack.push({ dir: subdirs[i]!, depth: depth + 1 })
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return undefined
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function getMobookRoot(): string | undefined {
|
|
277
|
-
if (process.platform === 'win32') return path.join('C:\\', 'mobook')
|
|
278
|
-
if (process.platform === 'darwin') return path.join(os.homedir(), 'mobook')
|
|
279
|
-
return undefined
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export function getFilePathByFile(file: string) {
|
|
283
|
-
const ws = getWorkspaceDir()
|
|
284
|
-
const candidates: string[] = [file]
|
|
285
|
-
candidates.push(path.join(ws, file))
|
|
286
|
-
candidates.push(path.join(ws, file.replace(/^\//, '')))
|
|
287
|
-
const underMobook = file.replace(/^\/mobook\//i, '').replace(/^mobook[\\/]/i, '')
|
|
288
|
-
const workspaceMobookRoot = path.join(ws, 'mobook')
|
|
289
|
-
const homeMobookRoot = underMobook ? getMobookRoot() : undefined
|
|
290
|
-
|
|
291
|
-
if (underMobook) {
|
|
292
|
-
if (fs.existsSync(workspaceMobookRoot)) {
|
|
293
|
-
candidates.push(path.join(workspaceMobookRoot, underMobook))
|
|
294
|
-
}
|
|
295
|
-
if (homeMobookRoot) {
|
|
296
|
-
candidates.push(path.join(homeMobookRoot, underMobook))
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
const resolved = candidates.find((p) => fs.existsSync(p))
|
|
300
|
-
if (resolved) return resolved
|
|
301
|
-
|
|
302
|
-
if (!underMobook) return undefined
|
|
303
|
-
const base = path.basename(underMobook)
|
|
304
|
-
if (fs.existsSync(workspaceMobookRoot)) {
|
|
305
|
-
const inWorkspace = findMobookFileByBasename(workspaceMobookRoot, base)
|
|
306
|
-
if (inWorkspace) return inWorkspace
|
|
307
|
-
}
|
|
308
|
-
if (homeMobookRoot) {
|
|
309
|
-
const inHome = findMobookFileByBasename(homeMobookRoot, base)
|
|
310
|
-
if (inHome) return inHome
|
|
311
|
-
}
|
|
312
|
-
return undefined
|
|
221
|
+
/**
|
|
222
|
+
* 规范路径(去重用)
|
|
223
|
+
*/
|
|
224
|
+
function normalizePath(path: string) {
|
|
225
|
+
return path
|
|
226
|
+
.replace(/\/+/g, '/') // 多斜杠 → 单斜杠
|
|
227
|
+
.replace(/\/$/, '') // 去掉结尾 /
|
|
313
228
|
}
|