mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-16 19:39:11 +08:00
refactor: 重构部分图表为插件形式
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user