diff --git a/binary/jce/writer.go b/binary/jce/writer.go index d5d803c8..3153fbdb 100644 --- a/binary/jce/writer.go +++ b/binary/jce/writer.go @@ -34,7 +34,7 @@ func (w *JceWriter) WriteByte(b, tag byte) *JceWriter { } func (w *JceWriter) WriteBool(b bool, tag byte) { - var by byte = 0 + var by byte if b { by = 1 } diff --git a/binary/protobuf.go b/binary/protobuf.go index 30573eac..51c5b278 100644 --- a/binary/protobuf.go +++ b/binary/protobuf.go @@ -14,6 +14,8 @@ type encoder struct { func (msg DynamicProtoMessage) Encode() []byte { en := &encoder{} + + //nolint:staticcheck for id, value := range msg { key := id << 3 switch v := value.(type) { diff --git a/binary/reader.go b/binary/reader.go index 0ddd86af..05bb2d77 100644 --- a/binary/reader.go +++ b/binary/reader.go @@ -97,11 +97,12 @@ func (r *Reader) ReadTlvMap(tagSize int) (m TlvMap) { return m } var k uint16 - if tagSize == 1 { + switch tagSize { + case 1: k = uint16(r.ReadByte()) - } else if tagSize == 2 { + case 2: k = r.ReadUInt16() - } else if tagSize == 4 { + case 4: k = uint16(r.ReadInt32()) } if k == 255 { diff --git a/binary/tea.go b/binary/tea.go index 455933f2..1966935d 100644 --- a/binary/tea.go +++ b/binary/tea.go @@ -29,7 +29,7 @@ func (t TEA) Encrypt(src []byte) (dst []byte) { block := binary.BigEndian.Uint64(dst[i:]) holder = block ^ iv1 iv1 = t.encode(holder) - iv1 = iv1 ^ iv2 + iv1 ^= iv2 iv2 = holder binary.BigEndian.PutUint64(dst[i:], iv1) } diff --git a/client/builders.go b/client/builders.go index c6a543f8..69a0d1c5 100644 --- a/client/builders.go +++ b/client/builders.go @@ -3,7 +3,6 @@ package client import ( "crypto/md5" "encoding/hex" - "fmt" "math/rand" "time" @@ -14,7 +13,6 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/profilecard" - "github.com/Mrs4s/MiraiGo/client/pb/qweb" "github.com/Mrs4s/MiraiGo/client/pb/structmsg" "github.com/Mrs4s/MiraiGo/internal/crypto" "github.com/Mrs4s/MiraiGo/internal/packets" @@ -1080,6 +1078,7 @@ func (c *QQClient) buildQuitGroupPacket(groupCode int64) (uint16, []byte) { return seq, packet } +/* this function is unused // LightAppSvc.mini_app_info.GetAppInfoById func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) { seq := c.nextSeq() @@ -1099,6 +1098,7 @@ func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) { packet := packets.BuildUniPacket(c.Uin, seq, "LightAppSvc.mini_app_info.GetAppInfoById", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return seq, packet } +*/ func (c *QQClient) buildWordSegmentationPacket(data []byte) (uint16, []byte) { seq := c.nextSeq() diff --git a/client/client.go b/client/client.go index 4f5f358b..40e92d71 100644 --- a/client/client.go +++ b/client/client.go @@ -95,11 +95,9 @@ type QQClient struct { fileStorageInfo *jce.FileStoragePushFSSvcList // message state - lastMessageSeq int32 msgSvcCache *utils.Cache lastC2CMsgTime int64 transCache *utils.Cache - lastLostMsg string groupSysMsgCache *GroupSystemMessages groupMsgBuilders sync.Map onlinePushCache *utils.Cache @@ -107,7 +105,6 @@ type QQClient struct { groupSeq int32 friendSeq int32 heartbeatEnabled bool - groupDataTransSeq int32 highwayApplyUpSeq int32 eventHandlers *eventHandlers @@ -150,6 +147,13 @@ type handlerInfo struct { params requestParams } +func (h *handlerInfo) getParams() requestParams { + if h == nil { + return nil + } + return h.params +} + var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interface{}, error){ "wtlogin.login": decodeLoginResponse, "wtlogin.exchange_emp": decodeExchangeEmpResponse, @@ -174,7 +178,6 @@ var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interfac "OidbSvc.0xd79": decodeWordSegmentation, "OidbSvc.0x990": decodeTranslateResponse, "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse, - "LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse, } func init() { @@ -547,10 +550,8 @@ func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage { if m == nil { return nil } - var ( - item *msg.PbMultiMsgItem - ret = &message.ForwardMessage{Nodes: []*message.ForwardNode{}} - ) + var item *msg.PbMultiMsgItem + ret := &message.ForwardMessage{Nodes: []*message.ForwardNode{}} for _, iter := range m.Items { if iter.GetFileName() == m.FileName { item = iter @@ -560,16 +561,15 @@ func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage { return nil } for _, m := range item.GetBuffer().GetMsg() { + name := m.Head.GetFromNick() + if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil { + name = m.Head.GroupInfo.GetGroupCard() + } ret.Nodes = append(ret.Nodes, &message.ForwardNode{ - SenderId: m.Head.GetFromUin(), - SenderName: func() string { - if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil { - return m.Head.GroupInfo.GetGroupCard() - } - return m.Head.GetFromNick() - }(), - Time: m.Head.GetMsgTime(), - Message: message.ParseMessageElems(m.Body.RichText.Elems), + SenderId: m.Head.GetFromUin(), + SenderName: name, + Time: m.Head.GetMsgTime(), + Message: message.ParseMessageElems(m.Body.RichText.Elems), }) } return ret @@ -866,10 +866,6 @@ func (c *QQClient) nextQWebSeq() int64 { return atomic.AddInt64(&c.qwebSeq, 1) } -func (c *QQClient) nextGroupDataTransSeq() int32 { - return atomic.AddInt32(&c.groupDataTransSeq, 2) -} - func (c *QQClient) nextHighwayApplySeq() int32 { return atomic.AddInt32(&c.highwayApplyUpSeq, 2) } diff --git a/client/decoders.go b/client/decoders.go index 91127096..731fff34 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -20,7 +20,6 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/profilecard" - "github.com/Mrs4s/MiraiGo/client/pb/qweb" "github.com/Mrs4s/MiraiGo/client/pb/structmsg" "github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/utils" @@ -204,8 +203,7 @@ func decodeExchangeEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byt reader.ReadUInt16() m := reader.ReadTlvMap(2) if t != 0 { - c.Error("exchange_emp error: %v", t) - return nil, errors.New("exchange_emp failed") + return nil, errors.Errorf("exchange_emp failed: %v", t) } if cmd == 15 { c.decodeT119R(m[0x119]) @@ -795,6 +793,7 @@ func decodeWordSegmentation(_ *QQClient, _ *incomingPacketInfo, payload []byte) return nil, errors.New("no word received") } +/* unused // LightAppSvc.mini_app_info.GetAppInfoById func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { pkg := qweb.QWebRsp{} @@ -810,6 +809,7 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) ( } return rsp.AppInfo, nil } +*/ func ignoreDecoder(_ *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) { return nil, nil diff --git a/client/global.go b/client/global.go index 73a1cd7d..882ae371 100644 --- a/client/global.go +++ b/client/global.go @@ -157,10 +157,7 @@ var SystemDeviceInfo = &DeviceInfo{ }, } -var ( - EmptyBytes = []byte{} - NumberRange = "0123456789" -) +var EmptyBytes = make([]byte, 0) func init() { r := make([]byte, 16) @@ -174,8 +171,9 @@ func init() { func GenRandomDevice() { r := make([]byte, 16) rand.Read(r) - SystemDeviceInfo.Display = []byte("MIRAI." + utils.RandomStringRange(6, NumberRange) + ".001") - SystemDeviceInfo.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, NumberRange) + ":user/release-keys") + const numberRange = "0123456789" + SystemDeviceInfo.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001") + SystemDeviceInfo.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys") SystemDeviceInfo.BootId = binary.GenUUID(r) SystemDeviceInfo.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") rand.Read(r) @@ -498,8 +496,7 @@ func qualityTest(addr string) (int64, error) { return 0, errors.Wrap(err, "failed to connect to server during quality test") } _ = conn.Close() - end := time.Now() - return end.Sub(start).Milliseconds(), nil + return time.Since(start).Milliseconds(), nil } func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage { @@ -582,10 +579,12 @@ func (b *groupMessageBuilder) build() *msg.Message { return base } -func packUniRequestData(data []byte) (r []byte) { - r = append([]byte{0x0A}, data...) +func packUniRequestData(data []byte) []byte { + r := make([]byte, 0, len(data)+2) + r = append(r, 0x0a) + r = append(r, data...) r = append(r, 0x0B) - return + return r } func genForwardTemplate(resID, preview, title, brief, source, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement { diff --git a/client/guild_msg.go b/client/guild_msg.go index b2fc2067..d21b36ad 100644 --- a/client/guild_msg.go +++ b/client/guild_msg.go @@ -22,20 +22,18 @@ import ( "github.com/Mrs4s/MiraiGo/utils" ) -type ( - guildImageUploadResponse struct { - UploadKey []byte - UploadIp []uint32 - UploadPort []uint32 - Width int32 - Height int32 - Message string - DownloadIndex string - FileId int64 - ResultCode int32 - IsExists bool - } -) +type guildImageUploadResponse struct { + UploadKey []byte + UploadIp []uint32 + UploadPort []uint32 + Width int32 + Height int32 + Message string + DownloadIndex string + FileId int64 + ResultCode int32 + IsExists bool +} func init() { decoders["ImgStore.QQMeetPicUp"] = decodeGuildImageStoreResponse @@ -205,20 +203,18 @@ func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq, param.Version = []uint64{eventVersion} } + withVersionFlag := uint32(0) + if eventVersion != 0 { + withVersionFlag = 1 + } + directFlag := uint32(0) + if direct { + directFlag = 1 + } payload, _ := proto.Marshal(&channel.ChannelMsgReq{ - ChannelParam: param, - WithVersionFlag: proto.Uint32(func() uint32 { - if eventVersion != 0 { - return 1 - } - return 0 - }()), - DirectMessageFlag: proto.Uint32(func() uint32 { - if direct { - return 1 - } - return 0 - }()), + ChannelParam: param, + WithVersionFlag: &withVersionFlag, + DirectMessageFlag: &directFlag, }) seq := s.c.nextSeq() packet := packets.BuildUniPacket(s.c.Uin, seq, "trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) diff --git a/client/http_api.go b/client/http_api.go index 03ecda5e..118daf0d 100644 --- a/client/http_api.go +++ b/client/http_api.go @@ -124,10 +124,9 @@ func (c *QQClient) GetGroupHonorInfo(groupCode int64, honorType HonorType) (*Gro /* -------- TextToSpeech -------- */ func (c *QQClient) GetTts(text string) ([]byte, error) { - url := "https://textts.qq.com/cgi-bin/tts" - bt, _ := json.Marshal(text) - data := fmt.Sprintf(`{"appid": "201908021016","sendUin": %v,"text": %s}`, c.Uin, bt) - rsp, err := utils.HttpPostBytesWithCookie(url, []byte(data), c.getCookies()) + apiUrl := "https://textts.qq.com/cgi-bin/tts" + data := fmt.Sprintf(`{"appid": "201908021016","sendUin": %v,"text": %q}`, c.Uin, text) + rsp, err := utils.HttpPostBytesWithCookie(apiUrl, []byte(data), c.getCookies()) if err != nil { return nil, errors.Wrap(err, "failed to post to tts server") } diff --git a/client/image.go b/client/image.go index 0db3036b..54edfacb 100644 --- a/client/image.go +++ b/client/image.go @@ -32,27 +32,25 @@ func init() { var imgWaiter = utils.NewUploadWaiter() -type ( - imageUploadResponse struct { - UploadKey []byte - UploadIp []uint32 - UploadPort []uint32 - ResourceId string - Message string - FileId int64 - Width int32 - Height int32 - ResultCode int32 - IsExists bool - } -) +type imageUploadResponse struct { + UploadKey []byte + UploadIp []uint32 + UploadPort []uint32 + ResourceId string + Message string + FileId int64 + Width int32 + Height int32 + ResultCode int32 + IsExists bool +} func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) { _, _ = img.Seek(0, io.SeekStart) // safe fh, length := utils.ComputeMd5AndLength(img) _, _ = img.Seek(0, io.SeekStart) - key := hex.EncodeToString(fh) + key := string(fh) imgWaiter.Wait(key) defer imgWaiter.Done(key) diff --git a/client/multimsg.go b/client/multimsg.go index c2d3f478..b5e7696c 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -95,12 +95,13 @@ func decodeMultiApplyDownResponse(_ *QQClient, _ *incomingPacketInfo, payload [] return nil, errors.New("not found") } rsp := body.MultimsgApplydownRsp[0] - prefix := func() string { - if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 { - return "https://ssl.htdata.qq.com" - } - return fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0]) - }() + + var prefix string + if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 { + prefix = "https://ssl.htdata.qq.com" + } else { + prefix = fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0]) + } b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "") if err != nil { return nil, errors.Wrap(err, "failed to download by multi apply down") diff --git a/client/network.go b/client/network.go index 77cae726..9018696e 100644 --- a/client/network.go +++ b/client/network.go @@ -75,7 +75,7 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { }() start := time.Now() if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil { - r.LongMessageServerResponseLatency = time.Now().Sub(start).Milliseconds() + r.LongMessageServerResponseLatency = time.Since(start).Milliseconds() } else { c.Error("test long message server response latency error: %v", err) r.LongMessageServerResponseLatency = 9999 @@ -335,12 +335,7 @@ func (c *QQClient) netLoop() { decoded, err = decoder(c, &incomingPacketInfo{ SequenceId: pkt.SequenceId, CommandName: pkt.CommandName, - Params: func() requestParams { - if !ok { - return nil - } - return info.params - }(), + Params: info.getParams(), }, pkt.Payload) if err != nil { c.Debug("decode pkt %v error: %+v", pkt.CommandName, err) diff --git a/client/private_msg.go b/client/private_msg.go index 87c5ca56..4aa9b30a 100644 --- a/client/private_msg.go +++ b/client/private_msg.go @@ -131,6 +131,7 @@ func (s *TempSessionInfo) SendMessage(m *message.SendingMessage) (*message.TempM } } +/* this function is unused func (c *QQClient) buildGetOneDayRoamMsgRequest(target, lastMsgTime, random int64, count uint32) (uint16, []byte) { seq := c.nextSeq() req := &msg.PbGetOneDayRoamMsgReq{ @@ -143,6 +144,7 @@ func (c *QQClient) buildGetOneDayRoamMsgRequest(target, lastMsgTime, random int6 packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetOneDayRoamMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return seq, packet } +*/ // MessageSvc.PbSendMsg func (c *QQClient) buildFriendSendingPacket(target int64, msgSeq, r, pkgNum, pkgIndex, pkgDiv int32, time int64, m []message.IMessageElement) (uint16, []byte) { diff --git a/client/ptt.go b/client/ptt.go index 76ac4fd4..39e007e1 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -35,7 +35,7 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag pttWaiter.Wait(key) defer pttWaiter.Done(key) - ext := c.buildGroupPttStoreBDHExt(groupCode, fh[:], int32(length), 0, int32(length)) + ext := c.buildGroupPttStoreBDHExt(groupCode, fh, int32(length), 0, int32(length)) rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ CommandID: 29, Body: voice, @@ -60,8 +60,8 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag Ptt: &msg.Ptt{ FileType: proto.Int32(4), SrcUin: &c.Uin, - FileMd5: fh[:], - FileName: proto.String(hex.EncodeToString(fh[:]) + ".amr"), + FileMd5: fh, + FileName: proto.String(hex.EncodeToString(fh) + ".amr"), FileSize: proto.Int32(int32(length)), GroupFileKey: pkt.TryupPttRsp[0].FileKey, BoolValid: proto.Bool(true), @@ -81,7 +81,7 @@ func (c *QQClient) UploadPrivatePtt(target int64, voice io.ReadSeeker) (*message pttWaiter.Wait(key) defer pttWaiter.Done(key) - ext := c.buildC2CPttStoreBDHExt(target, fh[:], int32(length), int32(length)) + ext := c.buildC2CPttStoreBDHExt(target, fh, int32(length), int32(length)) rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ CommandID: 26, Body: voice, @@ -107,8 +107,8 @@ func (c *QQClient) UploadPrivatePtt(target int64, voice io.ReadSeeker) (*message FileType: proto.Int32(4), SrcUin: &c.Uin, FileUuid: pkt.ApplyUploadRsp.Uuid, - FileMd5: fh[:], - FileName: proto.String(hex.EncodeToString(fh[:]) + ".amr"), + FileMd5: fh, + FileName: proto.String(hex.EncodeToString(fh) + ".amr"), FileSize: proto.Int32(int32(length)), // Reserve: constructPTTExtraInfo(1, int32(len(voice))), // todo length BoolValid: proto.Bool(true), diff --git a/client/richmsg.go b/client/richmsg.go index 801b6de0..b5125eb2 100644 --- a/client/richmsg.go +++ b/client/richmsg.go @@ -101,15 +101,14 @@ func (c *QQClient) SendGuildMusicShare(guildID, channelID uint64, msg *message.M func (c *QQClient) buildRichMsgSendingPacket(guild uint64, target int64, msg *message.MusicShareElement, sendType uint32) (uint16, []byte) { seq := c.nextSeq() tp := musicType[msg.MusicType] // MusicType + msgStyle := uint32(0) + if msg.MusicUrl != "" { + msgStyle = 4 + } body := &oidb.DB77ReqBody{ - AppId: tp.appID, - AppType: tp.appType, - MsgStyle: func() uint32 { - if msg.MusicUrl == "" { - return 0 - } - return 4 - }(), + AppId: tp.appID, + AppType: tp.appType, + MsgStyle: msgStyle, ClientInfo: &oidb.DB77ClientInfo{ Platform: tp.platform, SdkVersion: tp.sdkVersion, diff --git a/client/stash.go b/client/stash.go deleted file mode 100644 index e8957b52..00000000 --- a/client/stash.go +++ /dev/null @@ -1,303 +0,0 @@ -// UNCHECKED - -package client - -import ( - "bytes" - "encoding/binary" - "io" - "strings" -) - -// Stash will store the data for the client, this will speed up booting -// the QQ client but some data may not sync with the server. So it is -// recommended to use this in development mode only. - -//go:generate stringer -type=syncMarker -trimprefix=syncMarker -type syncMarker int8 - -const ( - syncMarkerNone syncMarker = iota - syncMarkerFriendList - syncMarkerFriendInfo - syncMarkerGroupList - syncMarkerGroupInfo - syncMarkerGroupMemberList - syncMarkerGroupMemberInfo -) - -type StashSyncMarkerError struct { - marker syncMarker - expected syncMarker -} - -func (e *StashSyncMarkerError) Error() string { - return "stash sync marker error: expected " + e.expected.String() + ", got " + e.marker.String() -} - -// WriteStash will write the stash to the given writer. -func WriteStash(client *QQClient, writer io.Writer) { - var out intWriter - w := stashWriter{ - stringIndex: make(map[string]uint64), - } - - w.friendList(client.FriendList) - w.groupList(client.GroupList) - - out.uvarint(uint64(w.strings.Len())) - out.uvarint(uint64(w.data.Len())) - _, _ = io.Copy(&out, &w.strings) - _, _ = io.Copy(&out, &w.data) - _, _ = io.Copy(writer, &out) -} - -type stashWriter struct { - data intWriter - strings intWriter - stringIndex map[string]uint64 -} - -func (w *stashWriter) string(s string) { - off, ok := w.stringIndex[s] - if !ok { - off = uint64(w.strings.Len()) - w.strings.uvarint(uint64(len(s))) - _, _ = w.strings.WriteString(s) - w.stringIndex[s] = off - } - w.uvarint(off) -} - -func (w *stashWriter) sync(marker syncMarker) { w.data.uvarint(uint64(marker)) } -func (w *stashWriter) uvarint(v uint64) { w.data.uvarint(v) } -func (w *stashWriter) svarint(v int64) { w.data.svarint(v) } - -func (w *stashWriter) int64(v int64) { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], uint64(v)) - _, _ = w.data.Write(buf[:]) -} - -func (w *stashWriter) friendList(list []*FriendInfo) { - w.sync(syncMarkerFriendList) - w.uvarint(uint64(len(list))) - for _, friend := range list { - w.sync(syncMarkerFriendInfo) - w.int64(friend.Uin) - w.string(friend.Nickname) - w.string(friend.Remark) - w.svarint(int64(friend.FaceId)) - } -} - -func (w *stashWriter) groupList(list []*GroupInfo) { - w.sync(syncMarkerGroupList) - w.uvarint(uint64(len(list))) - for _, group := range list { - w.sync(syncMarkerGroupInfo) - w.int64(group.Uin) - w.int64(group.Code) - w.string(group.Name) - w.string(group.Memo) - w.int64(group.OwnerUin) - w.uvarint(uint64(group.GroupCreateTime)) - w.uvarint(uint64(group.MemberCount)) - w.uvarint(uint64(group.MaxMemberCount)) - w.svarint(group.LastMsgSeq) - - w.groupMemberList(group.Members) - } -} - -func (w *stashWriter) groupMemberList(list []*GroupMemberInfo) { - w.sync(syncMarkerGroupMemberList) - w.uvarint(uint64(len(list))) - for _, member := range list { - w.sync(syncMarkerGroupMemberInfo) - w.int64(member.Uin) - w.uvarint(uint64(member.Gender)) - w.string(member.Nickname) - w.string(member.CardName) - w.uvarint(uint64(member.Level)) - w.int64(member.JoinTime) - w.int64(member.LastSpeakTime) - w.string(member.SpecialTitle) - w.svarint(member.SpecialTitleExpireTime) - w.svarint(member.ShutUpTimestamp) - w.uvarint(uint64(member.Permission)) - } -} - -type intWriter struct { - bytes.Buffer -} - -func (w *intWriter) svarint(x int64) { - w.uvarint(uint64(x)<<1 ^ uint64(x>>63)) -} - -func (w *intWriter) uvarint(x uint64) { - var buf [binary.MaxVarintLen64]byte - n := binary.PutUvarint(buf[:], x) - _, _ = w.Write(buf[:n]) -} - -// ReadStash will read the stash from the given reader and apply to the given QQClient. -func ReadStash(client *QQClient, data string) (err error) { - in := newIntReader(data) - sl := int64(in.uvarint()) - dl := int64(in.uvarint()) - whence, err := in.Seek(0, io.SeekCurrent) - if err != nil { - return err - } - sData := data[whence : whence+sl] - dData := data[whence+sl : whence+sl+dl] - - r := stashReader{ - data: newIntReader(dData), - strings: newIntReader(sData), - stringIndex: make(map[uint64]string), - } - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok { - err = e - return - } - panic(r) - } - }() - - client.FriendList = r.friendList() - client.GroupList = r.groupList(client) - return nil -} - -type stashReader struct { - data intReader - strings intReader - stringIndex map[uint64]string -} - -func (r *stashReader) string() string { - off := r.data.uvarint() - if off == 0 { - return "" - } - if s, ok := r.stringIndex[off]; ok { - return s - } - _, _ = r.strings.Seek(int64(off), io.SeekStart) - l := int64(r.strings.uvarint()) - whence, _ := r.strings.Seek(0, io.SeekCurrent) - s := r.strings.data[whence : whence+l] - r.stringIndex[off] = s - return s -} - -func (r *stashReader) sync(marker syncMarker) { - got := syncMarker(r.data.uvarint()) - if got != marker { - panic(&StashSyncMarkerError{ - marker: got, - expected: marker, - }) - } -} - -func (r *stashReader) friendList() []*FriendInfo { - r.sync(syncMarkerFriendList) - l := r.uvarint() - list := make([]*FriendInfo, l) - for i := uint64(0); i < l; i++ { - r.sync(syncMarkerFriendInfo) - list[i] = &FriendInfo{ - Uin: r.int64(), - Nickname: r.string(), - Remark: r.string(), - FaceId: int16(r.svarint()), - } - } - return list -} - -func (r *stashReader) groupList(client *QQClient) []*GroupInfo { - r.sync(syncMarkerGroupList) - l := r.uvarint() - list := make([]*GroupInfo, l) - for i := uint64(0); i < l; i++ { - r.sync(syncMarkerGroupInfo) - list[i] = &GroupInfo{ - Uin: r.int64(), - Code: r.int64(), - Name: r.string(), - Memo: r.string(), - OwnerUin: r.int64(), - GroupCreateTime: uint32(r.uvarint()), - GroupLevel: uint32(r.uvarint()), - MemberCount: uint16(r.uvarint()), - MaxMemberCount: uint16(r.uvarint()), - client: client, - } - list[i].Members = r.groupMemberList(list[i]) - list[i].LastMsgSeq = r.svarint() - } - return list -} - -func (r *stashReader) groupMemberList(group *GroupInfo) []*GroupMemberInfo { - r.sync(syncMarkerGroupMemberList) - l := r.uvarint() - list := make([]*GroupMemberInfo, l) - for i := uint64(0); i < l; i++ { - r.sync(syncMarkerGroupMemberInfo) - list[i] = &GroupMemberInfo{ - Group: group, - Uin: r.int64(), - Gender: byte(r.uvarint()), - Nickname: r.string(), - CardName: r.string(), - Level: uint16(r.uvarint()), - JoinTime: r.int64(), - LastSpeakTime: r.int64(), - SpecialTitle: r.string(), - SpecialTitleExpireTime: r.svarint(), - ShutUpTimestamp: r.svarint(), - Permission: MemberPermission(r.uvarint()), - } - } - return list -} - -func (r *stashReader) uvarint() uint64 { return r.data.uvarint() } -func (r *stashReader) svarint() int64 { return r.data.svarint() } - -func (r *stashReader) int64() int64 { - var buf [8]byte - _, _ = r.data.Read(buf[:]) - return int64(binary.LittleEndian.Uint64(buf[:])) -} - -type intReader struct { - data string - *strings.Reader -} - -func newIntReader(s string) intReader { - return intReader{ - data: s, - Reader: strings.NewReader(s), - } -} - -func (r *intReader) svarint() int64 { - i, _ := binary.ReadVarint(r) - return i -} - -func (r *intReader) uvarint() uint64 { - i, _ := binary.ReadUvarint(r) - return i -} diff --git a/client/syncmarker_string.go b/client/syncmarker_string.go deleted file mode 100644 index 729efd8b..00000000 --- a/client/syncmarker_string.go +++ /dev/null @@ -1,29 +0,0 @@ -// Code generated by "stringer -type=syncMarker -trimprefix=syncMarker"; DO NOT EDIT. - -package client - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[syncMarkerNone-0] - _ = x[syncMarkerFriendList-1] - _ = x[syncMarkerFriendInfo-2] - _ = x[syncMarkerGroupList-3] - _ = x[syncMarkerGroupInfo-4] - _ = x[syncMarkerGroupMemberList-5] - _ = x[syncMarkerGroupMemberInfo-6] -} - -const _syncMarker_name = "NoneFriendListFriendInfoGroupListGroupInfoGroupMemberListGroupMemberInfo" - -var _syncMarker_index = [...]uint8{0, 4, 14, 24, 33, 42, 57, 72} - -func (i syncMarker) String() string { - if i < 0 || i >= syncMarker(len(_syncMarker_index)-1) { - return "syncMarker(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _syncMarker_name[_syncMarker_index[i]:_syncMarker_index[i+1]] -} diff --git a/client/system_msg.go b/client/system_msg.go index bfb35c87..33e9e3ed 100644 --- a/client/system_msg.go +++ b/client/system_msg.go @@ -138,31 +138,26 @@ func (c *QQClient) buildSystemMsgNewGroupPacket(suspicious bool) (uint16, []byte // ProfileService.Pb.ReqSystemMsgAction.Group func (c *QQClient) buildSystemMsgGroupActionPacket(reqID, requester, group int64, msgType int32, isInvite, accept, block bool, reason string) (uint16, []byte) { seq := c.nextSeq() + subSrcId := int32(31) + groupMsgType := int32(1) + if isInvite { + subSrcId = 10016 + groupMsgType = 2 + } + infoType := int32(12) + if accept { + infoType = 11 + } req := &structmsg.ReqSystemMsgAction{ - MsgType: msgType, - MsgSeq: reqID, - ReqUin: requester, - SubType: 1, - SrcId: 3, - SubSrcId: func() int32 { - if isInvite { - return 10016 - } - return 31 - }(), - GroupMsgType: func() int32 { - if isInvite { - return 2 - } - return 1 - }(), + MsgType: msgType, + MsgSeq: reqID, + ReqUin: requester, + SubType: 1, + SrcId: 3, + SubSrcId: subSrcId, + GroupMsgType: groupMsgType, ActionInfo: &structmsg.SystemMsgActionInfo{ - Type: func() int32 { - if accept { - return 11 - } - return 12 - }(), + Type: infoType, GroupCode: group, Blacklist: block, Msg: reason, @@ -178,6 +173,10 @@ func (c *QQClient) buildSystemMsgGroupActionPacket(reqID, requester, group int64 // ProfileService.Pb.ReqSystemMsgAction.Friend func (c *QQClient) buildSystemMsgFriendActionPacket(reqID, requester int64, accept bool) (uint16, []byte) { seq := c.nextSeq() + infoType := int32(3) + if accept { + infoType = 2 + } req := &structmsg.ReqSystemMsgAction{ MsgType: 1, MsgSeq: reqID, @@ -186,12 +185,7 @@ func (c *QQClient) buildSystemMsgFriendActionPacket(reqID, requester int64, acce SrcId: 6, SubSrcId: 7, ActionInfo: &structmsg.SystemMsgActionInfo{ - Type: func() int32 { - if accept { - return 2 - } - return 3 - }(), + Type: infoType, Blacklist: false, AddFrdSNInfo: &structmsg.AddFrdSNInfo{}, }, diff --git a/message/elements.go b/message/elements.go index e1fa3c3b..09e48915 100644 --- a/message/elements.go +++ b/message/elements.go @@ -205,12 +205,12 @@ func NewUrlShare(url, title, content, image string) *ServiceElement { } } -func NewRichXml(template string, ResId int64) *ServiceElement { - if ResId == 0 { - ResId = 60 // 默认值60 +func NewRichXml(template string, resID int64) *ServiceElement { + if resID == 0 { + resID = 60 // 默认值60 } return &ServiceElement{ - Id: int32(ResId), + Id: int32(resID), Content: template, SubType: "xml", } diff --git a/message/image.go b/message/image.go index 80018ceb..41b11100 100644 --- a/message/image.go +++ b/message/image.go @@ -138,8 +138,7 @@ func (e *GroupImageElement) Pack() (r []*msg.Elem) { return []*msg.Elem{elem} } -func (e *FriendImageElement) Pack() (r []*msg.Elem) { - r = []*msg.Elem{} +func (e *FriendImageElement) Pack() []*msg.Elem { image := &msg.NotOnlineImage{ FilePath: &e.ImageId, ResId: &e.ImageId, diff --git a/message/message.go b/message/message.go index 2a38b637..887e96b2 100644 --- a/message/message.go +++ b/message/message.go @@ -298,20 +298,17 @@ func ToProtoElems(elems []IMessageElement, generalFlags bool) (r []*msg.Elem) { return } -func ToSrcProtoElems(elems []IMessageElement) (r []*msg.Elem) { - for _, elem := range elems { - switch elem.Type() { - case Image: - r = append(r, &msg.Elem{ - Text: &msg.Text{ - Str: proto.String("[图片]"), - }, - }) - default: - r = append(r, ToProtoElems([]IMessageElement{elem}, false)...) +var photoTextElem IMessageElement = NewText("[图片]") + +func ToSrcProtoElems(elems []IMessageElement) []*msg.Elem { + elems2 := make([]IMessageElement, len(elems)) + copy(elems2, elems) + for i, elem := range elems2 { + if elem.Type() == Image { + elems2[i] = photoTextElem } } - return + return ToProtoElems(elems2, false) } func ParseMessageElems(elems []*msg.Elem) []IMessageElement { @@ -431,11 +428,9 @@ func ParseMessageElems(elems []*msg.Elem) []IMessageElement { if isOk := strings.Contains(content, "