mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
fix(parser): support current QQ chunked exports
This commit is contained in:
1 parent
4777ca9f63
commit
eb2d1ea3bf
2 files changed
+77
-7
No files matched your search
@@ -0,0 +1,69 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { describe, it } from 'node:test'
|
||||
import { ChatType, MessageType } from '@openchatlab/shared-types'
|
||||
|
||||
import { detectFormat, findEntryFileInDirectory, parseFileSync } from '../index'
|
||||
|
||||
describe('shuakami-qq-exporter chunked parser', () => {
|
||||
it('detects and parses manifests that store the JSONL format in chunked', async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'chatlab-qce-chunked-'))
|
||||
const chunksDir = join(root, 'chunks')
|
||||
const manifestPath = join(root, 'manifest.json')
|
||||
|
||||
try {
|
||||
mkdirSync(chunksDir)
|
||||
writeFileSync(
|
||||
manifestPath,
|
||||
JSON.stringify({
|
||||
metadata: { name: 'QQChatExporter', version: '0.1.0' },
|
||||
chatInfo: { name: 'Test Group', type: 'group', selfUin: '100' },
|
||||
statistics: { totalMessages: 2 },
|
||||
chunked: {
|
||||
format: 'jsonl',
|
||||
chunksDir: 'chunks',
|
||||
chunkFileExt: '.jsonl',
|
||||
maxMessagesPerChunk: 50000,
|
||||
maxBytesPerChunk: 52428800,
|
||||
chunks: [{ relativePath: 'chunks/c000001.jsonl', count: 2 }],
|
||||
},
|
||||
})
|
||||
)
|
||||
writeFileSync(
|
||||
join(chunksDir, 'c000001.jsonl'),
|
||||
[
|
||||
{
|
||||
id: 'message-1',
|
||||
timestamp: 1752192000000,
|
||||
sender: { uid: 'u_100', uin: '100', name: 'Alice', nickname: 'Alice' },
|
||||
type: 'text',
|
||||
content: { text: 'hello' },
|
||||
},
|
||||
{
|
||||
id: 'message-2',
|
||||
timestamp: 1752192060000,
|
||||
sender: { uid: 'u_200', uin: '200', name: 'Bob', nickname: 'Bob' },
|
||||
type: 'text',
|
||||
content: { text: 'recalled' },
|
||||
recalled: true,
|
||||
},
|
||||
]
|
||||
.map((message) => JSON.stringify(message))
|
||||
.join('\n')
|
||||
)
|
||||
|
||||
assert.equal(detectFormat(manifestPath)?.id, 'shuakami-qq-exporter-chunked')
|
||||
assert.equal(findEntryFileInDirectory(root), manifestPath)
|
||||
|
||||
const result = await parseFileSync(manifestPath)
|
||||
assert.equal(result.meta.type, ChatType.GROUP)
|
||||
assert.equal(result.messages.length, 2)
|
||||
assert.equal(result.messages[1].type, MessageType.RECALL)
|
||||
assert.equal(result.messages[1].content, '[已撤回] recalled')
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -48,8 +48,8 @@ export const feature: FormatFeature = {
|
||||
priority: 5, // 比单文件版本优先级更高
|
||||
extensions: ['.json'],
|
||||
signatures: {
|
||||
// 使用 head 签名 "format": "chunked-jsonl" 来区分。
|
||||
head: [/"format"\s*:\s*"chunked-jsonl"/],
|
||||
// V5 使用 metadata.format,0.1.x 将格式标识移至 chunked.format。
|
||||
head: [/"format"\s*:\s*"chunked-jsonl"/, /"chunked"\s*:\s*\{[^{}]*"format"\s*:\s*"jsonl"/],
|
||||
requiredFields: ['metadata', 'chatInfo'],
|
||||
},
|
||||
}
|
||||
@@ -74,9 +74,9 @@ interface Manifest {
|
||||
metadata: {
|
||||
name?: string
|
||||
copyright?: string
|
||||
exportTime: string
|
||||
exportTime?: string
|
||||
version: string
|
||||
format: string
|
||||
format?: string
|
||||
}
|
||||
chatInfo: {
|
||||
name: string
|
||||
@@ -339,9 +339,10 @@ async function* parseChunkedJsonl(options: ParseOptions): AsyncGenerator<ParseEv
|
||||
}
|
||||
|
||||
// 验证格式
|
||||
if (manifest.metadata.format !== 'chunked-jsonl') {
|
||||
onLog?.('error', `不支持的格式: ${manifest.metadata.format}`)
|
||||
yield { type: 'error', data: new Error(`不支持的格式: ${manifest.metadata.format}`) }
|
||||
if (manifest.metadata.format !== 'chunked-jsonl' && manifest.chunked.format !== 'jsonl') {
|
||||
const format = manifest.metadata.format ?? manifest.chunked.format ?? 'unknown'
|
||||
onLog?.('error', `不支持的格式: ${format}`)
|
||||
yield { type: 'error', data: new Error(`不支持的格式: ${format}`) }
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user