From 99442d8eb7bcf4e8b92ef6f1c3deeacd95c230ec Mon Sep 17 00:00:00 2001 From: Symb0x76 <9667434@qq.com> Date: Fri, 10 Oct 2025 12:52:15 +0800 Subject: [PATCH] fix(search): search content excludes pic links --- internal/chatlog/ctx/context.go | 6 ++ internal/chatlog/manager.go | 107 ++++++++++++++++++++++++++++++-- internal/model/message.go | 47 ++++++++++++-- 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/internal/chatlog/ctx/context.go b/internal/chatlog/ctx/context.go index a54c34b..8c2db73 100644 --- a/internal/chatlog/ctx/context.go +++ b/internal/chatlog/ctx/context.go @@ -247,6 +247,12 @@ func (c *Context) SetAutoDecrypt(enabled bool) { c.UpdateConfig() } +func (c *Context) SetWeChatInstances(instances []*wechat.Account) { + c.mu.Lock() + defer c.mu.Unlock() + c.WeChatInstances = instances +} + // 更新配置 func (c *Context) UpdateConfig() { diff --git a/internal/chatlog/manager.go b/internal/chatlog/manager.go index 8ce20e7..1a633ba 100644 --- a/internal/chatlog/manager.go +++ b/internal/chatlog/manager.go @@ -5,6 +5,8 @@ import ( "fmt" "os" "strings" + "sync" + "time" "github.com/rs/zerolog/log" "github.com/sjzar/chatlog/internal/chatlog/conf" @@ -18,6 +20,8 @@ import ( "github.com/sjzar/chatlog/pkg/util/dat2img" ) +const initialDecryptPollInterval = 5 * time.Second + // Manager 管理聊天日志应用 type Manager struct { ctx *ctx.Context @@ -31,6 +35,10 @@ type Manager struct { // Terminal UI app *App + + initialDecryptOnce sync.Once + initialDecryptMu sync.Mutex + initialDecryptLastErr string } func New() *Manager { @@ -51,11 +59,14 @@ func (m *Manager) Run(configPath string) error { m.http = http.NewService(m.ctx, m.db) - m.ctx.WeChatInstances = m.wechat.GetWeChatInstances() - if len(m.ctx.WeChatInstances) >= 1 { - m.ctx.SwitchCurrent(m.ctx.WeChatInstances[0]) + instances := m.wechat.GetWeChatInstances() + m.ctx.SetWeChatInstances(instances) + if len(instances) >= 1 { + m.ctx.SwitchCurrent(instances[0]) } + m.startInitialDecryptWatcher() + if m.ctx.HTTPEnabled { // 启动HTTP服务 if err := m.StartService(); err != nil { @@ -68,6 +79,94 @@ func (m *Manager) Run(configPath string) error { return nil } +func (m *Manager) startInitialDecryptWatcher() { + m.initialDecryptOnce.Do(func() { + go m.initialDecryptLoop() + }) +} + +func (m *Manager) initialDecryptLoop() { + if m.tryInitialDecryptOnce() { + return + } + + ticker := time.NewTicker(initialDecryptPollInterval) + defer ticker.Stop() + + for range ticker.C { + if m.tryInitialDecryptOnce() { + return + } + } +} + +func (m *Manager) tryInitialDecryptOnce() bool { + if m.ctx == nil || m.wechat == nil { + return false + } + + instances := m.wechat.GetWeChatInstances() + if len(instances) == 0 { + return false + } + + m.ctx.SetWeChatInstances(instances) + + target := m.selectAccountForAutoDecrypt(instances) + if target == nil { + target = instances[0] + } + + if m.ctx.Current == nil || m.ctx.Current.Name != target.Name || m.ctx.Current.PID != target.PID { + m.ctx.SwitchCurrent(target) + } else { + m.ctx.Refresh() + } + + if err := m.DecryptDBFiles(); err != nil { + m.recordInitialDecryptError(err) + return false + } + + log.Info().Str("account", target.Name).Msg("自动解密完成") + return true +} + +func (m *Manager) selectAccountForAutoDecrypt(instances []*iwechat.Account) *iwechat.Account { + if m.ctx.Current != nil { + for _, inst := range instances { + if inst.Name == m.ctx.Current.Name { + return inst + } + } + } + + if m.ctx.Account != "" { + for _, inst := range instances { + if inst.Name == m.ctx.Account { + return inst + } + } + } + + return nil +} + +func (m *Manager) recordInitialDecryptError(err error) { + if err == nil { + return + } + + msg := err.Error() + m.initialDecryptMu.Lock() + defer m.initialDecryptMu.Unlock() + if msg == m.initialDecryptLastErr { + return + } + m.initialDecryptLastErr = msg + log.Warn().Err(err).Msg("自动解密失败,将继续重试") +} + func (m *Manager) Switch(info *iwechat.Account, history string) error { if m.ctx.AutoDecrypt { if err := m.StopAutoDecrypt(); err != nil { @@ -250,7 +349,7 @@ func (m *Manager) CommandKey(configPath string, pid int, force bool, showXorKey m.wechat = wechat.NewService(m.ctx) - m.ctx.WeChatInstances = m.wechat.GetWeChatInstances() + m.ctx.SetWeChatInstances(m.wechat.GetWeChatInstances()) if len(m.ctx.WeChatInstances) == 0 { return "", fmt.Errorf("wechat process not found") } diff --git a/internal/model/message.go b/internal/model/message.go index d329028..0eaafda 100644 --- a/internal/model/message.go +++ b/internal/model/message.go @@ -384,12 +384,22 @@ func (m *Message) PlainTextContent() string { if voice, ok := m.Contents["voice"]; ok { // 可选时长字段(可能来源于不同表:voicelength/voiceduration/length 秒) var durStr string - if d, ok2 := m.Contents["voicelength"]; ok2 { durStr = fmt.Sprint(d) } - if d, ok2 := m.Contents["voiceduration"]; ok2 { durStr = fmt.Sprint(d) } - if d, ok2 := m.Contents["length"]; ok2 && durStr=="" { durStr = fmt.Sprint(d) } + if d, ok2 := m.Contents["voicelength"]; ok2 { + durStr = fmt.Sprint(d) + } + if d, ok2 := m.Contents["voiceduration"]; ok2 { + durStr = fmt.Sprint(d) + } + if d, ok2 := m.Contents["length"]; ok2 && durStr == "" { + durStr = fmt.Sprint(d) + } if durStr != "" { // 规范成整数秒显示 - if strings.Contains(durStr, ".") { if f,err:=strconv.ParseFloat(durStr,64); err==nil { durStr = fmt.Sprint(int(f+0.5)) } } + if strings.Contains(durStr, ".") { + if f, err := strconv.ParseFloat(durStr, 64); err == nil { + durStr = fmt.Sprint(int(f + 0.5)) + } + } // >=60s 转换为 XmYYs 形式 if secInt, err := strconv.Atoi(durStr); err == nil { if secInt >= 60 { @@ -423,7 +433,7 @@ func (m *Message) PlainTextContent() string { keylist = append(keylist, path) } } - return fmt.Sprintf("![视频](http://%s/video/%s)", host, strings.Join(keylist, ",")) + return fmt.Sprintf("![视频](http://%s/video/%s)", host, strings.Join(keylist, ",")) case MessageTypeAnimation: if m.Contents["cdnurl"] != nil { if cdnURL, ok := m.Contents["cdnurl"].(string); ok { @@ -444,7 +454,32 @@ func (m *Message) PlainTextContent() string { case MessageTypeShare: switch m.SubType { case MessageSubTypeText: - return fmt.Sprintf("[链接|%s](%s)", m.Contents["title"], m.Contents["desc"]) + title := fmt.Sprint(m.Contents["title"]) + desc := fmt.Sprint(m.Contents["desc"]) + url := fmt.Sprint(m.Contents["url"]) + if title == "" { + title = "链接" + } + if desc == "" { + desc = "" + } + if url == "" { + url = "" + } + if url == "" && strings.HasPrefix(desc, "http") { + url = desc + } + if url != "" { + text := fmt.Sprintf("[链接|%s](%s)", title, url) + if desc != "" && desc != url { + text = text + "\n" + desc + } + return text + } + if desc != "" { + return fmt.Sprintf("[链接|%s]\n%s", title, desc) + } + return fmt.Sprintf("[链接|%s]", title) case MessageSubTypeLink, MessageSubTypeLink2: return fmt.Sprintf("[链接|%s](%s)", m.Contents["title"], m.Contents["url"]) case MessageSubTypeFile: