mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
新的提交
This commit is contained in:
91
src/types/analytics.ts
Normal file
91
src/types/analytics.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
// 分析数据类型定义
|
||||
|
||||
// 聊天统计数据
|
||||
export interface ChatStatistics {
|
||||
totalMessages: number
|
||||
textMessages: number
|
||||
imageMessages: number
|
||||
voiceMessages: number
|
||||
videoMessages: number
|
||||
emojiMessages: number
|
||||
otherMessages: number
|
||||
sentMessages: number
|
||||
receivedMessages: number
|
||||
firstMessageTime: number | null // Unix timestamp
|
||||
lastMessageTime: number | null
|
||||
activeDays: number
|
||||
messageTypeCounts: Record<number, number>
|
||||
}
|
||||
|
||||
// 时间分布统计
|
||||
export interface TimeDistribution {
|
||||
hourlyDistribution: Record<number, number> // 0-23
|
||||
weekdayDistribution: Record<number, number> // 1-7
|
||||
monthlyDistribution: Record<string, number> // YYYY-MM
|
||||
}
|
||||
|
||||
// 联系人排名
|
||||
export interface ContactRanking {
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
sentCount: number
|
||||
receivedCount: number
|
||||
lastMessageTime: number | null
|
||||
}
|
||||
|
||||
// 消息类型标签映射
|
||||
export const MESSAGE_TYPE_LABELS: Record<number, string> = {
|
||||
1: '文本',
|
||||
244813135921: '文本',
|
||||
3: '图片',
|
||||
34: '语音',
|
||||
43: '视频',
|
||||
47: '表情',
|
||||
48: '位置',
|
||||
49: '链接/文件',
|
||||
42: '名片',
|
||||
10000: '系统消息',
|
||||
}
|
||||
|
||||
// 星期几名称
|
||||
export const WEEKDAY_NAMES = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
|
||||
// 获取消息类型分布(用于图表)
|
||||
export function getMessageTypeDistribution(stats: ChatStatistics): Record<string, number> {
|
||||
if (Object.keys(stats.messageTypeCounts).length > 0) {
|
||||
const distribution: Record<string, number> = {}
|
||||
|
||||
for (const [type, count] of Object.entries(stats.messageTypeCounts)) {
|
||||
const typeNum = parseInt(type)
|
||||
const label = MESSAGE_TYPE_LABELS[typeNum] || '其他'
|
||||
distribution[label] = (distribution[label] || 0) + count
|
||||
}
|
||||
|
||||
return distribution
|
||||
}
|
||||
|
||||
return {
|
||||
'文本': stats.textMessages,
|
||||
'图片': stats.imageMessages,
|
||||
'语音': stats.voiceMessages,
|
||||
'视频': stats.videoMessages,
|
||||
'表情': stats.emojiMessages,
|
||||
'其他': stats.otherMessages,
|
||||
}
|
||||
}
|
||||
|
||||
// 计算聊天时长(天数)
|
||||
export function getChatDurationDays(stats: ChatStatistics): number {
|
||||
if (!stats.firstMessageTime || !stats.lastMessageTime) return 0
|
||||
const diffMs = (stats.lastMessageTime - stats.firstMessageTime) * 1000
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24)) + 1
|
||||
}
|
||||
|
||||
// 平均每天消息数
|
||||
export function getAverageMessagesPerDay(stats: ChatStatistics): number {
|
||||
const days = getChatDurationDays(stats)
|
||||
if (days === 0) return 0
|
||||
return stats.totalMessages / days
|
||||
}
|
||||
329
src/types/electron.d.ts
vendored
Normal file
329
src/types/electron.d.ts
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
import type { ChatSession, Message, Contact } from './models'
|
||||
|
||||
export interface ElectronAPI {
|
||||
window: {
|
||||
minimize: () => void
|
||||
maximize: () => void
|
||||
close: () => void
|
||||
openAgreementWindow: () => Promise<boolean>
|
||||
completeOnboarding: () => Promise<boolean>
|
||||
openOnboardingWindow: () => Promise<boolean>
|
||||
setTitleBarOverlay: (options: { symbolColor: string }) => void
|
||||
}
|
||||
config: {
|
||||
get: (key: string) => Promise<unknown>
|
||||
set: (key: string, value: unknown) => Promise<void>
|
||||
clear: () => Promise<boolean>
|
||||
}
|
||||
dialog: {
|
||||
openFile: (options?: Electron.OpenDialogOptions) => Promise<Electron.OpenDialogReturnValue>
|
||||
openDirectory: (options?: Electron.OpenDialogOptions) => Promise<Electron.OpenDialogReturnValue>
|
||||
saveFile: (options?: Electron.SaveDialogOptions) => Promise<Electron.SaveDialogReturnValue>
|
||||
}
|
||||
shell: {
|
||||
openPath: (path: string) => Promise<string>
|
||||
openExternal: (url: string) => Promise<void>
|
||||
}
|
||||
app: {
|
||||
getDownloadsPath: () => Promise<string>
|
||||
getVersion: () => Promise<string>
|
||||
checkForUpdates: () => Promise<{ hasUpdate: boolean; version?: string; releaseNotes?: string }>
|
||||
downloadAndInstall: () => Promise<void>
|
||||
onDownloadProgress: (callback: (progress: number) => void) => () => void
|
||||
onUpdateAvailable: (callback: (info: { version: string; releaseNotes: string }) => void) => () => void
|
||||
}
|
||||
log: {
|
||||
getPath: () => Promise<string>
|
||||
read: () => Promise<{ success: boolean; content?: string; error?: string }>
|
||||
}
|
||||
dbPath: {
|
||||
autoDetect: () => Promise<{ success: boolean; path?: string; error?: string }>
|
||||
scanWxids: (rootPath: string) => Promise<WxidInfo[]>
|
||||
getDefault: () => Promise<string>
|
||||
}
|
||||
wcdb: {
|
||||
testConnection: (dbPath: string, hexKey: string, wxid: string) => Promise<{ success: boolean; error?: string; sessionCount?: number }>
|
||||
open: (dbPath: string, hexKey: string, wxid: string) => Promise<boolean>
|
||||
close: () => Promise<boolean>
|
||||
}
|
||||
key: {
|
||||
autoGetDbKey: () => Promise<{ success: boolean; key?: string; error?: string; logs?: string[] }>
|
||||
autoGetImageKey: (manualDir?: string) => Promise<{ success: boolean; xorKey?: number; aesKey?: string; error?: string }>
|
||||
onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => () => void
|
||||
onImageKeyStatus: (callback: (payload: { message: string }) => void) => () => void
|
||||
}
|
||||
chat: {
|
||||
connect: () => Promise<{ success: boolean; error?: string }>
|
||||
getSessions: () => Promise<{ success: boolean; sessions?: ChatSession[]; error?: string }>
|
||||
getMessages: (sessionId: string, offset?: number, limit?: number) => Promise<{
|
||||
success: boolean;
|
||||
messages?: Message[];
|
||||
hasMore?: boolean;
|
||||
error?: string
|
||||
}>
|
||||
getLatestMessages: (sessionId: string, limit?: number) => Promise<{
|
||||
success: boolean
|
||||
messages?: Message[]
|
||||
error?: string
|
||||
}>
|
||||
getContact: (username: string) => Promise<Contact | null>
|
||||
getContactAvatar: (username: string) => Promise<{ avatarUrl?: string; displayName?: string } | null>
|
||||
getMyAvatarUrl: () => Promise<{ success: boolean; avatarUrl?: string; error?: string }>
|
||||
downloadEmoji: (cdnUrl: string, md5?: string) => Promise<{ success: boolean; localPath?: string; error?: string }>
|
||||
close: () => Promise<boolean>
|
||||
getSessionDetail: (sessionId: string) => Promise<{
|
||||
success: boolean
|
||||
detail?: {
|
||||
wxid: string
|
||||
displayName: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
alias?: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
firstMessageTime?: number
|
||||
latestMessageTime?: number
|
||||
messageTables: { dbName: string; tableName: string; count: number }[]
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
getImageData: (sessionId: string, msgId: string) => Promise<{ success: boolean; data?: string; error?: string }>
|
||||
getVoiceData: (sessionId: string, msgId: string) => Promise<{ success: boolean; data?: string; error?: string }>
|
||||
}
|
||||
image: {
|
||||
decrypt: (payload: { sessionId?: string; imageMd5?: string; imageDatName?: string; force?: boolean }) => Promise<{ success: boolean; localPath?: string; error?: string }>
|
||||
resolveCache: (payload: { sessionId?: string; imageMd5?: string; imageDatName?: string }) => Promise<{ success: boolean; localPath?: string; hasUpdate?: boolean; error?: string }>
|
||||
preload: (payloads: Array<{ sessionId?: string; imageMd5?: string; imageDatName?: string }>) => Promise<boolean>
|
||||
onUpdateAvailable: (callback: (payload: { cacheKey: string; imageMd5?: string; imageDatName?: string }) => void) => () => void
|
||||
onCacheResolved: (callback: (payload: { cacheKey: string; imageMd5?: string; imageDatName?: string; localPath: string }) => void) => () => void
|
||||
}
|
||||
analytics: {
|
||||
getOverallStatistics: () => Promise<{
|
||||
success: boolean
|
||||
data?: {
|
||||
totalMessages: number
|
||||
textMessages: number
|
||||
imageMessages: number
|
||||
voiceMessages: number
|
||||
videoMessages: number
|
||||
emojiMessages: number
|
||||
otherMessages: number
|
||||
sentMessages: number
|
||||
receivedMessages: number
|
||||
firstMessageTime: number | null
|
||||
lastMessageTime: number | null
|
||||
activeDays: number
|
||||
messageTypeCounts: Record<number, number>
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
getContactRankings: (limit?: number) => Promise<{
|
||||
success: boolean
|
||||
data?: Array<{
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
sentCount: number
|
||||
receivedCount: number
|
||||
lastMessageTime: number | null
|
||||
}>
|
||||
error?: string
|
||||
}>
|
||||
getTimeDistribution: () => Promise<{
|
||||
success: boolean
|
||||
data?: {
|
||||
hourlyDistribution: Record<number, number>
|
||||
weekdayDistribution: Record<number, number>
|
||||
monthlyDistribution: Record<string, number>
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
onProgress: (callback: (payload: { status: string; progress: number }) => void) => () => void
|
||||
}
|
||||
groupAnalytics: {
|
||||
getGroupChats: () => Promise<{
|
||||
success: boolean
|
||||
data?: Array<{
|
||||
username: string
|
||||
displayName: string
|
||||
memberCount: number
|
||||
avatarUrl?: string
|
||||
}>
|
||||
error?: string
|
||||
}>
|
||||
getGroupMembers: (chatroomId: string) => Promise<{
|
||||
success: boolean
|
||||
data?: Array<{
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
}>
|
||||
error?: string
|
||||
}>
|
||||
getGroupMessageRanking: (chatroomId: string, limit?: number, startTime?: number, endTime?: number) => Promise<{
|
||||
success: boolean
|
||||
data?: Array<{
|
||||
member: {
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
}
|
||||
messageCount: number
|
||||
}>
|
||||
error?: string
|
||||
}>
|
||||
getGroupActiveHours: (chatroomId: string, startTime?: number, endTime?: number) => Promise<{
|
||||
success: boolean
|
||||
data?: {
|
||||
hourlyDistribution: Record<number, number>
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
getGroupMediaStats: (chatroomId: string, startTime?: number, endTime?: number) => Promise<{
|
||||
success: boolean
|
||||
data?: {
|
||||
typeCounts: Array<{
|
||||
type: number
|
||||
name: string
|
||||
count: number
|
||||
}>
|
||||
total: number
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
}
|
||||
annualReport: {
|
||||
getAvailableYears: () => Promise<{
|
||||
success: boolean
|
||||
data?: number[]
|
||||
error?: string
|
||||
}>
|
||||
generateReport: (year: number) => Promise<{
|
||||
success: boolean
|
||||
data?: {
|
||||
year: number
|
||||
totalMessages: number
|
||||
totalFriends: number
|
||||
coreFriends: Array<{
|
||||
username: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
sentCount: number
|
||||
receivedCount: number
|
||||
}>
|
||||
monthlyTopFriends: Array<{
|
||||
month: number
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
messageCount: number
|
||||
}>
|
||||
peakDay: {
|
||||
date: string
|
||||
messageCount: number
|
||||
topFriend?: string
|
||||
topFriendCount?: number
|
||||
} | null
|
||||
longestStreak: {
|
||||
friendName: string
|
||||
days: number
|
||||
startDate: string
|
||||
endDate: string
|
||||
} | null
|
||||
activityHeatmap: {
|
||||
data: number[][]
|
||||
}
|
||||
midnightKing: {
|
||||
displayName: string
|
||||
count: number
|
||||
percentage: number
|
||||
} | null
|
||||
selfAvatarUrl?: string
|
||||
mutualFriend: {
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
sentCount: number
|
||||
receivedCount: number
|
||||
ratio: number
|
||||
} | null
|
||||
socialInitiative: {
|
||||
initiatedChats: number
|
||||
receivedChats: number
|
||||
initiativeRate: number
|
||||
} | null
|
||||
responseSpeed: {
|
||||
avgResponseTime: number
|
||||
fastestFriend: string
|
||||
fastestTime: number
|
||||
} | null
|
||||
topPhrases: Array<{
|
||||
phrase: string
|
||||
count: number
|
||||
}>
|
||||
}
|
||||
error?: string
|
||||
}>
|
||||
exportImages: (payload: { baseDir: string; folderName: string; images: Array<{ name: string; dataUrl: string }> }) => Promise<{
|
||||
success: boolean
|
||||
dir?: string
|
||||
error?: string
|
||||
}>
|
||||
onProgress: (callback: (payload: { status: string; progress: number }) => void) => () => void
|
||||
}
|
||||
export: {
|
||||
exportSessions: (sessionIds: string[], outputDir: string, options: ExportOptions) => Promise<{
|
||||
success: boolean
|
||||
successCount?: number
|
||||
failCount?: number
|
||||
error?: string
|
||||
}>
|
||||
exportSession: (sessionId: string, outputPath: string, options: ExportOptions) => Promise<{
|
||||
success: boolean
|
||||
error?: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface ExportOptions {
|
||||
format: 'chatlab' | 'chatlab-jsonl' | 'json' | 'html' | 'txt' | 'excel' | 'sql'
|
||||
dateRange?: { start: number; end: number } | null
|
||||
exportMedia?: boolean
|
||||
exportAvatars?: boolean
|
||||
}
|
||||
|
||||
export interface WxidInfo {
|
||||
wxid: string
|
||||
modifiedTime: number
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: ElectronAPI
|
||||
}
|
||||
|
||||
// Electron 类型声明
|
||||
namespace Electron {
|
||||
interface OpenDialogOptions {
|
||||
title?: string
|
||||
defaultPath?: string
|
||||
filters?: { name: string; extensions: string[] }[]
|
||||
properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'createDirectory')[]
|
||||
}
|
||||
interface OpenDialogReturnValue {
|
||||
canceled: boolean
|
||||
filePaths: string[]
|
||||
}
|
||||
interface SaveDialogOptions {
|
||||
title?: string
|
||||
defaultPath?: string
|
||||
filters?: { name: string; extensions: string[] }[]
|
||||
}
|
||||
interface SaveDialogReturnValue {
|
||||
canceled: boolean
|
||||
filePath?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { }
|
||||
55
src/types/models.ts
Normal file
55
src/types/models.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// 聊天会话
|
||||
export interface ChatSession {
|
||||
username: string
|
||||
type: number
|
||||
unreadCount: number
|
||||
summary: string
|
||||
sortTimestamp: number // 用于排序
|
||||
lastTimestamp: number // 用于显示时间
|
||||
lastMsgType: number
|
||||
displayName?: string
|
||||
avatarUrl?: string
|
||||
}
|
||||
|
||||
// 联系人
|
||||
export interface Contact {
|
||||
id: number
|
||||
username: string
|
||||
localType: number
|
||||
alias: string
|
||||
remark: string
|
||||
nickName: string
|
||||
bigHeadUrl: string
|
||||
smallHeadUrl: string
|
||||
}
|
||||
|
||||
// 消息
|
||||
export interface Message {
|
||||
localId: number
|
||||
serverId: number
|
||||
localType: number
|
||||
createTime: number
|
||||
sortSeq: number
|
||||
isSend: number | null
|
||||
senderUsername: string | null
|
||||
parsedContent: string
|
||||
imageMd5?: string
|
||||
imageDatName?: string
|
||||
emojiCdnUrl?: string
|
||||
emojiMd5?: string
|
||||
voiceDurationSeconds?: number
|
||||
// 引用消息
|
||||
quotedContent?: string
|
||||
quotedSender?: string
|
||||
}
|
||||
|
||||
// 分析数据
|
||||
export interface AnalyticsData {
|
||||
totalMessages: number
|
||||
totalDays: number
|
||||
myMessages: number
|
||||
otherMessages: number
|
||||
messagesByType: Record<number, number>
|
||||
messagesByHour: number[]
|
||||
messagesByDay: number[]
|
||||
}
|
||||
Reference in New Issue
Block a user