This commit is contained in:
Shen Junzheng
2025-03-21 21:45:08 +08:00
parent 78cce92ce3
commit 80c7e67106
86 changed files with 7061 additions and 2316 deletions

25
pkg/appver/version.go Normal file
View File

@@ -0,0 +1,25 @@
package appver
type Info struct {
FilePath string `json:"file_path"`
CompanyName string `json:"company_name"`
FileDescription string `json:"file_description"`
Version int `json:"version"`
FullVersion string `json:"full_version"`
LegalCopyright string `json:"legal_copyright"`
ProductName string `json:"product_name"`
ProductVersion string `json:"product_version"`
}
func New(filePath string) (*Info, error) {
i := &Info{
FilePath: filePath,
}
err := i.initialize()
if err != nil {
return nil, err
}
return i, nil
}

View File

@@ -0,0 +1,41 @@
package appver
import (
"os"
"path/filepath"
"strconv"
"strings"
"howett.net/plist"
)
const (
InfoFile = "Info.plist"
)
type Plist struct {
CFBundleShortVersionString string `plist:"CFBundleShortVersionString"`
NSHumanReadableCopyright string `plist:"NSHumanReadableCopyright"`
}
func (i *Info) initialize() error {
parts := strings.Split(i.FilePath, string(filepath.Separator))
file := filepath.Join(append(parts[:len(parts)-2], InfoFile)...)
b, err := os.ReadFile("/" + file)
if err != nil {
return err
}
p := Plist{}
_, err = plist.Unmarshal(b, &p)
if err != nil {
return err
}
i.FullVersion = p.CFBundleShortVersionString
i.Version, _ = strconv.Atoi(strings.Split(i.FullVersion, ".")[0])
i.CompanyName = p.NSHumanReadableCopyright
return nil
}

View File

