From e53e97a82eeeef7bcecd66b3a36d99264652baa8 Mon Sep 17 00:00:00 2001 From: Symb0x76 <9667434@qq.com> Date: Wed, 24 Sep 2025 16:04:43 +0800 Subject: [PATCH] fix(Content_type): Content type fix to fit Weixin documenation --- internal/chatlog/http/route.go | 15 +++------ .../datasource/darwinv3/datasource.go | 33 +++++++++++++++++++ internal/wechatdb/datasource/datasource.go | 2 ++ internal/wechatdb/datasource/v4/datasource.go | 29 ++++++++++++++++ .../datasource/windowsv3/datasource.go | 20 +++++++++++ internal/wechatdb/repository/repository.go | 7 ++++ internal/wechatdb/wechatdb.go | 4 +++ 7 files changed, 99 insertions(+), 11 deletions(-) diff --git a/internal/chatlog/http/route.go b/internal/chatlog/http/route.go index 24dd57b..94e4c3d 100644 --- a/internal/chatlog/http/route.go +++ b/internal/chatlog/http/route.go @@ -325,6 +325,7 @@ func (s *Service) handleDashboard(c *gin.Context) { "撤回消息":0, } for k, v := range gstats.ByType { if _, ok := msgTypes[k]; ok { msgTypes[k] += v } } + for k, v := range gstats.ByType { if _, ok := msgTypes[k]; ok { msgTypes[k] += v } } // 时间轴 durationDays := 0.0 @@ -404,7 +405,7 @@ func (s *Service) handleDashboard(c *gin.Context) { WeeklyAvg int `json:"weekly_avg"` MostActiveHour string `json:"most_active_hour"` } - type ContentAnalysis struct { Text int64 `json:"text_messages"`; Images int64 `json:"images"`; Voice int64 `json:"voice_messages"`; Files int64 `json:"files"`; Links int64 `json:"links"`; FileLinkTotal int64 `json:"file_link_total"`; Others int64 `json:"others"` } + type ContentAnalysis struct { Text int64 `json:"text_messages"`; Images int64 `json:"images"`; Voice int64 `json:"voice_messages"`; Files int64 `json:"files"`; Links int64 `json:"links"`; Others int64 `json:"others"` } type GroupListItem struct { Name string `json:"name"`; Members int `json:"members"`; Messages int64 `json:"messages"`; Active bool `json:"active"` } type HourlyActivity struct { Hour string `json:"hour"`; Messages int64 `json:"messages"` } type GroupAnalysis struct { @@ -510,16 +511,8 @@ func (s *Service) handleDashboard(c *gin.Context) { GroupAnalysis: GroupAnalysis{ Title: "群聊分析", Overview: GroupOverview{ TotalGroups: len(overviewGroups), ActiveGroups: activeGroups, TodayMessages: int(todayMessages), WeeklyAvg: weeklyAvg, MostActiveHour: mostActiveHour }, - // 扩展:增加 links 与汇总 file_link_total - ContentAnalysis: ContentAnalysis{ - Text: msgTypes["文本消息"], - Images: msgTypes["图片消息"], - Voice: msgTypes["语音消息"], - Files: msgTypes["文件消息"], - Links: msgTypes["链接消息"], - FileLinkTotal: msgTypes["文件消息"] + msgTypes["链接消息"], - Others: totalMsgs - (msgTypes["文本消息"] + msgTypes["图片消息"] + msgTypes["语音消息"] + msgTypes["文件消息"] + msgTypes["链接消息"]), - }, + // 扩展:增加 links 字段(结构体需更新) + ContentAnalysis: ContentAnalysis{ Text: msgTypes["文本消息"], Images: msgTypes["图片消息"], Voice: msgTypes["语音消息"], Files: msgTypes["文件消息"], Links: msgTypes["链接消息"], Others: totalMsgs - (msgTypes["文本消息"]+msgTypes["图片消息"]+msgTypes["语音消息"]+msgTypes["文件消息"]+msgTypes["链接消息"]) }, GroupList: glist, HourlyActivity: hacts, }, diff --git a/internal/wechatdb/datasource/darwinv3/datasource.go b/internal/wechatdb/datasource/darwinv3/datasource.go index 759a99d..5a942f4 100644 --- a/internal/wechatdb/datasource/darwinv3/datasource.go +++ b/internal/wechatdb/datasource/darwinv3/datasource.go @@ -982,6 +982,39 @@ func (ds *DataSource) MonthlyTrend(ctx context.Context, months int) ([]model.Mon return trends, nil } +// GroupMessageTypeStats 统计群聊消息类型分布(Darwin v3) +func (ds *DataSource) GroupMessageTypeStats(ctx context.Context) (map[string]int64, error) { + result := make(map[string]int64) + // 构建群聊 md5 映射 + mapping := make(map[string]string) + if cdb, err := ds.dbm.GetDB(ChatRoom); err == nil { + if rows, err2 := cdb.QueryContext(ctx, `SELECT IFNULL(m_nsUsrName,"") FROM GroupContact`); err2 == nil { + for rows.Next() { var uname string; if rows.Scan(&uname)==nil && uname!="" { sum := md5.Sum([]byte(uname)); mapping["Chat_"+hex.EncodeToString(sum[:])] = uname } } + rows.Close() + } + } + dbs, err := ds.dbm.GetDBs(Message) + if err != nil { return result, nil } + for _, db := range dbs { + trows, err := db.QueryContext(ctx, `SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'Chat_%' AND name NOT LIKE '%_dels'`) + if err != nil { continue } + var tables []string + for trows.Next() { var name string; if trows.Scan(&name)==nil { tables = append(tables, name) } } + trows.Close() + for _, tbl := range tables { if _, ok := mapping[tbl]; !ok { continue } + // 非49 + q := fmt.Sprintf(`SELECT messageType, COUNT(*) FROM %s WHERE messageType != 49 GROUP BY messageType`, tbl) + rows, err2 := db.QueryContext(ctx, q) + if err2 == nil { for rows.Next(){ var t int64; var cnt int64; if rows.Scan(&t,&cnt)==nil { label := mapV4TypeToLabel(t); if label!="" { result[label]+=cnt } } }; rows.Close() } + // 49 + q49 := fmt.Sprintf(`SELECT msgContent FROM %s WHERE messageType = 49`, tbl) + orows, err3 := db.QueryContext(ctx, q49) + if err3 == nil { for orows.Next(){ var mc string; if orows.Scan(&mc)==nil { lc := strings.ToLower(mc); if strings.Contains(lc,"") && strings.Contains(lc,""){ i1:=strings.Index(lc,""); i2:=strings.Index(lc[i1+6:],""); if i1>=0 && i2>0 { val := strings.TrimSpace(lc[i1+6:i1+6+i2]); if val=="6" { result["文件消息"]++; continue }; if val=="5" || val=="33" { result["链接消息"]++; continue } } } }; if strings.Contains(lc,"http://") || strings.Contains(lc,"https://") { result["链接消息"]++; continue }; result["XML消息"]++ } }; orows.Close() } + } + } + return result, nil +} + // Heatmap 小时x星期(wday: 0=Sunday..6) func (ds *DataSource) Heatmap(ctx context.Context) ([24][7]int64, error) { var grid [24][7]int64 diff --git a/internal/wechatdb/datasource/datasource.go b/internal/wechatdb/datasource/datasource.go index 6c364f8..bb976a7 100644 --- a/internal/wechatdb/datasource/datasource.go +++ b/internal/wechatdb/datasource/datasource.go @@ -38,6 +38,8 @@ type DataSource interface { GlobalMessageStats(ctx context.Context) (*model.GlobalMessageStats, error) // 群聊消息计数:返回 talker(群名) -> count GroupMessageCounts(ctx context.Context) (map[string]int64, error) + // 群聊消息类型分布:返回 typeLabel -> count(只统计群消息) + GroupMessageTypeStats(ctx context.Context) (map[string]int64, error) // 群聊今日消息计数:返回 talker(群名) -> today_count GroupTodayMessageCounts(ctx context.Context) (map[string]int64, error) // 本周(从周一00:00起)群聊消息总数(所有群合计) diff --git a/internal/wechatdb/datasource/v4/datasource.go b/internal/wechatdb/datasource/v4/datasource.go index 2ea531b..92c4b68 100644 --- a/internal/wechatdb/datasource/v4/datasource.go +++ b/internal/wechatdb/datasource/v4/datasource.go @@ -1181,4 +1181,33 @@ func (ds *DataSource) GlobalTodayHourly(ctx context.Context) ([24]int64, error) } } return hours, nil +} + +// GroupMessageTypeStats 统计群聊消息类型分布(v4) +func (ds *DataSource) GroupMessageTypeStats(ctx context.Context) (map[string]int64, error) { + result := make(map[string]int64) + cdb, err := ds.dbm.GetDB(Contact) + if err != nil { return result, nil } + urows, err := cdb.QueryContext(ctx, `SELECT username FROM chat_room`) + if err != nil { return result, nil } + var rooms []string + for urows.Next() { var u string; if urows.Scan(&u)==nil { rooms = append(rooms, u) } } + urows.Close() + if len(rooms) == 0 { return result, nil } + dbs, err := ds.dbm.GetDBs(Message) + if err != nil { return result, nil } + for _, db := range dbs { + for _, uname := range rooms { + md5sum := md5.Sum([]byte(uname)); tbl := "Msg_" + hex.EncodeToString(md5sum[:]) + // 先统计非49 + q := fmt.Sprintf(`SELECT local_type, COUNT(*) FROM %s WHERE local_type != 49 GROUP BY local_type`, tbl) + rows, err := db.QueryContext(ctx, q) + if err == nil { for rows.Next(){ var t int64; var cnt int64; if rows.Scan(&t,&cnt)==nil { label := mapV4TypeToLabel(t); if label!="" { result[label]+=cnt } } }; rows.Close() } + // 处理49 + q49 := fmt.Sprintf(`SELECT message_content FROM %s WHERE local_type=49`, tbl) + orows, err := db.QueryContext(ctx, q49) + if err == nil { for orows.Next(){ var mc []byte; if err := orows.Scan(&mc); err==nil { lc := strings.ToLower(string(mc)); if strings.Contains(lc,"") && strings.Contains(lc,""){ i1:=strings.Index(lc,""); i2:=strings.Index(lc[i1+6:],""); if i1>=0 && i2>0 { val := strings.TrimSpace(lc[i1+6:i1+6+i2]); if val=="6" { result["文件消息"]++; continue }; if val=="5" || val=="33" { result["链接消息"]++; continue } } } }; if strings.Contains(lc,"http://") || strings.Contains(lc,"https://") { result["链接消息"]++; continue }; result["XML消息"]++ } }; orows.Close() } + } + } + return result, nil } \ No newline at end of file diff --git a/internal/wechatdb/datasource/windowsv3/datasource.go b/internal/wechatdb/datasource/windowsv3/datasource.go index 5a0a277..233e9f0 100644 --- a/internal/wechatdb/datasource/windowsv3/datasource.go +++ b/internal/wechatdb/datasource/windowsv3/datasource.go @@ -902,6 +902,26 @@ func (ds *DataSource) GroupWeekMessageCount(ctx context.Context) (int64, error) return total, nil } +// GroupMessageTypeStats 统计群聊消息类型分布(Windows v3) +func (ds *DataSource) GroupMessageTypeStats(ctx context.Context) (map[string]int64, error) { + result := make(map[string]int64) + dbs, err := ds.dbm.GetDBs(Message) + if err != nil { return result, nil } + for _, db := range dbs { + rows, err := db.QueryContext(ctx, `SELECT Type, SubType, COUNT(*) FROM MSG WHERE StrTalker LIKE '%@chatroom' GROUP BY Type, SubType`) + if err != nil { continue } + for rows.Next() { + var t int64; var st int64; var cnt int64 + if rows.Scan(&t,&st,&cnt)==nil { + label := mapV3TypeToLabel(t, st) + if label != "" { result[label] += cnt } + } + } + rows.Close() + } + return result, nil +} + // MonthlyTrend 返回每月 sent/received(近 months 月,若 months<=0 则返回全部) func (ds *DataSource) MonthlyTrend(ctx context.Context, months int) ([]model.MonthlyTrend, error) { agg := make(map[string][2]int64) diff --git a/internal/wechatdb/repository/repository.go b/internal/wechatdb/repository/repository.go index 97db42b..d9b221b 100644 --- a/internal/wechatdb/repository/repository.go +++ b/internal/wechatdb/repository/repository.go @@ -153,3 +153,10 @@ func (r *Repository) GroupWeekMessageCount(ctx context.Context) (int64, error) { } return 0, nil } + +func (r *Repository) GroupMessageTypeStats(ctx context.Context) (map[string]int64, error) { + if ds, ok := r.ds.(interface{ GroupMessageTypeStats(context.Context) (map[string]int64, error) }); ok { + return ds.GroupMessageTypeStats(ctx) + } + return map[string]int64{}, nil +} diff --git a/internal/wechatdb/wechatdb.go b/internal/wechatdb/wechatdb.go index ed2c82d..ba47068 100644 --- a/internal/wechatdb/wechatdb.go +++ b/internal/wechatdb/wechatdb.go @@ -163,3 +163,7 @@ func (w *DB) GroupTodayMessageCounts() (map[string]int64, error) { func (w *DB) GroupWeekMessageCount() (int64, error) { return w.repo.GroupWeekMessageCount(context.Background()) } + +func (w *DB) GroupMessageTypeStats() (map[string]int64, error) { + return w.repo.GroupMessageTypeStats(context.Background()) +}