Enhance auto decryption and UI message preview

Added pre-check and error handler for auto decryption: the system now tests decryption before enabling auto mode and stops with a popup if errors occur during operation. The footer UI now displays a real-time preview of the latest message, including sender, time, and content summary, with improved fallback for missing nicknames. Also fixed a bug where batch decryption incorrectly reported success when all files failed.
This commit is contained in:
teest114514
2025-12-16 03:28:06 +08:00
parent cbf5201216
commit 1a214ea4c4
6 changed files with 107 additions and 22 deletions

View File

@@ -1,12 +0,0 @@
{
"permissions": {
"allow": [
"Bash(xargs:*)",
"Bash(tree:*)",
"Bash(go build:*)",
"Bash(go run:*)"
],
"deny": [],
"ask": []
}
}

View File

@@ -4,6 +4,8 @@
未经修改的源代码在 [main 分支](https://github.com/CJYKK/chatlog_backup/tree/main),本人不对代码中的任何内容负责。
感谢 [wx_key](https://github.com/ycccccccy/wx_key) 项目提供的解密源码
目前测试成功微信版本4.1.5.30
## 项目概述
@@ -95,6 +97,16 @@
## 更新日志
### 2025年12月16日
- **自动解密机制优化**
- 增加开启前预检:开启自动解密前会自动运行一次解密测试,失败则禁止开启。
- 增加故障自动熔断:运行过程中若解密失败(如密钥失效),会自动停止服务并弹窗提示,防止错误循环。
- **UI 交互增强**
- 底部状态栏增加最新消息预览:实时显示最新一条消息的发送人、时间及内容摘要。
- 优化发送人显示逻辑:昵称缺失时自动降级显示账号 ID。
- **修复**
- 修复批量解密时即便所有文件失败仍提示成功的 Bug。
### 2025年12月15日
- **重构密钥获取逻辑**:实现 Data Key (DLL) 和 Image Key (原生扫描) 的职责分离与并行执行。
- **优化图片密钥获取**:适配 Dart 版逻辑,支持 60 秒轮询等待,允许用户后置操作(打开图片)。

View File

@@ -188,6 +188,15 @@ func (a *App) refresh() {
a.infoBar.UpdateAutoDecrypt("[未开启]")
}
// Update latest message in footer
if session, err := a.m.GetLatestSession(); err == nil && session != nil {
sender := session.NickName
if sender == "" {
sender = session.UserName
}
a.footer.UpdateLatestMessage(sender, session.NTime.Format("15:04:05"), session.Content)
}
a.Draw()
}
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/sjzar/chatlog/internal/chatlog/database"
"github.com/sjzar/chatlog/internal/chatlog/http"
"github.com/sjzar/chatlog/internal/chatlog/wechat"
"github.com/sjzar/chatlog/internal/model"
iwechat "github.com/sjzar/chatlog/internal/wechat"
"github.com/sjzar/chatlog/pkg/config"
"github.com/sjzar/chatlog/pkg/util"
@@ -296,10 +297,29 @@ func (m *Manager) StartAutoDecrypt() error {
if m.ctx.DataKey == "" || m.ctx.DataDir == "" {
return fmt.Errorf("请先获取密钥")
}
// 尝试运行一次解密,验证环境和密钥是否正常
// 如果解密失败,说明配置或环境有问题,不应开启自动解密
if err := m.DecryptDBFiles(); err != nil {
return fmt.Errorf("初始解密失败,无法开启自动解密: %w", err)
}
if m.ctx.WorkDir == "" {
return fmt.Errorf("请先执行解密数据")
}
m.wechat.SetAutoDecryptErrorHandler(func(err error) {
log.Error().Err(err).Msg("自动解密失败,停止服务")
m.StopAutoDecrypt()
if m.app != nil {
m.app.QueueUpdateDraw(func() {
m.app.showError(fmt.Errorf("自动解密失败,已停止服务: %v", err))
m.app.updateMenuItemsState()
})
}
})
if err := m.wechat.StartAutoDecrypt(); err != nil {
return err
}
@@ -334,6 +354,20 @@ func (m *Manager) RefreshSession() error {
return nil
}
func (m *Manager) GetLatestSession() (*model.Session, error) {
if m.db == nil || m.db.GetDB() == nil {
return nil, nil
}
resp, err := m.db.GetSessions("", 1, 0)
if err != nil {
return nil, err
}
if len(resp.Items) > 0 {
return resp.Items[0], nil
}
return nil, nil
}
func (m *Manager) CommandKey(configPath string, pid int, force bool, showXorKey bool) (string, error) {
var err error

View File

@@ -29,6 +29,7 @@ type Service struct {
pendingActions map[string]bool
mutex sync.Mutex
fm *filemonitor.FileMonitor
errorHandler func(error)
}
type Config interface {
@@ -47,6 +48,11 @@ func NewService(conf Config) *Service {
}
}
// SetAutoDecryptErrorHandler sets the callback for auto decryption errors
func (s *Service) SetAutoDecryptErrorHandler(handler func(error)) {
s.errorHandler = handler
}
// GetWeChatInstances returns all running WeChat instances
func (s *Service) GetWeChatInstances() []*wechat.Account {
wechat.Load()
@@ -145,7 +151,11 @@ func (s *Service) waitAndProcess(dbFile string) {
s.mutex.Unlock()
log.Debug().Msgf("Processing file: %s", dbFile)
s.DecryptDBFile(dbFile)
if err := s.DecryptDBFile(dbFile); err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
}
}
return
}
s.mutex.Unlock()
@@ -203,12 +213,21 @@ func (s *Service) DecryptDBFiles() error {
return err
}
var lastErr error
failCount := 0
for _, dbFile := range dbFiles {
if err := s.DecryptDBFile(dbFile); err != nil {
log.Debug().Msgf("DecryptDBFile %s failed: %v", dbFile, err)
lastErr = err
failCount++
continue
}
}
if len(dbFiles) > 0 && failCount == len(dbFiles) {
return fmt.Errorf("decryption failed for all %d files, last error: %w", len(dbFiles), lastErr)
}
return nil
}

View File

@@ -15,17 +15,19 @@ const (
type Footer struct {
*tview.Flex
title string
copyRight *tview.TextView
help *tview.TextView
title string
copyRight *tview.TextView
help *tview.TextView
latestMessage *tview.TextView
}
func New() *Footer {
footer := &Footer{
Flex: tview.NewFlex(),
title: Title,
copyRight: tview.NewTextView(),
help: tview.NewTextView(),
Flex: tview.NewFlex(),
title: Title,
copyRight: tview.NewTextView(),
help: tview.NewTextView(),
latestMessage: tview.NewTextView(),
}
footer.copyRight.
@@ -39,7 +41,7 @@ func New() *Footer {
footer.help.
SetDynamicColors(true).
SetWrap(true).
SetTextAlign(tview.AlignRight)
SetTextAlign(tview.AlignCenter)
footer.help.
SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor)
@@ -52,9 +54,17 @@ func New() *Footer {
style.GetColorHex(style.MenuBgColor), style.GetColorHex(style.PageHeaderFgColor),
)
footer.latestMessage.
SetDynamicColors(true).
SetWrap(false).
SetTextAlign(tview.AlignRight)
footer.latestMessage.
SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor)
footer.
AddItem(footer.copyRight, 0, 1, false).
AddItem(footer.help, 0, 1, false)
AddItem(footer.help, 0, 2, false).
AddItem(footer.latestMessage, 0, 2, false)
return footer
}
@@ -66,3 +76,16 @@ func (f *Footer) SetCopyRight(text string) {
func (f *Footer) SetHelp(text string) {
f.help.SetText(text)
}
func (f *Footer) UpdateLatestMessage(sender, time, content string) {
runes := []rune(content)
if len(runes) > 15 {
content = string(runes[:15]) + "..."
}
// [颜色]发送人 [颜色]时间 [颜色]内容
text := fmt.Sprintf("[%s]%s [%s]%s [%s]%s",
style.GetColorHex(style.PageHeaderFgColor), sender,
style.GetColorHex(style.InfoBarItemFgColor), time,
style.GetColorHex(style.FgColor), content)
f.latestMessage.SetText(text)
}