From cad198d7a00906e319e8628528b12362d5bb60cf Mon Sep 17 00:00:00 2001 From: Sarv Date: Tue, 26 Aug 2025 01:15:13 +0800 Subject: [PATCH] fix(model): enhanced message parsing and display (#223) --- internal/chatlog/app.go | 13 +- internal/model/mediamessage.go | 186 ++++++++++++++++++++++-- internal/model/message.go | 250 ++++++++++++++++++++++++++------- internal/ui/infobar/infobar.go | 18 ++- 4 files changed, 401 insertions(+), 66 deletions(-) diff --git a/internal/chatlog/app.go b/internal/chatlog/app.go index e3f5576..2963ae9 100644 --- a/internal/chatlog/app.go +++ b/internal/chatlog/app.go @@ -148,6 +148,7 @@ func (a *App) refresh() { a.infoBar.UpdateBasicInfo(a.ctx.PID, a.ctx.FullVersion, a.ctx.ExePath) a.infoBar.UpdateStatus(a.ctx.Status) a.infoBar.UpdateDataKey(a.ctx.DataKey) + a.infoBar.UpdateImageKey(a.ctx.ImgKey) a.infoBar.UpdatePlatform(a.ctx.Platform) a.infoBar.UpdateDataUsageDir(a.ctx.DataUsage, a.ctx.DataDir) a.infoBar.UpdateWorkUsageDir(a.ctx.WorkUsage, a.ctx.WorkDir) @@ -201,14 +202,14 @@ func (a *App) inputCapture(event *tcell.EventKey) *tcell.EventKey { func (a *App) initMenu() { getDataKey := &menu.Item{ Index: 2, - Name: "获取数据密钥", - Description: "从进程获取数据密钥", + Name: "获取密钥", + Description: "从进程获取数据密钥 & 图片密钥", Selected: func(i *menu.Item) { modal := tview.NewModal() if runtime.GOOS == "darwin" { - modal.SetText("获取数据密钥中...\n预计需要 20 秒左右的时间,期间微信会卡住,请耐心等待") + modal.SetText("获取密钥中...\n预计需要 20 秒左右的时间,期间微信会卡住,请耐心等待") } else { - modal.SetText("获取数据密钥中...") + modal.SetText("获取密钥中...") } a.mainPages.AddPage("modal", modal, true, true) a.SetFocus(modal) @@ -220,10 +221,10 @@ func (a *App) initMenu() { a.QueueUpdateDraw(func() { if err != nil { // 解密失败 - modal.SetText("获取数据密钥失败: " + err.Error()) + modal.SetText("获取密钥失败: " + err.Error()) } else { // 解密成功 - modal.SetText("获取数据密钥成功") + modal.SetText("获取密钥成功") } // 添加确认按钮 diff --git a/internal/model/mediamessage.go b/internal/model/mediamessage.go index 8b395e9..bfee576 100644 --- a/internal/model/mediamessage.go +++ b/internal/model/mediamessage.go @@ -8,10 +8,12 @@ import ( ) type MediaMsg struct { - XMLName xml.Name `xml:"msg"` - Image Image `xml:"img,omitempty"` - Video Video `xml:"videomsg,omitempty"` - App App `xml:"appmsg,omitempty"` + XMLName xml.Name `xml:"msg"` + Image Image `xml:"img,omitempty"` + Video Video `xml:"videomsg,omitempty"` + App App `xml:"appmsg,omitempty"` + Emoji Emoji `xml:"emoji,omitempty"` + Location Location `xml:"location,omitempty"` } type Image struct { @@ -70,9 +72,63 @@ type App struct { FinderFeed *FinderFeed `xml:"finderFeed,omitempty"` // type 51 视频号 ReferMsg *ReferMsg `xml:"refermsg,omitempty"` // type 57 引用 PatMsg *PatMsg `xml:"patMsg,omitempty"` // type 62 拍一拍 + PatInfo *PatInfo `xml:"patinfo,omitempty"` // type 62 拍一拍 v2 + FinderLive *FinderLive `xml:"finderLive,omitempty"` // type 63 视频号直播 WCPayInfo *WCPayInfo `xml:"wcpayinfo,omitempty"` // type 2000 微信转账 } +type Emoji struct { + FromUsername string `xml:"fromusername,attr"` + ToUsername string `xml:"tousername,attr"` + Type string `xml:"type,attr"` + IdBuffer string `xml:"idbuffer,attr"` + Md5 string `xml:"md5,attr"` + Len string `xml:"len,attr"` + CdnURL string `xml:"cdnurl,attr"` + AesKey string `xml:"aeskey,attr"` + Width string `xml:"width,attr"` + Height string `xml:"height,attr"` + // ProductId string `xml:"productid,attr"` + // AndroidMd5 string `xml:"androidmd5,attr"` + // AndroidLen string `xml:"androidlen,attr"` + // S60v3Md5 string `xml:"s60v3md5,attr"` + // S60v3Len string `xml:"s60v3len,attr"` + // S60v5Md5 string `xml:"s60v5md5,attr"` + // S60v5Len string `xml:"s60v5len,attr"` + // DesignerId string `xml:"designerid,attr"` + // ThumbUrl string `xml:"thumburl,attr"` + // EncryptUrl string `xml:"encrypturl,attr"` + // ExternUrl string `xml:"externurl,attr"` + // ExternMd5 string `xml:"externmd5,attr"` + // TpUrl string `xml:"tpurl,attr"` + // TpAuthKey string `xml:"tpauthkey,attr"` + // AttachedText string `xml:"attachedtext,attr"` + // AttachedTextColor string `xml:"attachedtextcolor,attr"` + // LensId string `xml:"lensid,attr"` + // EmojiAttr string `xml:"emojiattr,attr"` + // LinkId string `xml:"linkid,attr"` + // Desc string `xml:"desc,attr"` +} + +type Location struct { + X string `xml:"x,attr"` + Y string `xml:"y,attr"` + Scale string `xml:"scale,attr"` + Label string `xml:"label,attr"` + MapType string `xml:"maptype,attr"` + Adcode string `xml:"adcode,attr"` + CityName string `xml:"cityname,attr"` + // PoiName string `xml:"poiname,attr"` + // PoiId string `xml:"poiid,attr"` + // BuildingId string `xml:"buildingId,attr"` + // FloorName string `xml:"floorName,attr"` + // PoiCategoryTips string `xml:"poiCategoryTips,attr"` + // PoiBusinessHour string `xml:"poiBusinessHour,attr"` + // PoiPhone string `xml:"poiPhone,attr"` + // PoiPriceTips string `xml:"poiPriceTips,attr"` + // IsFromPoiList string `xml:"isFromPoiList,attr"` +} + // ReferMsg 表示引用消息 type ReferMsg struct { Type int64 `xml:"type"` @@ -153,27 +209,50 @@ type DataItem struct { MessageUUID string `xml:"messageuuid,omitempty"` FromNewMsgID string `xml:"fromnewmsgid,omitempty"` + // 链接 + Link string `xml:"link,omitempty"` + + // 音乐 + StreamWebURL string `xml:"streamweburl,omitempty"` + + // 位置 + Location DataItemLocation `xml:"location,omitempty"` + // 套娃合并转发 DataTitle string `xml:"datatitle,omitempty"` RecordXML *RecordXML `xml:"recordxml,omitempty"` } +type DataItemLocation struct { + Lat string `xml:"lat,attr"` + Lng string `xml:"lng,attr"` + Scale string `xml:"scale,attr"` + Label string `xml:"label,attr"` + PoiName string `xml:"poiname,attr"` +} + type RecordXML struct { RecordInfo RecordInfo `xml:"recordinfo,omitempty"` } -func (r *RecordInfo) String(title, host string) string { +func (r *RecordInfo) String(_type, title, host string) string { buf := strings.Builder{} if title == "" { title = r.Title } - buf.WriteString(fmt.Sprintf("[合并转发|%s]\n", title)) + if title == "" { + title = strings.TrimSpace(strings.ReplaceAll(r.Desc, "\n", " ")) + if len(title) > 80 { + title = title[:80] + "..." + } + } + buf.WriteString(fmt.Sprintf("[%s|%s]\n", _type, title)) for _, item := range r.DataList.DataItems { buf.WriteString(fmt.Sprintf(" %s %s\n", item.SourceName, item.SourceTime)) // 套娃合并转发 if item.DataType == "17" && item.RecordXML != nil { - content := item.RecordXML.RecordInfo.String(item.DataTitle, host) + content := item.RecordXML.RecordInfo.String(_type, item.DataTitle, host) if content != "" { for _, line := range strings.Split(content, "\n") { buf.WriteString(fmt.Sprintf(" %s\n", line)) @@ -182,14 +261,44 @@ func (r *RecordInfo) String(title, host string) string { continue } - switch item.DataFmt { - case "pic", "jpg": + switch item.DataType { + case "2": + // 图片 buf.WriteString(fmt.Sprintf(" ![图片](http://%s/image/%s)\n", host, item.FullMD5)) + case "4": + //视频 + buf.WriteString(fmt.Sprintf(" ![视频](http://%s/video/%s)\n", host, item.FullMD5)) + case "8": + // 文件 + // FIXME 笔记的第一条是 htm 数据,暂时跳过处理 + if item.DataFmt == ".htm" { + continue + } + buf.WriteString(fmt.Sprintf(" [文件|%s](http://%s/file/%s)\n", item.DataTitle, host, item.FullMD5)) + case "5": + // Link + buf.WriteString(fmt.Sprintf(" [链接|%s](%s)\n", item.DataTitle, item.Link)) + case "6": + // Location + buf.WriteString(fmt.Sprintf(" [位置|%s]\n", item.Location.PoiName)) + case "22": + // 视频号 + buf.WriteString(fmt.Sprintf(" [视频号|%s]\n", strings.TrimSpace(strings.ReplaceAll(item.DataDesc, "\n", " ")))) + case "23": + // 视频号直播 + buf.WriteString(fmt.Sprintf(" [视频号直播|%s]\n", strings.TrimSpace(strings.ReplaceAll(item.DataDesc, "\n", " ")))) + case "32": + // 音乐 + buf.WriteString(fmt.Sprintf(" [音乐|%s](%s)\n", item.DataTitle, item.StreamWebURL)) + case "37": + // 动画表情 + buf.WriteString(" [动画表情]\n") default: for _, line := range strings.Split(item.DataDesc, "\n") { buf.WriteString(fmt.Sprintf(" %s\n", line)) } } + buf.WriteString("\n") } return buf.String() @@ -202,6 +311,16 @@ type PatMsg struct { Records Records `xml:"records"` // 拍一拍记录 } +// PatInfo 拍一拍 v2 +type PatInfo struct { + FromUsername string `xml:"fromusername"` + ChatUsername string `xml:"chatusername"` + PattedUsername string `xml:"pattedusername"` + PatSuffix string `xml:"patsuffix"` + PatSuffixVersion int `xml:"patsuffixversion"` + Template string `xml:"template"` +} + // Records 拍一拍记录集合 type Records struct { Record []PatRecord `xml:"record"` // 拍一拍记录列表 @@ -283,10 +402,49 @@ type FinderMegaVideo struct { ObjectNonceID string `xml:"objectNonceId"` } +type FinderLive struct { + FinderLiveID string `xml:"finderLiveID"` + FinderUsername string `xml:"finderUsername"` + FinderObjectID string `xml:"finderObjectID"` + FinderNonceID string `xml:"finderNonceID"` + Nickname string `xml:"nickname"` + HeadURL string `xml:"headUrl"` + Desc string `xml:"desc"` + LiveStatus int `xml:"liveStatus"` + LiveSourceTypeStr string `xml:"liveSourceTypeStr"` + ExtFlag int `xml:"extFlag"` + LiveSecondaryDeviceFlagStr string `xml:"liveSecondaryDeviceFlagStr"` + LiveFlag int `xml:"liveFlag"` + AuthIconURL string `xml:"authIconUrl"` + AuthIconTypeStr string `xml:"authIconTypeStr"` + BindType int `xml:"bindType"` + BizUsername string `xml:"bizUsername"` + BizNickname string `xml:"bizNickname"` + ChargeFlag int `xml:"chargeFlag"` + ReplayStatus int `xml:"replayStatus"` + SpamLiveExtFlagString string `xml:"spamLiveExtFlagString"` + EnterSessionID string `xml:"enterSessionId"` + LiveMode int `xml:"liveMode"` + LiveSubMode int `xml:"liveSubMode"` + Media FinderLiveMedia `xml:"media"` + ShareScene int `xml:"shareScene"` +} + +type FinderLiveMedia struct { + CoverURL FinderLiveMediaCoverURL `xml:"coverUrl"` + Height int `xml:"height"` + Width int `xml:"width"` +} + +type FinderLiveMediaCoverURL struct { + Text string `xml:",cdata"` +} + type SysMsg struct { Type string `xml:"type,attr"` DelChatRoomMember *DelChatRoomMember `xml:"delchatroommember,omitempty"` SysMsgTemplate *SysMsgTemplate `xml:"sysmsgtemplate,omitempty"` + RevokeMsg *RevokeMsg `xml:"revokemsg,omitempty"` } // 第一种消息类型:删除群成员/二维码邀请 @@ -296,6 +454,11 @@ type DelChatRoomMember struct { Link QRLink `xml:"link"` } +type RevokeMsg struct { + Content string `xml:"content"` + RevokeTime int `xml:"revoketime"` +} + type QRLink struct { Scene string `xml:"scene"` Text string `xml:"text"` @@ -345,8 +508,11 @@ type Member struct { } func (s *SysMsg) String() string { - if s.Type == "delchatroommember" { + switch s.Type { + case "delchatroommember": return s.DelChatRoomMemberString() + case "revokemsg": + return s.RevokeMsg.Content } return s.SysMsgTemplateString() } diff --git a/internal/model/message.go b/internal/model/message.go index 71c16b4..4317be9 100644 --- a/internal/model/message.go +++ b/internal/model/message.go @@ -17,6 +17,94 @@ const ( WeChatDarwinV3 = "wechatdarwinv3" ) +const ( + // MessageTypeText 文本 + MessageTypeText = 1 + + // MessageTypeImage 图片 + MessageTypeImage = 3 + + // MessageTypeVoice 语音 + MessageTypeVoice = 34 + + // MessageTypeCard 名片 + MessageTypeCard = 42 + + // MessageTypeVideo 视频 + MessageTypeVideo = 43 + + // MessageTypeAnimation 动画表情 + MessageTypeAnimation = 47 + + // MessageTypeLocation 位置 + MessageTypeLocation = 48 + + // MessageTypeShare 分享 + MessageTypeShare = 49 + + // MessageTypeVOIP 语音通话 + MessageTypeVOIP = 50 + + // MessageTypeSystem 系统 + MessageTypeSystem = 10000 +) + +const ( + // MessageSubTypeText 文本 + MessageSubTypeText = 1 + + // MessageSubTypeLink 链接分享 + MessageSubTypeLink = 4 + + // MessageSubTypeLink2 链接分享 + MessageSubTypeLink2 = 5 + + // MessageSubTypeFile 文件 + MessageSubTypeFile = 6 + + // MessageSubTypeGIF 动图 + MessageSubTypeGIF = 8 + + // MessageSubTypeMergeForward 合并转发 + MessageSubTypeMergeForward = 19 + + // MessageSubTypeNote 笔记 + MessageSubTypeNote = 24 + + // MessageSubTypeMiniProgram 小程序 + MessageSubTypeMiniProgram = 33 + + // MessageSubTypeMiniProgram2 小程序 + MessageSubTypeMiniProgram2 = 36 + + // MessageSubTypeChannel 视频号 + MessageSubTypeChannel = 51 + + // MessageSubTypeQuote 引用 + MessageSubTypeQuote = 57 + + // MessageSubTypePat 拍一拍 + MessageSubTypePat = 62 + + // MessageSubTypeChannelLive 视频号直播 + MessageSubTypeChannelLive = 63 + + // MessageSubTypeChatRoomNotice 群公告 + MessageSubTypeChatRoomNotice = 87 + + // MessageSubTypeMusic 音乐 + MessageSubTypeMusic = 92 + + // MessageSubTypePay 转账 + MessageSubTypePay = 2000 + + // MessageSubTypeRedEnvelope 红包 + MessageSubTypeRedEnvelope = 2001 + + // MessageSubTypeRedEnvelopeCover 红包封面 + MessageSubTypeRedEnvelopeCover = 2003 +) + type Message struct { Version string `json:"-"` // 消息版本,内部判断 Seq int64 `json:"seq"` // 消息序号,10位时间戳 + 3位序号 @@ -46,7 +134,9 @@ func (m *Message) ParseMediaInfo(data string) error { return nil } - if m.Type == 10000 { + if m.Type == MessageTypeSystem { + m.Sender = "系统消息" + m.SenderName = "" var sysMsg SysMsg if err := xml.Unmarshal([]byte(data), &sysMsg); err != nil { m.Content = data @@ -55,8 +145,6 @@ func (m *Message) ParseMediaInfo(data string) error { if Debug { m.SysMsg = &sysMsg } - m.Sender = "系统消息" - m.SenderName = "" m.Content = sysMsg.String() return nil } @@ -76,28 +164,36 @@ func (m *Message) ParseMediaInfo(data string) error { } switch m.Type { - case 3: + case MessageTypeImage: m.Contents["md5"] = msg.Image.MD5 - case 43: + case MessageTypeVideo: if msg.Video.Md5 != "" { m.Contents["md5"] = msg.Video.Md5 } if msg.Video.RawMd5 != "" { m.Contents["rawmd5"] = msg.Video.RawMd5 } - case 49: + case MessageTypeAnimation: + m.Contents["cdnurl"] = msg.Emoji.CdnURL + case MessageTypeLocation: + m.Contents["x"] = msg.Location.X + m.Contents["y"] = msg.Location.Y + m.Contents["label"] = msg.Location.Label + m.Contents["cityname"] = msg.Location.CityName + case MessageTypeShare: m.SubType = int64(msg.App.Type) switch m.SubType { - case 5: + case MessageSubTypeText, MessageSubTypeLink, MessageSubTypeLink2: // 链接 m.Contents["title"] = msg.App.Title + m.Contents["desc"] = msg.App.Des m.Contents["url"] = msg.App.URL - case 6: + case MessageSubTypeFile: // 文件 m.Contents["title"] = msg.App.Title m.Contents["md5"] = msg.App.MD5 - case 19: - // 合并转发 + case MessageSubTypeMergeForward, MessageSubTypeNote, MessageSubTypeChatRoomNotice: + // 合并转发 & 笔记 m.Contents["title"] = msg.App.Title m.Contents["desc"] = msg.App.Des if msg.App.RecordItem == nil { @@ -109,20 +205,20 @@ func (m *Message) ParseMediaInfo(data string) error { return err } m.Contents["recordInfo"] = recordInfo - case 33, 36: + case MessageSubTypeMiniProgram, MessageSubTypeMiniProgram2: // 小程序 m.Contents["title"] = msg.App.SourceDisplayName m.Contents["url"] = msg.App.URL - case 51: + case MessageSubTypeChannel: // 视频号 if msg.App.FinderFeed == nil { break } - m.Contents["title"] = msg.App.FinderFeed.Desc + m.Contents["title"] = strings.TrimSpace(strings.ReplaceAll(msg.App.FinderFeed.Desc, "\n", " ")) if len(msg.App.FinderFeed.MediaList.Media) > 0 { m.Contents["url"] = msg.App.FinderFeed.MediaList.Media[0].URL } - case 57: + case MessageSubTypeQuote: // 引用 m.Content = msg.App.Title if msg.App.ReferMsg == nil { @@ -141,17 +237,29 @@ func (m *Message) ParseMediaInfo(data string) error { break } m.Contents["refer"] = subMsg - case 62: + case MessageSubTypePat: // 拍一拍 - if msg.App.PatMsg == nil { + if msg.App.PatMsg != nil { + if len(msg.App.PatMsg.Records.Record) != 0 { + m.Sender = msg.App.PatMsg.Records.Record[0].FromUser + m.Content = msg.App.PatMsg.Records.Record[0].Templete + } + } + if msg.App.PatInfo != nil { + m.Content = msg.App.Title + } + case MessageSubTypeChannelLive: + // 视频号直播 + if msg.App.FinderLive == nil { break } - if len(msg.App.PatMsg.Records.Record) == 0 { - break - } - m.Sender = msg.App.PatMsg.Records.Record[0].FromUser - m.Content = msg.App.PatMsg.Records.Record[0].Templete - case 2000: + m.Contents["title"] = msg.App.FinderLive.Desc + case MessageSubTypeMusic: + // 音乐 + m.Contents["title"] = msg.App.Title + m.Contents["desc"] = msg.App.Des + m.Contents["url"] = msg.App.URL + case MessageSubTypePay: // 微信转账 if msg.App.WCPayInfo == nil { break @@ -236,9 +344,9 @@ func (m *Message) PlainText(showChatRoom bool, timeFormat string, host string) s func (m *Message) PlainTextContent() string { switch m.Type { - case 1: + case MessageTypeText: return m.Content - case 3: + case MessageTypeImage: keylist := make([]string, 0) if m.Contents["md5"] != nil { if md5, ok := m.Contents["md5"].(string); ok { @@ -251,14 +359,14 @@ func (m *Message) PlainTextContent() string { } } return fmt.Sprintf("![图片](http://%s/image/%s)", m.Contents["host"], strings.Join(keylist, ",")) - case 34: + case MessageTypeVoice: if voice, ok := m.Contents["voice"]; ok { return fmt.Sprintf("[语音](http://%s/voice/%s)", m.Contents["host"], voice) } return "[语音]" - case 42: + case MessageTypeCard: return "[名片]" - case 43: + case MessageTypeVideo: keylist := make([]string, 0) if m.Contents["md5"] != nil { if md5, ok := m.Contents["md5"].(string); ok { @@ -276,17 +384,34 @@ func (m *Message) PlainTextContent() string { } } return fmt.Sprintf("![视频](http://%s/video/%s)", m.Contents["host"], strings.Join(keylist, ",")) - case 47: + case MessageTypeAnimation: + if m.Contents["cdnurl"] != nil { + if cdnURL, ok := m.Contents["cdnurl"].(string); ok { + return fmt.Sprintf("![动画表情](%s)", cdnURL) + } + } return "[动画表情]" - case 49: + case MessageTypeLocation: + keylist := make([]string, 0) + for _, key := range []string{"label", "cityname", "x", "y"} { + if m.Contents[key] != nil { + if value, ok := m.Contents[key].(string); ok { + keylist = append(keylist, value) + } + } + } + return fmt.Sprintf("[位置|%s]", strings.Join(keylist, "|")) + case MessageTypeShare: switch m.SubType { - case 5: + case MessageSubTypeText: + return fmt.Sprintf("[链接|%s](%s)", m.Contents["title"], m.Contents["desc"]) + case MessageSubTypeLink, MessageSubTypeLink2: return fmt.Sprintf("[链接|%s](%s)", m.Contents["title"], m.Contents["url"]) - case 6: + case MessageSubTypeFile: return fmt.Sprintf("[文件|%s](http://%s/file/%s)", m.Contents["title"], m.Contents["host"], m.Contents["md5"]) - case 8: + case MessageSubTypeGIF: return "[GIF表情]" - case 19: + case MessageSubTypeMergeForward: _recordInfo, ok := m.Contents["recordInfo"] if !ok { return "[合并转发]" @@ -299,19 +424,33 @@ func (m *Message) PlainTextContent() string { if m.Contents["host"] != nil { host = m.Contents["host"].(string) } - return recordInfo.String("", host) - case 33, 36: + return recordInfo.String("合并转发", "", host) + case MessageSubTypeNote: + _recordInfo, ok := m.Contents["recordInfo"] + if !ok { + return "[笔记]" + } + recordInfo, ok := _recordInfo.(*RecordInfo) + if !ok { + return "[笔记]" + } + host := "" + if m.Contents["host"] != nil { + host = m.Contents["host"].(string) + } + return recordInfo.String("笔记", "", host) + case MessageSubTypeMiniProgram, MessageSubTypeMiniProgram2: if m.Contents["title"] == "" { return "[小程序]" } return fmt.Sprintf("[小程序|%s](%s)", m.Contents["title"], m.Contents["url"]) - case 51: + case MessageSubTypeChannel: if m.Contents["title"] == "" { return "[视频号]" } else { return fmt.Sprintf("[视频号|%s](%s)", m.Contents["title"], m.Contents["url"]) } - case 57: + case MessageSubTypeQuote: _refer, ok := m.Contents["refer"] if !ok { if m.Content == "" { @@ -342,24 +481,41 @@ func (m *Message) PlainTextContent() string { } buf.WriteString(m.Content) return buf.String() - case 62: + case MessageSubTypePat: return m.Content - case 63: - return "[视频号]" - case 87: - return "[群公告]" - case 2000: + case MessageSubTypeChannelLive: + if m.Contents["title"] != nil { + return fmt.Sprintf("[视频号直播|%s]", m.Contents["title"]) + } + return "[视频号直播]" + case MessageSubTypeChatRoomNotice: + _recordInfo, ok := m.Contents["recordInfo"] + if !ok { + return "[群公告]" + } + recordInfo, ok := _recordInfo.(*RecordInfo) + if !ok { + return "[群公告]" + } + host := "" + if m.Contents["host"] != nil { + host = m.Contents["host"].(string) + } + return recordInfo.String("群公告", "", host) + case MessageSubTypeMusic: + return fmt.Sprintf("[音乐|%s](%s)", m.Contents["title"], m.Contents["url"]) + case MessageSubTypePay: return m.Content - case 2001: + case MessageSubTypeRedEnvelope: return "[红包]" - case 2003: + case MessageSubTypeRedEnvelopeCover: return "[红包封面]" default: return "[分享]" } - case 50: + case MessageTypeVOIP: return "[语音通话]" - case 10000: + case MessageTypeSystem: return m.Content default: content := m.Content diff --git a/internal/ui/infobar/infobar.go b/internal/ui/infobar/infobar.go index 05aa648..63f1f92 100644 --- a/internal/ui/infobar/infobar.go +++ b/internal/ui/infobar/infobar.go @@ -15,7 +15,7 @@ const ( // InfoBarViewHeight info bar height. const ( - InfoBarViewHeight = 7 + InfoBarViewHeight = 8 accountRow = 0 statusRow = 1 platformRow = 2 @@ -23,6 +23,7 @@ const ( dataUsageRow = 4 workUsageRow = 5 httpServerRow = 6 + autoDecryptRow = 7 // 列索引 labelCol1 = 0 // 第一列标签 @@ -145,10 +146,17 @@ func New() *InfoBar { table.SetCell( httpServerRow, labelCol2, - tview.NewTableCell(fmt.Sprintf(" [%s::]%s", headerColor, "Auto Decrypt:")), + tview.NewTableCell(fmt.Sprintf(" [%s::]%s", headerColor, "Image Key:")), ) table.SetCell(httpServerRow, valueCol2, tview.NewTableCell("")) + table.SetCell( + autoDecryptRow, + labelCol1, + tview.NewTableCell(fmt.Sprintf(" [%s::]%s", headerColor, "Auto Decrypt:")), + ) + table.SetCell(autoDecryptRow, valueCol1, tview.NewTableCell("")) + // infobar infoBar := &InfoBar{ Box: tview.NewBox(), @@ -200,9 +208,13 @@ func (info *InfoBar) UpdateHTTPServer(server string) { info.table.GetCell(httpServerRow, valueCol1).SetText(server) } +func (info *InfoBar) UpdateImageKey(key string) { + info.table.GetCell(httpServerRow, valueCol2).SetText(key) +} + // UpdateAutoDecrypt updates Auto Decrypt value. func (info *InfoBar) UpdateAutoDecrypt(text string) { - info.table.GetCell(httpServerRow, valueCol2).SetText(text) + info.table.GetCell(autoDecryptRow, valueCol1).SetText(text) } // Draw draws this primitive onto the screen.