refactor: 重构部分图表为插件形式

This commit is contained in:
digua
2026-02-19 22:56:41 +08:00
parent 1f4c0cfbf7
commit 8a12aa5c1b
69 changed files with 2969 additions and 2049 deletions
@@ -179,14 +179,16 @@ async function* parseChatLabJsonl(options: ParseOptions): AsyncGenerator<ParseEv
break
case 'member':
const member: ParsedMember = {
platformId: parsed.platformId,
accountName: parsed.accountName,
groupNickname: parsed.groupNickname,
avatar: parsed.avatar,
{
const member: ParsedMember = {
platformId: parsed.platformId,
accountName: parsed.accountName,
groupNickname: parsed.groupNickname,
avatar: parsed.avatar,
}
members.push(member)
memberMap.set(parsed.platformId, member)
}
members.push(member)
memberMap.set(parsed.platformId, member)
break
case 'message':
@@ -31,12 +31,7 @@ import type {
ParsedMessage,
} from '../types'
import { getFileSize, createProgress } from '../utils'
import {
mapChatType,
extractPlatformId,
detectMessageType,
buildContent,
} from './utils/telegram-utils'
import { mapChatType, extractPlatformId, detectMessageType, buildContent } from './utils/telegram-utils'
import type { TelegramChat } from './utils/telegram-utils'
// ==================== 特征定义 ====================
@@ -54,7 +49,8 @@ export const feature: FormatFeature = {
requiredFields: ['messages'],
fieldPatterns: {
// Telegram 特有的聊天类型值,精确区分
telegramChatType: /\"type\"\s*:\s*\"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)\"/,
telegramChatType:
/"type"\s*:\s*"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)"/,
},
},
}
@@ -78,11 +74,7 @@ async function* parseTelegramSingle(options: ParseOptions): AsyncGenerator<Parse
const chatData = await new Promise<TelegramChat | null>((resolve, reject) => {
const readStream = fs.createReadStream(filePath, { encoding: 'utf-8' })
const pipeline = chain([
readStream,
parser(),
streamValues(),
])
const pipeline = chain([readStream, parser(), streamValues()])
let found = false
@@ -33,12 +33,7 @@ import type {
ParsedMessage,
} from '../types'
import { getFileSize, createProgress } from '../utils'
import {
mapChatType,
extractPlatformId,
detectMessageType,
buildContent,
} from './utils/telegram-utils'
import { mapChatType, extractPlatformId, detectMessageType, buildContent } from './utils/telegram-utils'
import type { TelegramChat } from './utils/telegram-utils'
// ==================== 类型定义 ====================
@@ -67,9 +62,7 @@ export const feature: FormatFeature = {
extensions: ['.json'],
signatures: {
// Telegram 导出 JSON 的特征(语言无关:品牌名在所有语言导出中都存在)
head: [
/Telegram/i,
],
head: [/Telegram/i],
// 注意:personal_information 在某些导出配置中是可选的,不能作为必需字段
requiredFields: ['chats'],
},
@@ -93,12 +86,7 @@ export async function scanChats(filePath: string): Promise<TelegramChatInfo[]> {
// 使用 stream-json 解析 chats.list 数组中的每个聊天对象
// ignore 过滤掉 messages 的实际内容以加速扫描
const pipeline = chain([
readStream,
parser(),
pick({ filter: /^chats\.list\.\d+$/ }),
streamValues(),
])
const pipeline = chain([readStream, parser(), pick({ filter: /^chats\.list\.\d+$/ }), streamValues()])
pipeline.on('data', ({ value }: { value: TelegramChat }) => {
const chat = value
@@ -127,7 +127,9 @@ export function buildContent(msg: TelegramMessage): string | null {
const action = msg.action || ''
const members = msg.members?.join(', ') || ''
if (members) return `[${action}] ${members}`
return `[${action}]` || text || null
// action 为空时回退到文本,避免常量表达式触发 lint 规则。
if (action) return `[${action}]`
return text || null
}
// 贴纸:使用 emoji 表示
@@ -78,7 +78,7 @@ export const feature: FormatFeature = {
*/
function cleanLine(line: string): string {
// 移除常见的不可见字符:BOM、LTR Mark、RTL Mark、零宽字符等
return line.replace(/^[\uFEFF\u200E\u200F\u200B\u200C\u200D\u2060]+/, '').trim()
return line.replace(/^(?:\uFEFF|\u200E|\u200F|\u200B|\u200C|\u200D|\u2060)+/, '').trim()
}
// ==================== 消息头正则 ====================
@@ -162,9 +162,7 @@ function parseWhatsAppTime(timeStr: string, isV2Format: boolean = false): number
if (isV2Format) {
// 方括号格式:先移除可能的逗号
const normalizedStr = timeStr.replace(',', '')
const match = normalizedStr.match(
/^(\d{1,4})\/(\d{1,2})\/(\d{2,4}) (\d{1,2}):(\d{2}):(\d{2})$/
)
const match = normalizedStr.match(/^(\d{1,4})\/(\d{1,2})\/(\d{2,4}) (\d{1,2}):(\d{2}):(\d{2})$/)
if (match) {
const [, part1, part2, part3, hour, minute, second] = match
let year: number, month: number, day: number
@@ -186,14 +184,7 @@ function parseWhatsAppTime(timeStr: string, isV2Format: boolean = false): number
year = 2000 + parseInt(part3, 10)
}
const date = new Date(
year,
month - 1,
day,
parseInt(hour, 10),
parseInt(minute, 10),
parseInt(second, 10)
)
const date = new Date(year, month - 1, day, parseInt(hour, 10), parseInt(minute, 10), parseInt(second, 10))
return Math.floor(date.getTime() / 1000)
}
}