@@ -1,6 +1,6 @@
//go:build !windows
//go:build !windows && !darwin
package dllver
package appver
func (i *Info) initialize() error {
return nil

View File

@@ -1,4 +1,4 @@
package dllver
package appver
import (
"fmt"
@@ -75,13 +75,13 @@ func (i *Info) initialize() error {
}
// 解析文件版本
i.FileVersion = fmt.Sprintf("%d.%d.%d.%d",
i.FullVersion = fmt.Sprintf("%d.%d.%d.%d",
(fixedFileInfo.FileVersionMS>>16)&0xffff,
(fixedFileInfo.FileVersionMS>>0)&0xffff,
(fixedFileInfo.FileVersionLS>>16)&0xffff,
(fixedFileInfo.FileVersionLS>>0)&0xffff,
)
i.FileMajorVersion = int((fixedFileInfo.FileVersionMS >> 16) & 0xffff)
i.Version = int((fixedFileInfo.FileVersionMS >> 16) & 0xffff)
i.ProductVersion = fmt.Sprintf("%d.%d.%d.%d",
(fixedFileInfo.ProductVersionMS>>16)&0xffff,
@@ -111,7 +111,7 @@ func (i *Info) initialize() error {
stringInfos := map[string]*string{
"CompanyName": &i.CompanyName,
"FileDescription": &i.FileDescription,
"FileVersion": &i.FileVersion,
"FileVersion": &i.FullVersion,
"LegalCopyright": &i.LegalCopyright,
"ProductName": &i.ProductName,
"ProductVersion": &i.ProductVersion,

View File

@@ -1,25 +0,0 @@
package dllver
type Info struct {
FilePath string `json:"file_path"`
CompanyName string `json:"company_name"`
FileDescription string `json:"file_description"`
FileVersion string `json:"file_version"`
FileMajorVersion int `json:"file_major_version"`
LegalCopyright string `json:"legal_copyright"`
ProductName string `json:"product_name"`
ProductVersion string `json:"product_version"`
}
func New(filePath string) (*Info, error) {
i := &Info{
FilePath: filePath,
}
err := i.initialize()
if err != nil {
return nil, err
}
return i, nil
}

View File

@@ -1,142 +0,0 @@
package model
import (
"github.com/sjzar/chatlog/pkg/model/wxproto"
"google.golang.org/protobuf/proto"
)
type ChatRoom struct {
Name string `json:"name"`
Owner string `json:"owner"`
Users []ChatRoomUser `json:"users"`
// Extra From Contact
Remark string `json:"remark"`
NickName string `json:"nickName"`
User2DisplayName map[string]string `json:"-"`
}
type ChatRoomUser struct {
UserName string `json:"userName"`
DisplayName string `json:"displayName"`
}
// CREATE TABLE ChatRoom(
// ChatRoomName TEXT PRIMARY KEY,
// UserNameList TEXT,
// DisplayNameList TEXT,
// ChatRoomFlag int Default 0,
// Owner INTEGER DEFAULT 0,
// IsShowName INTEGER DEFAULT 0,
// SelfDisplayName TEXT,
// Reserved1 INTEGER DEFAULT 0,
// Reserved2 TEXT,
// Reserved3 INTEGER DEFAULT 0,
// Reserved4 TEXT,
// Reserved5 INTEGER DEFAULT 0,
// Reserved6 TEXT,
// RoomData BLOB,
// Reserved7 INTEGER DEFAULT 0,
// Reserved8 TEXT
// )
type ChatRoomV3 struct {
ChatRoomName string `json:"ChatRoomName"`
Reserved2 string `json:"Reserved2"` // Creator
RoomData []byte `json:"RoomData"`
// // 非关键信息,暂时忽略
// UserNameList string `json:"UserNameList"`
// DisplayNameList string `json:"DisplayNameList"`
// ChatRoomFlag int `json:"ChatRoomFlag"`
// Owner int `json:"Owner"`
// IsShowName int `json:"IsShowName"`
// SelfDisplayName string `json:"SelfDisplayName"`
// Reserved1 int `json:"Reserved1"`
// Reserved3 int `json:"Reserved3"`
// Reserved4 string `json:"Reserved4"`
// Reserved5 int `json:"Reserved5"`
// Reserved6 string `json:"Reserved6"`
// Reserved7 int `json:"Reserved7"`
// Reserved8 string `json:"Reserved8"`
}
func (c *ChatRoomV3) Wrap() *ChatRoom {
var users []ChatRoomUser
if len(c.RoomData) != 0 {
users = ParseRoomData(c.RoomData)
}
user2DisplayName := make(map[string]string, len(users))
for _, user := range users {
if user.DisplayName != "" {
user2DisplayName[user.UserName] = user.DisplayName
}
}
return &ChatRoom{
Name: c.ChatRoomName,
Owner: c.Reserved2,
Users: users,
User2DisplayName: user2DisplayName,
}
}
// CREATE TABLE chat_room(
// id INTEGER PRIMARY KEY,
// username TEXT,
// owner TEXT,
// ext_buffer BLOB
// )
type ChatRoomV4 struct {
ID int `json:"id"`
UserName string `json:"username"`
Owner string `json:"owner"`
ExtBuffer []byte `json:"ext_buffer"`
}
func (c *ChatRoomV4) Wrap() *ChatRoom {
var users []ChatRoomUser
if len(c.ExtBuffer) != 0 {
users = ParseRoomData(c.ExtBuffer)
}
return &ChatRoom{
Name: c.UserName,
Owner: c.Owner,
Users: users,
}
}
func ParseRoomData(b []byte) (users []ChatRoomUser) {
var pbMsg wxproto.RoomData
if err := proto.Unmarshal(b, &pbMsg); err != nil {
return
}
if pbMsg.Users == nil {
return
}
users = make([]ChatRoomUser, 0, len(pbMsg.Users))
for _, user := range pbMsg.Users {
u := ChatRoomUser{UserName: user.UserName}
if user.DisplayName != nil {
u.DisplayName = *user.DisplayName
}
users = append(users, u)
}
return users
}
func (c *ChatRoom) DisplayName() string {
switch {
case c.Remark != "":
return c.Remark
case c.NickName != "":
return c.NickName
}
return ""
}

View File

@@ -1,158 +0,0 @@
package model
type Contact struct {
UserName string `json:"userName"`
Alias string `json:"alias"`
Remark string `json:"remark"`
NickName string `json:"nickName"`
IsFriend bool `json:"isFriend"`
}
// CREATE TABLE Contact(
// UserName TEXT PRIMARY KEY ,
// Alias TEXT,
// EncryptUserName TEXT,
// DelFlag INTEGER DEFAULT 0,
// Type INTEGER DEFAULT 0,
// VerifyFlag INTEGER DEFAULT 0,
// Reserved1 INTEGER DEFAULT 0,
// Reserved2 INTEGER DEFAULT 0,
// Reserved3 TEXT,
// Reserved4 TEXT,
// Remark TEXT,
// NickName TEXT,
// LabelIDList TEXT,
// DomainList TEXT,
// ChatRoomType int,
// PYInitial TEXT,
// QuanPin TEXT,
// RemarkPYInitial TEXT,
// RemarkQuanPin TEXT,
// BigHeadImgUrl TEXT,
// SmallHeadImgUrl TEXT,
// HeadImgMd5 TEXT,
// ChatRoomNotify INTEGER DEFAULT 0,
// Reserved5 INTEGER DEFAULT 0,
// Reserved6 TEXT,
// Reserved7 TEXT,
// ExtraBuf BLOB,
// Reserved8 INTEGER DEFAULT 0,
// Reserved9 INTEGER DEFAULT 0,
// Reserved10 TEXT,
// Reserved11 TEXT
// )
type ContactV3 struct {
UserName string `json:"UserName"`
Alias string `json:"Alias"`
Remark string `json:"Remark"`
NickName string `json:"NickName"`
Reserved1 int `json:"Reserved1"` // 1 自己好友或自己加入的群聊; 0 群聊成员(非好友)
// EncryptUserName string `json:"EncryptUserName"`
// DelFlag int `json:"DelFlag"`
// Type int `json:"Type"`
// VerifyFlag int `json:"VerifyFlag"`
// Reserved2 int `json:"Reserved2"`
// Reserved3 string `json:"Reserved3"`
// Reserved4 string `json:"Reserved4"`
// LabelIDList string `json:"LabelIDList"`
// DomainList string `json:"DomainList"`
// ChatRoomType int `json:"ChatRoomType"`
// PYInitial string `json:"PYInitial"`
// QuanPin string `json:"QuanPin"`
// RemarkPYInitial string `json:"RemarkPYInitial"`
// RemarkQuanPin string `json:"RemarkQuanPin"`
// BigHeadImgUrl string `json:"BigHeadImgUrl"`
// SmallHeadImgUrl string `json:"SmallHeadImgUrl"`
// HeadImgMd5 string `json:"HeadImgMd5"`
// ChatRoomNotify int `json:"ChatRoomNotify"`
// Reserved5 int `json:"Reserved5"`
// Reserved6 string `json:"Reserved6"`
// Reserved7 string `json:"Reserved7"`
// ExtraBuf []byte `json:"ExtraBuf"`
// Reserved8 int `json:"Reserved8"`
// Reserved9 int `json:"Reserved9"`
// Reserved10 string `json:"Reserved10"`
// Reserved11 string `json:"Reserved11"`
}
func (c *ContactV3) Wrap() *Contact {
return &Contact{
UserName: c.UserName,
Alias: c.Alias,
Remark: c.Remark,
NickName: c.NickName,
IsFriend: c.Reserved1 == 1,
}
}
// CREATE TABLE contact(
// id INTEGER PRIMARY KEY,
// username TEXT,
// local_type INTEGER,
// alias TEXT,
// encrypt_username TEXT,
// flag INTEGER,
// delete_flag INTEGER,
// verify_flag INTEGER,
// remark TEXT,
// remark_quan_pin TEXT,
// remark_pin_yin_initial TEXT,
// nick_name TEXT,
// pin_yin_initial TEXT,
// quan_pin TEXT,
// big_head_url TEXT,
// small_head_url TEXT,
// head_img_md5 TEXT,
// chat_room_notify INTEGER,
// is_in_chat_room INTEGER,
// description TEXT,
// extra_buffer BLOB,
// chat_room_type INTEGER
// )
type ContactV4 struct {
UserName string `json:"username"`
Alias string `json:"alias"`
Remark string `json:"remark"`
NickName string `json:"nick_name"`
LocalType int `json:"local_type"` // 2 群聊; 3 群聊成员(非好友); 5,6 企业微信;
// ID int `json:"id"`
// EncryptUserName string `json:"encrypt_username"`
// Flag int `json:"flag"`
// DeleteFlag int `json:"delete_flag"`
// VerifyFlag int `json:"verify_flag"`
// RemarkQuanPin string `json:"remark_quan_pin"`
// RemarkPinYinInitial string `json:"remark_pin_yin_initial"`
// PinYinInitial string `json:"pin_yin_initial"`
// QuanPin string `json:"quan_pin"`
// BigHeadUrl string `json:"big_head_url"`
// SmallHeadUrl string `json:"small_head_url"`
// HeadImgMd5 string `json:"head_img_md5"`
// ChatRoomNotify int `json:"chat_room_notify"`
// IsInChatRoom int `json:"is_in_chat_room"`
// Description string `json:"description"`
// ExtraBuffer []byte `json:"extra_buffer"`
// ChatRoomType int `json:"chat_room_type"`
}
func (c *ContactV4) Wrap() *Contact {
return &Contact{
UserName: c.UserName,
Alias: c.Alias,
Remark: c.Remark,
NickName: c.NickName,
IsFriend: c.LocalType != 3,
}
}
func (c *Contact) DisplayName() string {
switch {
case c.Remark != "":
return c.Remark
case c.NickName != "":
return c.NickName
}
return ""
}

View File

@@ -1,301 +0,0 @@
package model
import (
"fmt"
"strings"
"time"
"github.com/sjzar/chatlog/pkg/model/wxproto"
"github.com/sjzar/chatlog/pkg/util"
"google.golang.org/protobuf/proto"
)
const (
// Source
WeChatV3 = "wechatv3"
WeChatV4 = "wechatv4"
)
type Message struct {
Sequence int64 `json:"sequence"` // 消息序号10位时间戳 + 3位序号
CreateTime time.Time `json:"createTime"` // 消息创建时间10位时间戳
TalkerID int `json:"talkerID"` // 聊天对象Name2ID 表序号,索引值
Talker string `json:"talker"` // 聊天对象,微信 ID or 群 ID
IsSender int `json:"isSender"` // 是否为发送消息0 接收消息1 发送消息
Type int `json:"type"` // 消息类型
SubType int `json:"subType"` // 消息子类型
Content string `json:"content"` // 消息内容,文字聊天内容 或 XML
CompressContent []byte `json:"compressContent"` // 非文字聊天内容,如图片、语音、视频等
IsChatRoom bool `json:"isChatRoom"` // 是否为群聊消息
ChatRoomSender string `json:"chatRoomSender"` // 群聊消息发送人
// Fill Info
// 从联系人等信息中填充
DisplayName string `json:"-"` // 显示名称
CharRoomName string `json:"-"` // 群聊名称
Version string `json:"-"` // 消息版本,内部判断
}
// CREATE TABLE MSG (
// localId INTEGER PRIMARY KEY AUTOINCREMENT,
// TalkerId INT DEFAULT 0,
// MsgSvrID INT,
// Type INT,
// SubType INT,
// IsSender INT,
// CreateTime INT,
// Sequence INT DEFAULT 0,
// StatusEx INT DEFAULT 0,
// FlagEx INT,
// Status INT,
// MsgServerSeq INT,
// MsgSequence INT,
// StrTalker TEXT,
// StrContent TEXT,
// DisplayContent TEXT,
// Reserved0 INT DEFAULT 0,
// Reserved1 INT DEFAULT 0,
// Reserved2 INT DEFAULT 0,
// Reserved3 INT DEFAULT 0,
// Reserved4 TEXT,
// Reserved5 TEXT,
// Reserved6 TEXT,
// CompressContent BLOB,
// BytesExtra BLOB,
// BytesTrans BLOB
// )
type MessageV3 struct {
Sequence int64 `json:"Sequence"` // 消息序号10位时间戳 + 3位序号
CreateTime int64 `json:"CreateTime"` // 消息创建时间10位时间戳
TalkerID int `json:"TalkerId"` // 聊天对象Name2ID 表序号,索引值
StrTalker string `json:"StrTalker"` // 聊天对象,微信 ID or 群 ID
IsSender int `json:"IsSender"` // 是否为发送消息0 接收消息1 发送消息
Type int `json:"Type"` // 消息类型
SubType int `json:"SubType"` // 消息子类型
StrContent string `json:"StrContent"` // 消息内容,文字聊天内容 或 XML
CompressContent []byte `json:"CompressContent"` // 非文字聊天内容,如图片、语音、视频等
BytesExtra []byte `json:"BytesExtra"` // protobuf 额外数据,记录群聊发送人等信息
// 非关键信息,后续有需要再加入
// LocalID int64 `json:"localId"`
// MsgSvrID int64 `json:"MsgSvrID"`
// StatusEx int `json:"StatusEx"`
// FlagEx int `json:"FlagEx"`
// Status int `json:"Status"`
// MsgServerSeq int64 `json:"MsgServerSeq"`
// MsgSequence int64 `json:"MsgSequence"`
// DisplayContent string `json:"DisplayContent"`
// Reserved0 int `json:"Reserved0"`
// Reserved1 int `json:"Reserved1"`
// Reserved2 int `json:"Reserved2"`
// Reserved3 int `json:"Reserved3"`
// Reserved4 string `json:"Reserved4"`
// Reserved5 string `json:"Reserved5"`
// Reserved6 string `json:"Reserved6"`
// BytesTrans []byte `json:"BytesTrans"`
}
func (m *MessageV3) Wrap() *Message {
isChatRoom := strings.HasSuffix(m.StrTalker, "@chatroom")
var chatRoomSender string
if len(m.BytesExtra) != 0 && isChatRoom {
chatRoomSender = ParseBytesExtra(m.BytesExtra)
}
return &Message{
Sequence: m.Sequence,
CreateTime: time.Unix(m.CreateTime, 0),
TalkerID: m.TalkerID,
Talker: m.StrTalker,
IsSender: m.IsSender,
Type: m.Type,
SubType: m.SubType,
Content: m.StrContent,
CompressContent: m.CompressContent,
IsChatRoom: isChatRoom,
ChatRoomSender: chatRoomSender,
Version: WeChatV3,
}
}
// CREATE TABLE Msg_xxxxxxxxxxxx(
// local_id INTEGER PRIMARY KEY AUTOINCREMENT,
// server_id INTEGER,
// local_type INTEGER,
// sort_seq INTEGER,
// real_sender_id INTEGER,
// create_time INTEGER,
// status INTEGER,
// upload_status INTEGER,
// download_status INTEGER,
// server_seq INTEGER,
// origin_source INTEGER,
// source TEXT,
// message_content TEXT,
// compress_content TEXT,
// packed_info_data BLOB,
// WCDB_CT_message_content INTEGER DEFAULT NULL,
// WCDB_CT_source INTEGER DEFAULT NULL
// )
type MessageV4 struct {
SortSeq int64 `json:"sort_seq"` // 消息序号10位时间戳 + 3位序号
LocalType int `json:"local_type"` // 消息类型
RealSenderID int `json:"real_sender_id"` // 发送人 ID对应 Name2Id 表序号
CreateTime int64 `json:"create_time"` // 消息创建时间10位时间戳
MessageContent []byte `json:"message_content"` // 消息内容,文字聊天内容 或 zstd 压缩内容
PackedInfoData []byte `json:"packed_info_data"` // 额外数据,类似 proto格式与 v3 有差异
Status int `json:"status"` // 消息状态2 是已发送4 是已接收,可以用于判断 IsSender猜测
// 非关键信息,后续有需要再加入
// LocalID int `json:"local_id"`
// ServerID int64 `json:"server_id"`
// UploadStatus int `json:"upload_status"`
// DownloadStatus int `json:"download_status"`
// ServerSeq int `json:"server_seq"`
// OriginSource int `json:"origin_source"`
// Source string `json:"source"`
// CompressContent string `json:"compress_content"`
}
func (m *MessageV4) Wrap(id2Name map[int]string, isChatRoom bool) *Message {
_m := &Message{
Sequence: m.SortSeq,
CreateTime: time.Unix(m.CreateTime, 0),
TalkerID: m.RealSenderID, // 依赖 Name2Id 表进行转换为 StrTalker
CompressContent: m.PackedInfoData,
Type: m.LocalType,
Version: WeChatV4,
}
if name, ok := id2Name[m.RealSenderID]; ok {
_m.Talker = name
}
if m.Status == 2 {
_m.IsSender = 1
}
if util.IsNormalString(m.MessageContent) {
_m.Content = string(m.MessageContent)
} else {
_m.CompressContent = m.MessageContent
}
if isChatRoom {
_m.IsChatRoom = true
split := strings.Split(_m.Content, "\n")
if len(split) > 1 {
_m.Content = split[1]
_m.ChatRoomSender = strings.TrimSuffix(split[0], ":")
}
}
return _m
}
// ParseBytesExtra 解析额外数据
// 按需解析
func ParseBytesExtra(b []byte) (chatRoomSender string) {
var pbMsg wxproto.BytesExtra
if err := proto.Unmarshal(b, &pbMsg); err != nil {
return
}
if pbMsg.Items == nil {
return
}
for _, item := range pbMsg.Items {
if item.Type == 1 {
return item.Value
}
}
return
}
func (m *Message) PlainText(showChatRoom bool) string {
buf := strings.Builder{}
talker := m.Talker
if m.IsSender == 1 {
talker = "我"
} else if m.IsChatRoom {
talker = m.ChatRoomSender
}
if m.DisplayName != "" {
buf.WriteString(m.DisplayName)
buf.WriteString("(")
buf.WriteString(talker)
buf.WriteString(")")
} else {
buf.WriteString(talker)
}
buf.WriteString(" ")
if m.IsChatRoom && showChatRoom {
buf.WriteString("[")
if m.CharRoomName != "" {
buf.WriteString(m.CharRoomName)
buf.WriteString("(")
buf.WriteString(m.Talker)
buf.WriteString(")")
} else {
buf.WriteString(m.Talker)
}
buf.WriteString("] ")
}
buf.WriteString(m.CreateTime.Format("2006-01-02 15:04:05"))
buf.WriteString("\n")
switch m.Type {
case 1:
buf.WriteString(m.Content)
case 3:
buf.WriteString("[图片]")
case 34:
buf.WriteString("[语音]")
case 43:
buf.WriteString("[视频]")
case 47:
buf.WriteString("[动画表情]")
case 49:
switch m.SubType {
case 6:
buf.WriteString("[文件]")
case 8:
buf.WriteString("[GIF表情]")
case 19:
buf.WriteString("[合并转发]")
case 33, 36:
buf.WriteString("[小程序]")
case 57:
buf.WriteString("[引用]")
case 63:
buf.WriteString("[视频号]")
case 87:
buf.WriteString("[群公告]")
case 2000:
buf.WriteString("[转账]")
case 2003:
buf.WriteString("[红包封面]")
default:
buf.WriteString("[分享]")
}
case 50:
buf.WriteString("[语音通话]")
case 10000:
buf.WriteString("[系统消息]")
default:
buf.WriteString(fmt.Sprintf("Type: %d Content: %s", m.Type, m.Content))
}
buf.WriteString("\n")
return buf.String()
}

View File

@@ -1,133 +0,0 @@
package model
import (
"strings"
"time"
)
type Session struct {
UserName string `json:"userName"`
NOrder int `json:"nOrder"`
NickName string `json:"nickName"`
Content string `json:"content"`
NTime time.Time `json:"nTime"`
}
// CREATE TABLE Session(
// strUsrName TEXT PRIMARY KEY,
// nOrder INT DEFAULT 0,
// nUnReadCount INTEGER DEFAULT 0,
// parentRef TEXT,
// Reserved0 INTEGER DEFAULT 0,
// Reserved1 TEXT,
// strNickName TEXT,
// nStatus INTEGER,
// nIsSend INTEGER,
// strContent TEXT,
// nMsgType INTEGER,
// nMsgLocalID INTEGER,
// nMsgStatus INTEGER,
// nTime INTEGER,
// editContent TEXT,
// othersAtMe INT,
// Reserved2 INTEGER DEFAULT 0,
// Reserved3 TEXT,
// Reserved4 INTEGER DEFAULT 0,
// Reserved5 TEXT,
// bytesXml BLOB
// )
type SessionV3 struct {
StrUsrName string `json:"strUsrName"`
NOrder int `json:"nOrder"`
StrNickName string `json:"strNickName"`
StrContent string `json:"strContent"`
NTime int64 `json:"nTime"`
// NUnReadCount int `json:"nUnReadCount"`
// ParentRef string `json:"parentRef"`
// Reserved0 int `json:"Reserved0"`
// Reserved1 string `json:"Reserved1"`
// NStatus int `json:"nStatus"`
// NIsSend int `json:"nIsSend"`
// NMsgType int `json:"nMsgType"`
// NMsgLocalID int `json:"nMsgLocalID"`
// NMsgStatus int `json:"nMsgStatus"`
// EditContent string `json:"editContent"`
// OthersAtMe int `json:"othersAtMe"`
// Reserved2 int `json:"Reserved2"`
// Reserved3 string `json:"Reserved3"`
// Reserved4 int `json:"Reserved4"`
// Reserved5 string `json:"Reserved5"`
// BytesXml string `json:"bytesXml"`
}
func (s *SessionV3) Wrap() *Session {
return &Session{
UserName: s.StrUsrName,
NOrder: s.NOrder,
NickName: s.StrNickName,
Content: s.StrContent,
NTime: time.Unix(int64(s.NTime), 0),
}
}
// 注意v4 session 是独立数据库文件
// CREATE TABLE SessionTable(
// username TEXT PRIMARY KEY,
// type INTEGER,
// unread_count INTEGER,
// unread_first_msg_srv_id INTEGER,
// is_hidden INTEGER,
// summary TEXT,
// draft TEXT,
// status INTEGER,
// last_timestamp INTEGER,
// sort_timestamp INTEGER,
// last_clear_unread_timestamp INTEGER,
// last_msg_locald_id INTEGER,
// last_msg_type INTEGER,
// last_msg_sub_type INTEGER,
// last_msg_sender TEXT,
// last_sender_display_name TEXT,
// last_msg_ext_type INTEGER
// )
type SessionV4 struct {
Username string `json:"username"`
Summary string `json:"summary"`
LastTimestamp int `json:"last_timestamp"`
LastMsgSender string `json:"last_msg_sender"`
LastSenderDisplayName string `json:"last_sender_display_name"`
// Type int `json:"type"`
// UnreadCount int `json:"unread_count"`
// UnreadFirstMsgSrvID int `json:"unread_first_msg_srv_id"`
// IsHidden int `json:"is_hidden"`
// Draft string `json:"draft"`
// Status int `json:"status"`
// SortTimestamp int `json:"sort_timestamp"`
// LastClearUnreadTimestamp int `json:"last_clear_unread_timestamp"`
// LastMsgLocaldID int `json:"last_msg_locald_id"`
// LastMsgType int `json:"last_msg_type"`
// LastMsgSubType int `json:"last_msg_sub_type"`
// LastMsgExtType int `json:"last_msg_ext_type"`
}
func (s *Session) PlainText(limit int) string {
buf := strings.Builder{}
buf.WriteString(s.NickName)
buf.WriteString("(")
buf.WriteString(s.UserName)
buf.WriteString(") ")
buf.WriteString(s.NTime.Format("2006-01-02 15:04:05"))
buf.WriteString("\n")
if limit > 0 {
if len(s.Content) > limit {
buf.WriteString(s.Content[:limit])
buf.WriteString(" <...>")
} else {
buf.WriteString(s.Content)
}
}
buf.WriteString("\n")
return buf.String()
}

View File

@@ -1,254 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc v5.29.3
// source: bytesextra.proto
package wxproto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type BytesExtraHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
Field1 int32 `protobuf:"varint,1,opt,name=field1,proto3" json:"field1,omitempty"`
Field2 int32 `protobuf:"varint,2,opt,name=field2,proto3" json:"field2,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BytesExtraHeader) Reset() {
*x = BytesExtraHeader{}
mi := &file_bytesextra_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BytesExtraHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BytesExtraHeader) ProtoMessage() {}
func (x *BytesExtraHeader) ProtoReflect() protoreflect.Message {
mi := &file_bytesextra_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BytesExtraHeader.ProtoReflect.Descriptor instead.
func (*BytesExtraHeader) Descriptor() ([]byte, []int) {
return file_bytesextra_proto_rawDescGZIP(), []int{0}
}
func (x *BytesExtraHeader) GetField1() int32 {
if x != nil {
return x.Field1
}
return 0
}
func (x *BytesExtraHeader) GetField2() int32 {
if x != nil {
return x.Field2
}
return 0
}
type BytesExtraItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BytesExtraItem) Reset() {
*x = BytesExtraItem{}
mi := &file_bytesextra_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BytesExtraItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BytesExtraItem) ProtoMessage() {}
func (x *BytesExtraItem) ProtoReflect() protoreflect.Message {
mi := &file_bytesextra_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BytesExtraItem.ProtoReflect.Descriptor instead.
func (*BytesExtraItem) Descriptor() ([]byte, []int) {
return file_bytesextra_proto_rawDescGZIP(), []int{1}
}
func (x *BytesExtraItem) GetType() int32 {
if x != nil {
return x.Type
}
return 0
}
func (x *BytesExtraItem) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type BytesExtra struct {
state protoimpl.MessageState `protogen:"open.v1"`
Header *BytesExtraHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
Items []*BytesExtraItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BytesExtra) Reset() {
*x = BytesExtra{}
mi := &file_bytesextra_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BytesExtra) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BytesExtra) ProtoMessage() {}
func (x *BytesExtra) ProtoReflect() protoreflect.Message {
mi := &file_bytesextra_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BytesExtra.ProtoReflect.Descriptor instead.
func (*BytesExtra) Descriptor() ([]byte, []int) {
return file_bytesextra_proto_rawDescGZIP(), []int{2}
}
func (x *BytesExtra) GetHeader() *BytesExtraHeader {
if x != nil {
return x.Header
}
return nil
}
func (x *BytesExtra) GetItems() []*BytesExtraItem {
if x != nil {
return x.Items
}
return nil
}
var File_bytesextra_proto protoreflect.FileDescriptor
var file_bytesextra_proto_rawDesc = string([]byte{
0x0a, 0x10, 0x62, 0x79, 0x74, 0x65, 0x73, 0x65, 0x78, 0x74, 0x72, 0x61, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x0c, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x22, 0x42, 0x0a, 0x10, 0x42, 0x79, 0x74, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x48, 0x65,
0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x18, 0x01,
0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x12, 0x16, 0x0a, 0x06,
0x66, 0x69, 0x65, 0x6c, 0x64, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x66, 0x69,
0x65, 0x6c, 0x64, 0x32, 0x22, 0x3a, 0x0a, 0x0e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x45, 0x78, 0x74,
0x72, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x22, 0x78, 0x0a, 0x0a, 0x42, 0x79, 0x74, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x36,
0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x79,
0x74, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x49,
0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x3b,
0x77, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
file_bytesextra_proto_rawDescOnce sync.Once
file_bytesextra_proto_rawDescData []byte
)
func file_bytesextra_proto_rawDescGZIP() []byte {
file_bytesextra_proto_rawDescOnce.Do(func() {
file_bytesextra_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_bytesextra_proto_rawDesc), len(file_bytesextra_proto_rawDesc)))
})
return file_bytesextra_proto_rawDescData
}
var file_bytesextra_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_bytesextra_proto_goTypes = []any{
(*BytesExtraHeader)(nil), // 0: app.protobuf.BytesExtraHeader
(*BytesExtraItem)(nil), // 1: app.protobuf.BytesExtraItem
(*BytesExtra)(nil), // 2: app.protobuf.BytesExtra
}
var file_bytesextra_proto_depIdxs = []int32{
0, // 0: app.protobuf.BytesExtra.header:type_name -> app.protobuf.BytesExtraHeader
1, // 1: app.protobuf.BytesExtra.items:type_name -> app.protobuf.BytesExtraItem
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_bytesextra_proto_init() }
func file_bytesextra_proto_init() {
if File_bytesextra_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_bytesextra_proto_rawDesc), len(file_bytesextra_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_bytesextra_proto_goTypes,
DependencyIndexes: file_bytesextra_proto_depIdxs,
MessageInfos: file_bytesextra_proto_msgTypes,
}.Build()
File_bytesextra_proto = out.File
file_bytesextra_proto_goTypes = nil
file_bytesextra_proto_depIdxs = nil
}

View File

@@ -1,18 +0,0 @@
syntax = "proto3";
package app.protobuf;
option go_package=".;wxproto";
message BytesExtraHeader {
int32 field1 = 1;
int32 field2 = 2;
}
message BytesExtraItem {
int32 type = 1;
string value = 2;
}
message BytesExtra {
BytesExtraHeader header = 1;
repeated BytesExtraItem items = 3;
}

View File

@@ -1,222 +0,0 @@
// v3 & v4 通用,可能会有部分字段差异
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc v5.29.3
// source: roomdata.proto
package wxproto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RoomData struct {
state protoimpl.MessageState `protogen:"open.v1"`
Users []*RoomDataUser `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
RoomCap *int32 `protobuf:"varint,5,opt,name=roomCap,proto3,oneof" json:"roomCap,omitempty"` // 只在第一份数据中出现值为500
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RoomData) Reset() {
*x = RoomData{}
mi := &file_roomdata_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RoomData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RoomData) ProtoMessage() {}
func (x *RoomData) ProtoReflect() protoreflect.Message {
mi := &file_roomdata_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RoomData.ProtoReflect.Descriptor instead.
func (*RoomData) Descriptor() ([]byte, []int) {
return file_roomdata_proto_rawDescGZIP(), []int{0}
}
func (x *RoomData) GetUsers() []*RoomDataUser {
if x != nil {
return x.Users
}
return nil
}
func (x *RoomData) GetRoomCap() int32 {
if x != nil && x.RoomCap != nil {
return *x.RoomCap
}
return 0
}
type RoomDataUser struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserName string `protobuf:"bytes,1,opt,name=userName,proto3" json:"userName,omitempty"` // 用户ID或名称
DisplayName *string `protobuf:"bytes,2,opt,name=displayName,proto3,oneof" json:"displayName,omitempty"` // 显示名称可能是UTF-8编码的中文部分记录可能为空
Status int32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 状态码值范围0-9
Inviter *string `protobuf:"bytes,4,opt,name=inviter,proto3,oneof" json:"inviter,omitempty"` // 邀请人
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RoomDataUser) Reset() {
*x = RoomDataUser{}
mi := &file_roomdata_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RoomDataUser) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RoomDataUser) ProtoMessage() {}
func (x *RoomDataUser) ProtoReflect() protoreflect.Message {
mi := &file_roomdata_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RoomDataUser.ProtoReflect.Descriptor instead.
func (*RoomDataUser) Descriptor() ([]byte, []int) {
return file_roomdata_proto_rawDescGZIP(), []int{1}
}
func (x *RoomDataUser) GetUserName() string {
if x != nil {
return x.UserName
}
return ""
}
func (x *RoomDataUser) GetDisplayName() string {
if x != nil && x.DisplayName != nil {
return *x.DisplayName
}
return ""
}
func (x *RoomDataUser) GetStatus() int32 {
if x != nil {
return x.Status
}
return 0
}
func (x *RoomDataUser) GetInviter() string {
if x != nil && x.Inviter != nil {
return *x.Inviter
}
return ""
}
var File_roomdata_proto protoreflect.FileDescriptor
var file_roomdata_proto_rawDesc = string([]byte{
0x0a, 0x0e, 0x72, 0x6f, 0x6f, 0x6d, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x0c, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x67,
0x0a, 0x08, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73,
0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x61, 0x74,
0x61, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x07,
0x72, 0x6f, 0x6f, 0x6d, 0x43, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52,
0x07, 0x72, 0x6f, 0x6f, 0x6d, 0x43, 0x61, 0x70, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x72, 0x6f, 0x6f, 0x6d, 0x43, 0x61, 0x70, 0x22, 0xa4, 0x01, 0x0a, 0x0c, 0x52, 0x6f, 0x6f, 0x6d,
0x44, 0x61, 0x74, 0x61, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72,
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e,
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x69, 0x73,
0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x88,
0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61,
0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x72, 0x42, 0x0b,
0x5a, 0x09, 0x2e, 0x3b, 0x77, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
})
var (
file_roomdata_proto_rawDescOnce sync.Once
file_roomdata_proto_rawDescData []byte
)
func file_roomdata_proto_rawDescGZIP() []byte {
file_roomdata_proto_rawDescOnce.Do(func() {
file_roomdata_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_roomdata_proto_rawDesc), len(file_roomdata_proto_rawDesc)))
})
return file_roomdata_proto_rawDescData
}
var file_roomdata_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_roomdata_proto_goTypes = []any{
(*RoomData)(nil), // 0: app.protobuf.RoomData
(*RoomDataUser)(nil), // 1: app.protobuf.RoomDataUser
}
var file_roomdata_proto_depIdxs = []int32{
1, // 0: app.protobuf.RoomData.users:type_name -> app.protobuf.RoomDataUser
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_roomdata_proto_init() }
func file_roomdata_proto_init() {
if File_roomdata_proto != nil {
return
}
file_roomdata_proto_msgTypes[0].OneofWrappers = []any{}
file_roomdata_proto_msgTypes[1].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_roomdata_proto_rawDesc), len(file_roomdata_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_roomdata_proto_goTypes,
DependencyIndexes: file_roomdata_proto_depIdxs,
MessageInfos: file_roomdata_proto_msgTypes,
}.Build()
File_roomdata_proto = out.File
file_roomdata_proto_goTypes = nil
file_roomdata_proto_depIdxs = nil
}

View File

@@ -1,16 +0,0 @@
// v3 & v4 通用,可能会有部分字段差异
syntax = "proto3";
package app.protobuf;
option go_package=".;wxproto";
message RoomData {
repeated RoomDataUser users = 1;
optional int32 roomCap = 5; // 只在第一份数据中出现值为500
}
message RoomDataUser {
string userName = 1; // 用户ID或名称
optional string displayName = 2; // 显示名称可能是UTF-8编码的中文部分记录可能为空
int32 status = 3; // 状态码值范围0-9
optional string inviter = 4; // 邀请人
}

11
pkg/util/zstd/zstd.go Normal file
View File

@@ -0,0 +1,11 @@
package zstd
import (
"github.com/klauspost/compress/zstd"
)
var decoder, _ = zstd.NewReader(nil, zstd.WithDecoderConcurrency(0))
func Decompress(src []byte) ([]byte, error) {
return decoder.DecodeAll(src, nil)
}