From 3555d23136b0a3d425cdfd3333c1be95fbbb3fa3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 25 Feb 2022 21:21:21 +0800 Subject: [PATCH 01/44] network: remove unnecessary sync.Pool --- client/internal/highway/bdh.go | 16 +++++----- client/internal/highway/highway.go | 9 ++++-- client/internal/network/frame.go | 47 +++++++++------------------- client/internal/network/request.go | 2 -- client/internal/network/response.go | 1 + client/internal/network/transport.go | 1 + 6 files changed, 32 insertions(+), 44 deletions(-) diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 4d76a182..2921d37e 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -105,7 +105,8 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { ReqExtendinfo: input.Ext, }) offset += rl - _, err = io.Copy(conn, network.HeadBodyFrame(head, chunk)) + frame := network.HeadBodyFrame(head, chunk) + _, err = frame.WriteTo(conn) if err != nil { return nil, errors.Wrap(err, "write conn error") } @@ -189,7 +190,7 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in return err } - buffer := make([]byte, blockSize) + chunk := make([]byte, blockSize) for { nextId := atomic.AddUint32(&BlockId, 1) if nextId >= uint32(len(blocks)) { @@ -203,10 +204,10 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in } cond.L.Unlock() } - buffer = buffer[:blockSize] + chunk = chunk[:blockSize] cond.L.Lock() // lock protect reading - n, err := input.Body.ReadAt(buffer, block.Offset) + n, err := input.Body.ReadAt(chunk, block.Offset) cond.L.Unlock() if err != nil { @@ -214,12 +215,12 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in break } if err == io.ErrUnexpectedEOF { - buffer = buffer[:n] + chunk = chunk[:n] } else { return err } } - ch := md5.Sum(buffer) + ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ @@ -232,7 +233,8 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in }, ReqExtendinfo: input.Ext, }) - _, err = io.Copy(conn, network.HeadBodyFrame(head, buffer)) + frame := network.HeadBodyFrame(head, chunk) + _, err = frame.WriteTo(conn) if err != nil { return errors.Wrap(err, "write conn error") } diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index 81cd0ffd..d031bc6d 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -86,7 +86,8 @@ func (s *Session) Upload(addr Addr, input Input) error { ReqExtendinfo: []byte{}, }) offset += rl - _, err = io.Copy(conn, network.HeadBodyFrame(head, chunk)) + frame := network.HeadBodyFrame(head, chunk) + _, err = frame.WriteTo(conn) if err != nil { return errors.Wrap(err, "write conn error") } @@ -145,7 +146,8 @@ func (s *Session) UploadExciting(input ExcitingInput) ([]byte, error) { ReqExtendinfo: input.Ext, }) offset += int64(rl) - req, _ := http.NewRequest("POST", url, network.HeadBodyFrame(head, chunk)) + frame := network.HeadBodyFrame(head, chunk) + req, _ := http.NewRequest("POST", url, &frame) req.Header.Set("Accept", "*/*") req.Header.Set("Connection", "Keep-Alive") req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") @@ -206,7 +208,8 @@ func (s *Session) sendHeartbreak(conn net.Conn) error { LocaleId: 2052, }, }) - _, err := io.Copy(conn, network.HeadBodyFrame(head, nil)) + frame := network.HeadBodyFrame(head, nil) + _, err := frame.WriteTo(conn) return err } diff --git a/client/internal/network/frame.go b/client/internal/network/frame.go index c272a64a..2b1b8f08 100644 --- a/client/internal/network/frame.go +++ b/client/internal/network/frame.go @@ -2,30 +2,11 @@ package network import ( "encoding/binary" - "io" "net" - "sync" ) var etx = []byte{0x29} -type Buffers struct { - net.Buffers -} - -var pool = sync.Pool{ - New: func() interface{} { - lenHead := make([]byte, 9) - lenHead[0] = 0x28 - return &Buffers{net.Buffers{lenHead, nil, nil, etx}} - }, -} - -func (b *Buffers) WriteTo(w io.Writer) (n int64, err error) { - defer pool.Put(b) // implement auto put to pool - return b.Buffers.WriteTo(w) -} - // HeadBodyFrame 包格式 // * STX // * head length @@ -34,17 +15,19 @@ func (b *Buffers) WriteTo(w io.Writer) (n int64, err error) { // * body data // * ETX // 节省内存, 可被go runtime优化为writev操作 -func HeadBodyFrame(head []byte, body []byte) *Buffers { - b := pool.Get().(*Buffers) - if len(b.Buffers) == 0 { - lenHead := make([]byte, 9) - lenHead[0] = 0x28 - b.Buffers = net.Buffers{lenHead, nil, nil, etx} - } - b.Buffers[2] = body - b.Buffers[1] = head - _ = b.Buffers[0][8] - binary.BigEndian.PutUint32(b.Buffers[0][1:], uint32(len(head))) - binary.BigEndian.PutUint32(b.Buffers[0][5:], uint32(len(body))) - return b +func HeadBodyFrame(head []byte, body []byte) net.Buffers { + buffers := make(net.Buffers, 4) + // buffer0 format: + // * STX + // * head length + // * body length + buffer0 := make([]byte, 9) + buffer0[0] = 0x28 + binary.BigEndian.PutUint32(buffer0[1:], uint32(len(head))) + binary.BigEndian.PutUint32(buffer0[5:], uint32(len(body))) + buffers[0] = buffer0 + buffers[1] = head + buffers[2] = body + buffers[3] = etx + return buffers } diff --git a/client/internal/network/request.go b/client/internal/network/request.go index b34930f5..6e61686b 100644 --- a/client/internal/network/request.go +++ b/client/internal/network/request.go @@ -7,8 +7,6 @@ const ( RequestTypeSimple = 0x0B ) -var emptyKey = make([]byte, 16) - type EncryptType uint32 const ( diff --git a/client/internal/network/response.go b/client/internal/network/response.go index c2d97f3c..bdaa4c40 100644 --- a/client/internal/network/response.go +++ b/client/internal/network/response.go @@ -46,6 +46,7 @@ func (t *Transport) ReadResponse(head []byte) (*Response, error) { case EncryptTypeD2Key: body = binary.NewTeaCipher(t.Sig.D2Key).Decrypt(body) case EncryptTypeEmptyKey: + emptyKey := make([]byte, 16) body = binary.NewTeaCipher(emptyKey).Decrypt(body) } err := t.readSSOFrame(resp, body) diff --git a/client/internal/network/transport.go b/client/internal/network/transport.go index dd79a8d9..e2286e33 100644 --- a/client/internal/network/transport.go +++ b/client/internal/network/transport.go @@ -89,6 +89,7 @@ func (t *Transport) PackPacket(req *Request) []byte { case EncryptTypeD2Key: body = binary.NewTeaCipher(t.Sig.D2Key).Encrypt(body) case EncryptTypeEmptyKey: + emptyKey := make([]byte, 16) body = binary.NewTeaCipher(emptyKey).Encrypt(body) } w.Write(body) From a2d65a2bb68227a493b1fe390e4ab77a95ff4234 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 25 Feb 2022 22:18:52 +0800 Subject: [PATCH 02/44] highway: use io.ReadSeeker and disable multi-thread upload --- client/image.go | 2 +- client/internal/highway/bdh.go | 10 ++- client/ptt.go | 9 ++- utils/sys.go | 143 +++++++++++++++------------------ 4 files changed, 76 insertions(+), 88 deletions(-) diff --git a/client/image.go b/client/image.go index 3806b493..7e1a4f1b 100644 --- a/client/image.go +++ b/client/image.go @@ -118,7 +118,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee if tc > 1 && length > 3*1024*1024 { _, err = c.highwaySession.UploadBDHMultiThread(highway.BdhMultiThreadInput{ CommandID: cmd, - Body: utils.ReaderAtFrom2ReadSeeker(img, nil), + Body: img, Size: length, Sum: fh, Ticket: rsp.UploadKey, diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 2921d37e..e9605f3c 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -28,7 +28,7 @@ type BdhInput struct { type BdhMultiThreadInput struct { CommandID int32 - Body io.ReaderAt + Body io.ReadSeeker Sum []byte Size int64 Ticket []byte @@ -130,10 +130,11 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount int) ([]byte, error) { // for small file and small thread count, // use UploadBDH instead of UploadBDHMultiThread - if input.Size < 1024*1024*3 || threadCount < 2 { + // FIXME: enable multi-thread, now receive error code 81 + if true || input.Size < 1024*1024*3 || threadCount < 2 { return s.UploadBDH(BdhInput{ CommandID: input.CommandID, - Body: io.NewSectionReader(input.Body, 0, input.Size), + Body: input.Body, Ticket: input.Ticket, Ext: input.Ext, Encrypt: input.Encrypt, @@ -207,7 +208,8 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in chunk = chunk[:blockSize] cond.L.Lock() // lock protect reading - n, err := input.Body.ReadAt(chunk, block.Offset) + _, _ = input.Body.Seek(block.Offset, io.SeekStart) + n, err := input.Body.Read(chunk) cond.L.Unlock() if err != nil { diff --git a/client/ptt.go b/client/ptt.go index 0d3e7fdf..9ad2caeb 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -150,11 +150,13 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS cmd = 89 } ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoHash, thumbHash, videoLen, thumbLen).PttShortVideoUploadReq) + combined := utils.DoubleReadSeeker(video, thumb) if thread > 1 { - sum, _ := utils.ComputeMd5AndLength(utils.MultiReadSeeker(thumb, video)) + sum, _ := utils.ComputeMd5AndLength(combined) + _, _ = combined.Seek(0, io.SeekStart) input := highway.BdhMultiThreadInput{ CommandID: cmd, - Body: utils.ReaderAtFrom2ReadSeeker(thumb, video), + Body: combined, Size: videoLen + thumbLen, Sum: sum, Ticket: c.highwaySession.SigSession, @@ -163,10 +165,9 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS } hwRsp, err = c.highwaySession.UploadBDHMultiThread(input, thread) } else { - multi := utils.MultiReadSeeker(thumb, video) input := highway.BdhInput{ CommandID: cmd, - Body: multi, + Body: combined, Ticket: c.highwaySession.SigSession, Ext: ext, Encrypt: true, diff --git a/utils/sys.go b/utils/sys.go index 7ef22b45..75b54e7d 100644 --- a/utils/sys.go +++ b/utils/sys.go @@ -6,9 +6,57 @@ import ( "io" ) -type multiReadSeeker struct { - readers []io.ReadSeeker - multiReader io.Reader +type doubleReadSeeker struct { + rs1, rs2 io.ReadSeeker + rs1len, rs2len int64 + pos int64 +} + +func (r *doubleReadSeeker) Seek(offset int64, whence int) (int64, error) { + var err error + switch whence { + case io.SeekStart: + if offset < r.rs1len { + r.pos, err = r.rs1.Seek(offset, io.SeekStart) + return r.pos, err + } else { + r.pos, err = r.rs2.Seek(offset-r.rs1len, io.SeekStart) + r.pos += r.rs1len + return r.pos, err + } + case io.SeekEnd: // negative offset + return r.Seek(r.rs1len+r.rs2len+offset-1, io.SeekStart) + default: // io.SeekCurrent + return r.Seek(r.pos+offset, io.SeekStart) + } +} + +func (r *doubleReadSeeker) Read(p []byte) (n int, err error) { + switch { + case r.pos >= r.rs1len: // read only from the second reader + n, err := r.rs2.Read(p) + r.pos += int64(n) + return n, err + case r.pos+int64(len(p)) <= r.rs1len: // read only from the first reader + n, err := r.rs1.Read(p) + r.pos += int64(n) + return n, err + default: // read on the border - end of first reader and start of second reader + n1, err := r.rs1.Read(p) + r.pos += int64(n1) + if r.pos != r.rs1len || (err != nil && errors.Is(err, io.EOF)) { + // Read() might not read all, return + // If error (but not EOF), return + return n1, err + } + _, err = r.rs2.Seek(0, io.SeekStart) + if err != nil { + return n1, err + } + n2, err := r.rs2.Read(p[n1:]) + r.pos += int64(n2) + return n1 + n2, err + } } func ComputeMd5AndLength(r io.Reader) ([]byte, int64) { @@ -18,82 +66,19 @@ func ComputeMd5AndLength(r io.Reader) ([]byte, int64) { return fh, length } -func (r *multiReadSeeker) Read(p []byte) (int, error) { - if r.multiReader == nil { - var readers []io.Reader - for i := range r.readers { - _, _ = r.readers[i].Seek(0, io.SeekStart) - readers = append(readers, r.readers[i]) - } - r.multiReader = io.MultiReader(readers...) - } - return r.multiReader.Read(p) -} - -func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) { - if whence != 0 || offset != 0 { - return -1, errors.New("unsupported offset") - } - r.multiReader = nil - return 0, nil -} - -func MultiReadSeeker(r ...io.ReadSeeker) io.ReadSeeker { - return &multiReadSeeker{ - readers: r, - } -} - -type multiReadAt struct { - first io.ReadSeeker - second io.ReadSeeker - firstSize int64 - secondSize int64 -} - -func (m *multiReadAt) ReadAt(p []byte, off int64) (n int, err error) { - if m.second == nil { // quick path - _, _ = m.first.Seek(off, io.SeekStart) - return m.first.Read(p) - } - if off < m.firstSize && off+int64(len(p)) < m.firstSize { - _, err = m.first.Seek(off, io.SeekStart) - if err != nil { - return - } - return m.first.Read(p) - } else if off < m.firstSize && off+int64(len(p)) >= m.firstSize { - _, _ = m.first.Seek(off, io.SeekStart) - _, _ = m.second.Seek(0, io.SeekStart) - n, err = m.first.Read(p[:m.firstSize-off]) - if err != nil { - return - } - n2, err := m.second.Read(p[m.firstSize-off:]) - return n + n2, err - } - _, err = m.second.Seek(off-m.firstSize, io.SeekStart) - if err != nil { - return - } - return m.second.Read(p) -} - -func ReaderAtFrom2ReadSeeker(first, second io.ReadSeeker) io.ReaderAt { - firstSize, _ := first.Seek(0, io.SeekEnd) - if second == nil { - return &multiReadAt{ - first: first, - firstSize: firstSize, - secondSize: 0, - } - } - secondSize, _ := second.Seek(0, io.SeekEnd) - return &multiReadAt{ - first: first, - second: second, - firstSize: firstSize, - secondSize: secondSize, +// DoubleReadSeeker combines two io.ReadSeeker into one. +// input two io.ReadSeeker must be at the start. +func DoubleReadSeeker(first, second io.ReadSeeker) io.ReadSeeker { + rs1Len, _ := first.Seek(0, io.SeekEnd) + _, _ = first.Seek(0, io.SeekStart) // reset to start + rs2Len, _ := second.Seek(0, io.SeekEnd) + _, _ = second.Seek(0, io.SeekStart) // reset to start + return &doubleReadSeeker{ + rs1: first, + rs2: second, + rs1len: rs1Len, + rs2len: rs2Len, + pos: 0, } } From e6ad62569d1f49ca350a1ad75260e046de9244d7 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Feb 2022 14:13:23 +0800 Subject: [PATCH 03/44] Revert "highway: use io.ReadSeeker and disable multi-thread upload" This reverts commit a2d65a2bb68227a493b1fe390e4ab77a95ff4234. This commit break video uploading. --- client/image.go | 2 +- client/internal/highway/bdh.go | 10 +-- client/ptt.go | 9 +-- utils/sys.go | 143 ++++++++++++++++++--------------- 4 files changed, 88 insertions(+), 76 deletions(-) diff --git a/client/image.go b/client/image.go index 7e1a4f1b..3806b493 100644 --- a/client/image.go +++ b/client/image.go @@ -118,7 +118,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee if tc > 1 && length > 3*1024*1024 { _, err = c.highwaySession.UploadBDHMultiThread(highway.BdhMultiThreadInput{ CommandID: cmd, - Body: img, + Body: utils.ReaderAtFrom2ReadSeeker(img, nil), Size: length, Sum: fh, Ticket: rsp.UploadKey, diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index e9605f3c..2921d37e 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -28,7 +28,7 @@ type BdhInput struct { type BdhMultiThreadInput struct { CommandID int32 - Body io.ReadSeeker + Body io.ReaderAt Sum []byte Size int64 Ticket []byte @@ -130,11 +130,10 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount int) ([]byte, error) { // for small file and small thread count, // use UploadBDH instead of UploadBDHMultiThread - // FIXME: enable multi-thread, now receive error code 81 - if true || input.Size < 1024*1024*3 || threadCount < 2 { + if input.Size < 1024*1024*3 || threadCount < 2 { return s.UploadBDH(BdhInput{ CommandID: input.CommandID, - Body: input.Body, + Body: io.NewSectionReader(input.Body, 0, input.Size), Ticket: input.Ticket, Ext: input.Ext, Encrypt: input.Encrypt, @@ -208,8 +207,7 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in chunk = chunk[:blockSize] cond.L.Lock() // lock protect reading - _, _ = input.Body.Seek(block.Offset, io.SeekStart) - n, err := input.Body.Read(chunk) + n, err := input.Body.ReadAt(chunk, block.Offset) cond.L.Unlock() if err != nil { diff --git a/client/ptt.go b/client/ptt.go index 9ad2caeb..0d3e7fdf 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -150,13 +150,11 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS cmd = 89 } ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoHash, thumbHash, videoLen, thumbLen).PttShortVideoUploadReq) - combined := utils.DoubleReadSeeker(video, thumb) if thread > 1 { - sum, _ := utils.ComputeMd5AndLength(combined) - _, _ = combined.Seek(0, io.SeekStart) + sum, _ := utils.ComputeMd5AndLength(utils.MultiReadSeeker(thumb, video)) input := highway.BdhMultiThreadInput{ CommandID: cmd, - Body: combined, + Body: utils.ReaderAtFrom2ReadSeeker(thumb, video), Size: videoLen + thumbLen, Sum: sum, Ticket: c.highwaySession.SigSession, @@ -165,9 +163,10 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS } hwRsp, err = c.highwaySession.UploadBDHMultiThread(input, thread) } else { + multi := utils.MultiReadSeeker(thumb, video) input := highway.BdhInput{ CommandID: cmd, - Body: combined, + Body: multi, Ticket: c.highwaySession.SigSession, Ext: ext, Encrypt: true, diff --git a/utils/sys.go b/utils/sys.go index 75b54e7d..7ef22b45 100644 --- a/utils/sys.go +++ b/utils/sys.go @@ -6,57 +6,9 @@ import ( "io" ) -type doubleReadSeeker struct { - rs1, rs2 io.ReadSeeker - rs1len, rs2len int64 - pos int64 -} - -func (r *doubleReadSeeker) Seek(offset int64, whence int) (int64, error) { - var err error - switch whence { - case io.SeekStart: - if offset < r.rs1len { - r.pos, err = r.rs1.Seek(offset, io.SeekStart) - return r.pos, err - } else { - r.pos, err = r.rs2.Seek(offset-r.rs1len, io.SeekStart) - r.pos += r.rs1len - return r.pos, err - } - case io.SeekEnd: // negative offset - return r.Seek(r.rs1len+r.rs2len+offset-1, io.SeekStart) - default: // io.SeekCurrent - return r.Seek(r.pos+offset, io.SeekStart) - } -} - -func (r *doubleReadSeeker) Read(p []byte) (n int, err error) { - switch { - case r.pos >= r.rs1len: // read only from the second reader - n, err := r.rs2.Read(p) - r.pos += int64(n) - return n, err - case r.pos+int64(len(p)) <= r.rs1len: // read only from the first reader - n, err := r.rs1.Read(p) - r.pos += int64(n) - return n, err - default: // read on the border - end of first reader and start of second reader - n1, err := r.rs1.Read(p) - r.pos += int64(n1) - if r.pos != r.rs1len || (err != nil && errors.Is(err, io.EOF)) { - // Read() might not read all, return - // If error (but not EOF), return - return n1, err - } - _, err = r.rs2.Seek(0, io.SeekStart) - if err != nil { - return n1, err - } - n2, err := r.rs2.Read(p[n1:]) - r.pos += int64(n2) - return n1 + n2, err - } +type multiReadSeeker struct { + readers []io.ReadSeeker + multiReader io.Reader } func ComputeMd5AndLength(r io.Reader) ([]byte, int64) { @@ -66,19 +18,82 @@ func ComputeMd5AndLength(r io.Reader) ([]byte, int64) { return fh, length } -// DoubleReadSeeker combines two io.ReadSeeker into one. -// input two io.ReadSeeker must be at the start. -func DoubleReadSeeker(first, second io.ReadSeeker) io.ReadSeeker { - rs1Len, _ := first.Seek(0, io.SeekEnd) - _, _ = first.Seek(0, io.SeekStart) // reset to start - rs2Len, _ := second.Seek(0, io.SeekEnd) - _, _ = second.Seek(0, io.SeekStart) // reset to start - return &doubleReadSeeker{ - rs1: first, - rs2: second, - rs1len: rs1Len, - rs2len: rs2Len, - pos: 0, +func (r *multiReadSeeker) Read(p []byte) (int, error) { + if r.multiReader == nil { + var readers []io.Reader + for i := range r.readers { + _, _ = r.readers[i].Seek(0, io.SeekStart) + readers = append(readers, r.readers[i]) + } + r.multiReader = io.MultiReader(readers...) + } + return r.multiReader.Read(p) +} + +func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) { + if whence != 0 || offset != 0 { + return -1, errors.New("unsupported offset") + } + r.multiReader = nil + return 0, nil +} + +func MultiReadSeeker(r ...io.ReadSeeker) io.ReadSeeker { + return &multiReadSeeker{ + readers: r, + } +} + +type multiReadAt struct { + first io.ReadSeeker + second io.ReadSeeker + firstSize int64 + secondSize int64 +} + +func (m *multiReadAt) ReadAt(p []byte, off int64) (n int, err error) { + if m.second == nil { // quick path + _, _ = m.first.Seek(off, io.SeekStart) + return m.first.Read(p) + } + if off < m.firstSize && off+int64(len(p)) < m.firstSize { + _, err = m.first.Seek(off, io.SeekStart) + if err != nil { + return + } + return m.first.Read(p) + } else if off < m.firstSize && off+int64(len(p)) >= m.firstSize { + _, _ = m.first.Seek(off, io.SeekStart) + _, _ = m.second.Seek(0, io.SeekStart) + n, err = m.first.Read(p[:m.firstSize-off]) + if err != nil { + return + } + n2, err := m.second.Read(p[m.firstSize-off:]) + return n + n2, err + } + _, err = m.second.Seek(off-m.firstSize, io.SeekStart) + if err != nil { + return + } + return m.second.Read(p) +} + +func ReaderAtFrom2ReadSeeker(first, second io.ReadSeeker) io.ReaderAt { + firstSize, _ := first.Seek(0, io.SeekEnd) + if second == nil { + return &multiReadAt{ + first: first, + firstSize: firstSize, + secondSize: 0, + } + } + secondSize, _ := second.Seek(0, io.SeekEnd) + return &multiReadAt{ + first: first, + second: second, + firstSize: firstSize, + secondSize: secondSize, } } From ca4580dad5ade33c9bd6e90afcd1a73f0261a0ae Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Feb 2022 15:42:02 +0800 Subject: [PATCH 04/44] highway: refactor multi thread uploading --- client/image.go | 31 +++++------ client/internal/highway/bdh.go | 99 ++++++++++------------------------ client/ptt.go | 48 ++++++++--------- 3 files changed, 66 insertions(+), 112 deletions(-) diff --git a/client/image.go b/client/image.go index 3806b493..a0cf0b0f 100644 --- a/client/image.go +++ b/client/image.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "crypto/md5" "encoding/hex" "io" "math/rand" @@ -93,6 +94,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee var r interface{} var err error + var input highway.BdhInput switch target.SourceType { case message.SourceGroup: r, err = c.sendAndWait(c.buildGroupImageStorePacket(target.PrimaryID, fh, int32(length))) @@ -115,22 +117,18 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee } } + input = highway.BdhInput{ + CommandID: cmd, + Body: img, + Size: length, + Sum: fh, + Ticket: rsp.UploadKey, + Ext: ext, + } if tc > 1 && length > 3*1024*1024 { - _, err = c.highwaySession.UploadBDHMultiThread(highway.BdhMultiThreadInput{ - CommandID: cmd, - Body: utils.ReaderAtFrom2ReadSeeker(img, nil), - Size: length, - Sum: fh, - Ticket: rsp.UploadKey, - Ext: ext, - }, 4) + _, err = c.highwaySession.UploadBDHMultiThread(input, tc) } else { - _, err = c.highwaySession.UploadBDH(highway.BdhInput{ - CommandID: cmd, - Body: img, - Ticket: rsp.UploadKey, - Ext: ext, - }) + _, err = c.highwaySession.UploadBDH(input) } if err != nil { return nil, errors.Wrap(err, "upload failed") @@ -326,10 +324,13 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) { Uuid: binary.GenUUID(r), }) - buf, _ := io.ReadAll(img) + h := md5.New() + buf, _ := io.ReadAll(io.TeeReader(img, h)) rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ CommandID: 76, Body: bytes.NewReader(buf), + Size: int64(len(buf)), + Sum: h.Sum(nil), Ticket: c.highwaySession.SigSession, Ext: ext, Encrypt: false, diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 2921d37e..bfb70c61 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -15,22 +15,13 @@ import ( "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/internal/proto" - "github.com/Mrs4s/MiraiGo/utils" ) type BdhInput struct { CommandID int32 - Body io.ReadSeeker - Ticket []byte - Ext []byte - Encrypt bool -} - -type BdhMultiThreadInput struct { - CommandID int32 - Body io.ReaderAt - Sum []byte - Size int64 + Body io.Reader + Sum []byte // md5 sum of body + Size int64 // body size Ticket []byte Ext []byte Encrypt bool @@ -46,24 +37,12 @@ func (bdh *BdhInput) encrypt(key []byte) error { return nil } -func (bdh *BdhMultiThreadInput) encrypt(key []byte) error { - if bdh.Encrypt { - if len(key) == 0 { - return errors.New("session key not found. maybe miss some packet?") - } - bdh.Ext = binary.NewTeaCipher(key).Encrypt(bdh.Ext) - } - return nil -} - func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { if len(s.SsoAddr) == 0 { return nil, errors.New("srv addrs not found. maybe miss some packet?") } addr := s.SsoAddr[0].String() - sum, length := utils.ComputeMd5AndLength(input.Body) - _, _ = input.Body.Seek(0, io.SeekStart) if err := input.encrypt(s.SessionKey); err != nil { return nil, err } @@ -95,12 +74,12 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ - Filesize: length, + Filesize: input.Size, Dataoffset: int64(offset), Datalength: int32(rl), Serviceticket: input.Ticket, Md5: ch[:], - FileMd5: sum, + FileMd5: input.Sum, }, ReqExtendinfo: input.Ext, }) @@ -127,17 +106,11 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { return rspExt, nil } -func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount int) ([]byte, error) { +func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, error) { // for small file and small thread count, // use UploadBDH instead of UploadBDHMultiThread if input.Size < 1024*1024*3 || threadCount < 2 { - return s.UploadBDH(BdhInput{ - CommandID: input.CommandID, - Body: io.NewSectionReader(input.Body, 0, input.Size), - Ticket: input.Ticket, - Ext: input.Ext, - Encrypt: input.Encrypt, - }) + return s.UploadBDH(input) } if len(s.SsoAddr) == 0 { @@ -149,36 +122,21 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in return nil, err } - type BlockMetaData struct { - Id int - Offset int64 - } const blockSize int64 = 1024 * 512 var ( - blocks []BlockMetaData - rspExt []byte - BlockId = ^uint32(0) // -1 - uploadedCount uint32 - cond = sync.NewCond(&sync.Mutex{}) + rspExt []byte + completedThread uint32 + cond = sync.NewCond(&sync.Mutex{}) + offset = int64(0) + count = (input.Size + blockSize - 1) / blockSize + id = 0 ) - // Init Blocks - { - var temp int64 = 0 - for temp+blockSize < input.Size { - blocks = append(blocks, BlockMetaData{ - Id: len(blocks), - Offset: temp, - }) - temp += blockSize - } - blocks = append(blocks, BlockMetaData{ - Id: len(blocks), - Offset: temp, - }) - } doUpload := func() error { // send signal complete uploading - defer cond.Signal() + defer func() { + atomic.AddUint32(&completedThread, 1) + cond.Signal() + }() conn, err := net.DialTimeout("tcp", addr, time.Second*20) if err != nil { @@ -192,22 +150,20 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in chunk := make([]byte, blockSize) for { - nextId := atomic.AddUint32(&BlockId, 1) - if nextId >= uint32(len(blocks)) { - break - } - block := blocks[nextId] - if block.Id == len(blocks)-1 { - cond.L.Lock() - for atomic.LoadUint32(&uploadedCount) != uint32(len(blocks))-1 { + cond.L.Lock() // lock protect reading + off := offset + offset += blockSize + id++ + if int64(id) == count { // last + for atomic.LoadUint32(&completedThread) != uint32(threadCount-1) { cond.Wait() } + } else if int64(id) > count { cond.L.Unlock() + break } chunk = chunk[:blockSize] - - cond.L.Lock() // lock protect reading - n, err := input.Body.ReadAt(chunk, block.Offset) + n, err := io.ReadFull(input.Body, chunk) cond.L.Unlock() if err != nil { @@ -225,7 +181,7 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ Filesize: input.Size, - Dataoffset: block.Offset, + Dataoffset: off, Datalength: int32(n), Serviceticket: input.Ticket, Md5: ch[:], @@ -248,7 +204,6 @@ func (s *Session) UploadBDHMultiThread(input BdhMultiThreadInput, threadCount in if rspHead.RspExtendinfo != nil { rspExt = rspHead.RspExtendinfo } - atomic.AddUint32(&uploadedCount, 1) } return nil } diff --git a/client/ptt.go b/client/ptt.go index 0d3e7fdf..17c4185a 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -1,6 +1,7 @@ package client import ( + "crypto/md5" "encoding/hex" "io" @@ -74,6 +75,8 @@ func (c *QQClient) UploadVoice(target message.Source, voice io.ReadSeeker) (*mes rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ CommandID: cmd, Body: voice, + Sum: fh, + Size: length, Ticket: c.highwaySession.SigSession, Ext: ext, Encrypt: false, @@ -120,14 +123,17 @@ func (c *QQClient) UploadVoice(target message.Source, voice io.ReadSeeker) (*mes // UploadShortVideo 将视频和封面上传到服务器, 返回 message.ShortVideoElement 可直接发送 // thread 上传线程数 func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadSeeker, thread int) (*message.ShortVideoElement, error) { - videoHash, videoLen := utils.ComputeMd5AndLength(video) - thumbHash, thumbLen := utils.ComputeMd5AndLength(thumb) + thumbHash := md5.New() + thumbLen, _ := io.Copy(thumbHash, thumb) + thumbSum := thumbHash.Sum(nil) + videoSum, videoLen := utils.ComputeMd5AndLength(io.TeeReader(video, thumbHash)) + sum := thumbHash.Sum(nil) - key := string(videoHash) + string(thumbHash) + key := string(sum) pttWaiter.Wait(key) defer pttWaiter.Done(key) - i, err := c.sendAndWait(c.buildPttGroupShortVideoUploadReqPacket(target, videoHash, thumbHash, videoLen, thumbLen)) + i, err := c.sendAndWait(c.buildPttGroupShortVideoUploadReqPacket(target, videoSum, thumbSum, videoLen, thumbLen)) if err != nil { return nil, errors.Wrap(err, "upload req error") } @@ -135,8 +141,8 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS videoElement := &message.ShortVideoElement{ Size: int32(videoLen), ThumbSize: int32(thumbLen), - Md5: videoHash, - ThumbMd5: thumbHash, + Md5: videoSum, + ThumbMd5: thumbSum, Guild: target.SourceType == message.SourceGuildChannel, } if rsp.FileExists == 1 { @@ -149,28 +155,20 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS if target.SourceType == message.SourceGuildChannel { cmd = 89 } - ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoHash, thumbHash, videoLen, thumbLen).PttShortVideoUploadReq) + ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoSum, thumbSum, videoLen, thumbLen).PttShortVideoUploadReq) + combined := utils.MultiReadSeeker(thumb, video) + input := highway.BdhInput{ + CommandID: cmd, + Body: combined, + Size: videoLen + thumbLen, + Sum: sum, + Ticket: c.highwaySession.SigSession, + Ext: ext, + Encrypt: true, + } if thread > 1 { - sum, _ := utils.ComputeMd5AndLength(utils.MultiReadSeeker(thumb, video)) - input := highway.BdhMultiThreadInput{ - CommandID: cmd, - Body: utils.ReaderAtFrom2ReadSeeker(thumb, video), - Size: videoLen + thumbLen, - Sum: sum, - Ticket: c.highwaySession.SigSession, - Ext: ext, - Encrypt: true, - } hwRsp, err = c.highwaySession.UploadBDHMultiThread(input, thread) } else { - multi := utils.MultiReadSeeker(thumb, video) - input := highway.BdhInput{ - CommandID: cmd, - Body: multi, - Ticket: c.highwaySession.SigSession, - Ext: ext, - Encrypt: true, - } hwRsp, err = c.highwaySession.UploadBDH(input) } if err != nil { From 76539e425db65173f901aa5a4c6dd96eab836ceb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Feb 2022 16:52:57 +0800 Subject: [PATCH 05/44] client: avoid ReadAll in image ocr --- client/image.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/client/image.go b/client/image.go index a0cf0b0f..51b45b76 100644 --- a/client/image.go +++ b/client/image.go @@ -1,8 +1,6 @@ package client import ( - "bytes" - "crypto/md5" "encoding/hex" "io" "math/rand" @@ -211,7 +209,7 @@ func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { case *message.GroupImageElement: url = e.Url if b, err := utils.HTTPGetReadCloser(e.Url, ""); err == nil { - if url, err = c.uploadOcrImage(b); err != nil { + if url, err = c.uploadOcrImage(b, e.Size, e.Md5); err != nil { url = e.Url } _ = b.Close() @@ -316,7 +314,7 @@ func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd return c.uniPacket("ImgStore.GroupPicDown", payload) } -func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) { +func (c *QQClient) uploadOcrImage(img io.Reader, size int32, sum []byte) (string, error) { r := make([]byte, 16) rand.Read(r) ext, _ := proto.Marshal(&highway2.CommFileExtReq{ @@ -324,13 +322,11 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) { Uuid: binary.GenUUID(r), }) - h := md5.New() - buf, _ := io.ReadAll(io.TeeReader(img, h)) rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ CommandID: 76, - Body: bytes.NewReader(buf), - Size: int64(len(buf)), - Sum: h.Sum(nil), + Body: img, + Size: int64(size), + Sum: sum, Ticket: c.highwaySession.SigSession, Ext: ext, Encrypt: false, From 9884d9b0de2077653aa867e7e9e3502465705c2c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Feb 2022 21:54:50 +0800 Subject: [PATCH 06/44] dep: update protobuf --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e265f018..3ca942d0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/MiraiGo go 1.17 require ( - github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 + github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 github.com/fumiama/imgsz v0.0.2 github.com/pierrec/lz4/v4 v4.1.11 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index dfef8f81..ce0a0e55 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= -github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= +github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 15a746802b1ede4cbd1a00a1046eaf573c270300 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 13:39:27 +0800 Subject: [PATCH 07/44] network,highway: move HeadBodyFrame to highway This code only used by highway --- client/internal/highway/bdh.go | 9 +++-- client/internal/{network => highway}/frame.go | 10 +++--- client/internal/highway/highway.go | 34 ++++++++----------- 3 files changed, 24 insertions(+), 29 deletions(-) rename client/internal/{network => highway}/frame.go (80%) diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index bfb70c61..3b1e8fc6 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -12,7 +12,6 @@ import ( "golang.org/x/sync/errgroup" "github.com/Mrs4s/MiraiGo/binary" - "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/internal/proto" ) @@ -72,7 +71,7 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ Filesize: input.Size, Dataoffset: int64(offset), @@ -84,7 +83,7 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { ReqExtendinfo: input.Ext, }) offset += rl - frame := network.HeadBodyFrame(head, chunk) + frame := newFrame(head, chunk) _, err = frame.WriteTo(conn) if err != nil { return nil, errors.Wrap(err, "write conn error") @@ -178,7 +177,7 @@ func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ Filesize: input.Size, Dataoffset: off, @@ -189,7 +188,7 @@ func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, }, ReqExtendinfo: input.Ext, }) - frame := network.HeadBodyFrame(head, chunk) + frame := newFrame(head, chunk) _, err = frame.WriteTo(conn) if err != nil { return errors.Wrap(err, "write conn error") diff --git a/client/internal/network/frame.go b/client/internal/highway/frame.go similarity index 80% rename from client/internal/network/frame.go rename to client/internal/highway/frame.go index 2b1b8f08..ba1b6b38 100644 --- a/client/internal/network/frame.go +++ b/client/internal/highway/frame.go @@ -1,4 +1,4 @@ -package network +package highway import ( "encoding/binary" @@ -7,15 +7,15 @@ import ( var etx = []byte{0x29} -// HeadBodyFrame 包格式 -// * STX +// newFrame 包格式 +// * STX: 0x28(40) // * head length // * body length // * head data // * body data -// * ETX +// * ETX: 0x29(41) // 节省内存, 可被go runtime优化为writev操作 -func HeadBodyFrame(head []byte, body []byte) net.Buffers { +func newFrame(head []byte, body []byte) net.Buffers { buffers := make(net.Buffers, 4) // buffer0 format: // * STX diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index d031bc6d..83023c8f 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -12,12 +12,17 @@ import ( "github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/binary" - "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/utils" ) +// see com/tencent/mobileqq/highway/utils/BaseConstants.java#L120-L121 +const ( + _REQ_CMD_DATA = "PicUp.DataUp" + _REQ_CMD_HEART_BREAK = "PicUp.Echo" +) + type Session struct { Uin string AppID int32 @@ -74,7 +79,7 @@ func (s *Session) Upload(addr Addr, input Input) error { } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ Filesize: length, Dataoffset: int64(offset), @@ -86,7 +91,7 @@ func (s *Session) Upload(addr Addr, input Input) error { ReqExtendinfo: []byte{}, }) offset += rl - frame := network.HeadBodyFrame(head, chunk) + frame := newFrame(head, chunk) _, err = frame.WriteTo(conn) if err != nil { return errors.Wrap(err, "write conn error") @@ -134,7 +139,7 @@ func (s *Session) UploadExciting(input ExcitingInput) ([]byte, error) { } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(0, input.CommandID, 0), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 0, input.CommandID, 0), MsgSeghead: &pb.SegHead{ Filesize: fileLength, Dataoffset: offset, @@ -146,7 +151,7 @@ func (s *Session) UploadExciting(input ExcitingInput) ([]byte, error) { ReqExtendinfo: input.Ext, }) offset += int64(rl) - frame := network.HeadBodyFrame(head, chunk) + frame := newFrame(head, chunk) req, _ := http.NewRequest("POST", url, &frame) req.Header.Set("Accept", "*/*") req.Header.Set("Connection", "Keep-Alive") @@ -182,33 +187,24 @@ func (s *Session) nextSeq() int32 { return atomic.AddInt32(&s.seq, 2) } -func (s *Session) dataHighwayHead(flag, cmd, locale int32) *pb.DataHighwayHead { +func (s *Session) dataHighwayHead(cmd string, flag, cmdID, locale int32) *pb.DataHighwayHead { return &pb.DataHighwayHead{ Version: 1, Uin: s.Uin, - Command: "PicUp.DataUp", + Command: cmd, Seq: s.nextSeq(), Appid: s.AppID, Dataflag: flag, - CommandId: cmd, + CommandId: cmdID, LocaleId: locale, } } func (s *Session) sendHeartbreak(conn net.Conn) error { head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: &pb.DataHighwayHead{ - Version: 1, - Uin: s.Uin, - Command: "PicUp.Echo", - Seq: s.nextSeq(), - Appid: s.AppID, - Dataflag: 4096, - CommandId: 0, - LocaleId: 2052, - }, + MsgBasehead: s.dataHighwayHead(_REQ_CMD_HEART_BREAK, 4096, 0, 2052), }) - frame := network.HeadBodyFrame(head, nil) + frame := newFrame(head, nil) _, err := frame.WriteTo(conn) return err } From 6bc03d6b8cb656f9800594ffe304baadc9fe28af Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 15:00:33 +0800 Subject: [PATCH 08/44] utils: port ttl cache to generic --- client/c2c_processor.go | 2 +- client/client.go | 12 ++++++------ client/decoders.go | 2 +- client/entities.go | 3 +++ client/online_push.go | 2 +- go.mod | 2 +- utils/ttl.go | 24 ++++++++++++------------ 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/client/c2c_processor.go b/client/c2c_processor.go index f71d2b71..08504880 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -83,7 +83,7 @@ func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info *network.IncomingPac c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid()) return } - c.msgSvcCache.Add(strKey, "", time.Hour) + c.msgSvcCache.Add(strKey, unit{}, time.Hour) if c.lastC2CMsgTime > int64(pMsg.Head.GetMsgTime()) && (c.lastC2CMsgTime-int64(pMsg.Head.GetMsgTime())) > 60*10 { c.Debug("c2c msg filtered by time. lastMsgTime: %v msgTime: %v", c.lastC2CMsgTime, pMsg.Head.GetMsgTime()) return diff --git a/client/client.go b/client/client.go index 12a8ce33..0ddc358d 100644 --- a/client/client.go +++ b/client/client.go @@ -77,12 +77,12 @@ type QQClient struct { // fileStorageInfo *jce.FileStoragePushFSSvcList // message state - msgSvcCache *utils.Cache + msgSvcCache *utils.Cache[unit] lastC2CMsgTime int64 - transCache *utils.Cache + transCache *utils.Cache[unit] groupSysMsgCache *GroupSystemMessages msgBuilders sync.Map - onlinePushCache *utils.Cache + onlinePushCache *utils.Cache[unit] heartbeatEnabled bool requestPacketRequestID atomic.Int32 groupSeq atomic.Int32 @@ -164,9 +164,9 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { sig: &auth.SigInfo{ OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B}, }, - msgSvcCache: utils.NewCache(time.Second * 15), - transCache: utils.NewCache(time.Second * 15), - onlinePushCache: utils.NewCache(time.Second * 15), + msgSvcCache: utils.NewCache[unit](time.Second * 15), + transCache: utils.NewCache[unit](time.Second * 15), + onlinePushCache: utils.NewCache[unit](time.Second * 15), servers: []*net.TCPAddr{}, alive: true, highwaySession: new(highway.Session), diff --git a/client/decoders.go b/client/decoders.go index 736014f5..35a17919 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -646,7 +646,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay if _, ok := c.transCache.Get(idStr); ok { return nil, nil } - c.transCache.Add(idStr, "", time.Second*15) + c.transCache.Add(idStr, unit{}, time.Second*15) if info.GetMsgType() == 34 { data.ReadInt32() data.ReadByte() diff --git a/client/entities.go b/client/entities.go index adf22a15..d2351b56 100644 --- a/client/entities.go +++ b/client/entities.go @@ -296,6 +296,9 @@ type ( SigSession []byte SessionKey []byte } + + // unit is an alias for struct{}, like `()` in rust + unit = struct{} ) const ( diff --git a/client/online_push.go b/client/online_push.go index 5a913b92..64723ca0 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -37,7 +37,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa if _, ok := c.onlinePushCache.Get(k); ok { continue } - c.onlinePushCache.Add(k, "", time.Second*30) + c.onlinePushCache.Add(k, unit{}, time.Second*30) // 0x2dc if m.MsgType == 732 { r := binary.NewReader(m.VMsg) diff --git a/go.mod b/go.mod index 3ca942d0..6e26ead4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Mrs4s/MiraiGo -go 1.17 +go 1.18 require ( github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 diff --git a/utils/ttl.go b/utils/ttl.go index 1b589960..2d27e79e 100644 --- a/utils/ttl.go +++ b/utils/ttl.go @@ -7,26 +7,26 @@ import ( // https://github.com/Konstantin8105/SimpleTTL // entry - typical element of cache -type entry struct { +type entry[T any] struct { expiry time.Time - value interface{} + value T } // Cache - simple implementation of cache // More information: https://en.wikipedia.org/wiki/Time_to_live -type Cache struct { +type Cache[T any] struct { lock sync.RWMutex - cache map[string]*entry + cache map[string]*entry[T] } // NewCache - initialization of new cache. // For avoid mistake - minimal time to live is 1 minute. // For simplification, - key is string and cache haven`t stop method -func NewCache(interval time.Duration) *Cache { +func NewCache[T any](interval time.Duration) *Cache[T] { if interval < time.Second { interval = time.Second } - cache := &Cache{cache: make(map[string]*entry)} + cache := &Cache[T]{cache: make(map[string]*entry[T])} go func() { ticker := time.NewTicker(interval) for { @@ -47,7 +47,7 @@ func NewCache(interval time.Duration) *Cache { } // Count - return amount element of TTL map. -func (cache *Cache) Count() int { +func (cache *Cache[_]) Count() int { cache.lock.RLock() defer cache.lock.RUnlock() @@ -55,7 +55,7 @@ func (cache *Cache) Count() int { } // Get - return value from cache -func (cache *Cache) Get(key string) (interface{}, bool) { +func (cache *Cache[_]) Get(key string) (interface{}, bool) { cache.lock.RLock() defer cache.lock.RUnlock() @@ -67,7 +67,7 @@ func (cache *Cache) Get(key string) (interface{}, bool) { return nil, false } -func (cache *Cache) GetAndUpdate(key string, ttl time.Duration) (interface{}, bool) { +func (cache *Cache[_]) GetAndUpdate(key string, ttl time.Duration) (interface{}, bool) { cache.lock.RLock() defer cache.lock.RUnlock() @@ -79,18 +79,18 @@ func (cache *Cache) GetAndUpdate(key string, ttl time.Duration) (interface{}, bo } // Add - add key/value in cache -func (cache *Cache) Add(key string, value interface{}, ttl time.Duration) { +func (cache *Cache[T]) Add(key string, value T, ttl time.Duration) { cache.lock.Lock() defer cache.lock.Unlock() - cache.cache[key] = &entry{ + cache.cache[key] = &entry[T]{ value: value, expiry: time.Now().Add(ttl), } } // GetKeys - return all keys of cache map -func (cache *Cache) GetKeys() []string { +func (cache *Cache[T]) GetKeys() []string { cache.lock.RLock() defer cache.lock.RUnlock() From 81fe9e85d0e2a59b9d1f7ee9839a36618b719466 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 15:02:17 +0800 Subject: [PATCH 09/44] ci: enable ci check --- .github/workflows/go.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index cc3fc35d..ff0d611a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,9 +2,9 @@ name: Go on: push: - branches: [ master ] + branches: [ master, typeparam ] pull_request: - branches: [ master ] + branches: [ master, typeparam ] jobs: @@ -16,7 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.13 + go-version: 1.18rc1 - name: Check out code into the Go module directory uses: actions/checkout@v2 From d900412d415ee2a7ea537d92813bd314a85c3389 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 15:04:12 +0800 Subject: [PATCH 10/44] ci: adjust go toolchain version --- .github/workflows/go.yml | 2 +- utils/ttl.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ff0d611a..9ad567f2 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: 1.18rc1 + go-version: 1.18.0-rc1 - name: Check out code into the Go module directory uses: actions/checkout@v2 diff --git a/utils/ttl.go b/utils/ttl.go index 2d27e79e..6b43ca24 100644 --- a/utils/ttl.go +++ b/utils/ttl.go @@ -90,7 +90,7 @@ func (cache *Cache[T]) Add(key string, value T, ttl time.Duration) { } // GetKeys - return all keys of cache map -func (cache *Cache[T]) GetKeys() []string { +func (cache *Cache[_]) GetKeys() []string { cache.lock.RLock() defer cache.lock.RUnlock() From ab2ee601329c665d3cb935a68c16b0b6b6d1ea66 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 15:05:23 +0800 Subject: [PATCH 11/44] ci: use unstable toolchain --- .github/workflows/go.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9ad567f2..8083ec49 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,6 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: + stable: false go-version: 1.18.0-rc1 - name: Check out code into the Go module directory From d9f803837f49cd4271595b01bd031c3cf9c6f298 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 16:20:18 +0800 Subject: [PATCH 12/44] client: use generic event handler --- client/c2c_processor.go | 14 +- client/client.go | 31 ++- client/decoders.go | 20 +- client/events.go | 460 +++------------------------------------- client/group_msg.go | 12 +- client/network.go | 16 +- client/notify.go | 6 +- client/online_push.go | 20 +- client/private_msg.go | 2 +- client/sync.go | 4 +- client/system_msg.go | 4 +- utils/ttl.go | 9 +- 12 files changed, 116 insertions(+), 482 deletions(-) diff --git a/client/c2c_processor.go b/client/c2c_processor.go index 08504880..8b44f8a7 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -132,10 +132,10 @@ func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *network.IncomingPa } if pMsg.Head.GetFromUin() == c.Uin { - c.dispatchPrivateMessageSelf(c.parsePrivateMessage(pMsg)) + c.SelfPrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg)) return } - c.dispatchPrivateMessage(c.parsePrivateMessage(pMsg)) + c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg)) default: c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd()) } @@ -149,7 +149,7 @@ func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ *network.IncomingPacket // m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1) // T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct } - c.dispatchPrivateMessage(c.parsePrivateMessage(pMsg)) + c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg)) } func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *network.IncomingPacketInfo) { @@ -206,7 +206,7 @@ func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *network.IncomingPacke if pMsg.Head.GetFromUin() == c.Uin { return } - c.dispatchTempMessage(&TempMessageEvent{ + c.TempMessageEvent.dispatch(c, &TempMessageEvent{ Message: c.parseTempMessage(pMsg), Session: session, }) @@ -219,7 +219,7 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *network.I group := c.FindGroupByUin(pMsg.Head.GetFromUin()) if pMsg.Head.GetAuthUin() == c.Uin { if group == nil && c.ReloadGroupList() == nil { - c.dispatchJoinGroupEvent(c.FindGroupByUin(pMsg.Head.GetFromUin())) + c.GroupJoinEvent.dispatch(c, c.FindGroupByUin(pMsg.Head.GetFromUin())) } } else { if group != nil && group.FindMember(pMsg.Head.GetAuthUin()) == nil { @@ -232,7 +232,7 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *network.I info.Members = append(info.Members, mem) info.sort() }) - c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ + c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{ Group: group, Member: mem, }) @@ -275,7 +275,7 @@ func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info *network.IncomingP if err != nil { return } - c.dispatchOfflineFileEvent(&OfflineFileEvent{ + c.OfflineFileEvent.dispatch(c, &OfflineFileEvent{ FileName: string(sub4.NotOnlineFile.FileName), FileSize: sub4.NotOnlineFile.GetFileSize(), Sender: pMsg.Head.GetFromUin(), diff --git a/client/client.go b/client/client.go index 0ddc358d..e5fe70d0 100644 --- a/client/client.go +++ b/client/client.go @@ -19,6 +19,7 @@ import ( "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/oicq" "github.com/Mrs4s/MiraiGo/client/pb/msg" + "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" ) @@ -76,6 +77,35 @@ type QQClient struct { // otherSrvAddrs []string // fileStorageInfo *jce.FileStoragePushFSSvcList + // event handles + eventHandlers eventHandlers + PrivateMessageEvent EventHandle[*message.PrivateMessage] + TempMessageEvent EventHandle[*TempMessageEvent] + GroupMessageEvent EventHandle[*message.GroupMessage] + SelfPrivateMessageEvent EventHandle[*message.PrivateMessage] + SelfGroupMessageEvent EventHandle[*message.GroupMessage] + GroupMuteEvent EventHandle[*GroupMuteEvent] + GroupMessageRecalledEvent EventHandle[*GroupMessageRecalledEvent] + FriendMessageRecalledEvent EventHandle[*FriendMessageRecalledEvent] + GroupJoinEvent EventHandle[*GroupInfo] + GroupLeaveEvent EventHandle[*GroupLeaveEvent] + GroupMemberJoinEvent EventHandle[*MemberJoinGroupEvent] + GroupMemberLeaveEvent EventHandle[*MemberLeaveGroupEvent] + MemberCardUpdatedEvent EventHandle[*MemberCardUpdatedEvent] + GroupNameUpdatedEvent EventHandle[*GroupNameUpdatedEvent] + GroupMemberPermissionChangedEvent EventHandle[*MemberPermissionChangedEvent] + GroupInvitedEvent EventHandle[*GroupInvitedRequest] + UserWantJoinGroupEvent EventHandle[*UserJoinGroupRequest] + NewFriendEvent EventHandle[*NewFriendEvent] + NewFriendRequestEvent EventHandle[*NewFriendRequest] + DisconnectedEvent EventHandle[*ClientDisconnectedEvent] + GroupNotifyEvent EventHandle[INotifyEvent] + FriendNotifyEvent EventHandle[INotifyEvent] + MemberSpecialTitleUpdatedEvent EventHandle[*MemberSpecialTitleUpdatedEvent] + GroupDigestEvent EventHandle[*GroupDigestEvent] + OtherClientStatusChangedEvent EventHandle[*OtherClientStatusChangedEvent] + OfflineFileEvent EventHandle[*OfflineFileEvent] + // message state msgSvcCache *utils.Cache[unit] lastC2CMsgTime int64 @@ -88,7 +118,6 @@ type QQClient struct { groupSeq atomic.Int32 friendSeq atomic.Int32 highwayApplyUpSeq atomic.Int32 - eventHandlers eventHandlers groupListLock sync.Mutex } diff --git a/client/decoders.go b/client/decoders.go index 35a17919..5c180f6e 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -659,10 +659,10 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay switch typ { case 0x02: if target == c.Uin { - c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) + c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g}) } else if m := g.FindMember(target); m != nil { g.removeMember(target) - c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ + c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{ Group: g, Member: m, }) @@ -672,13 +672,13 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay return nil, err } if target == c.Uin { - c.dispatchLeaveGroupEvent(&GroupLeaveEvent{ + c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{ Group: g, Operator: g.FindMember(operator), }) } else if m := g.FindMember(target); m != nil { g.removeMember(target) - c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ + c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{ Group: g, Member: m, Operator: g.FindMember(operator), @@ -687,7 +687,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay case 0x82: if m := g.FindMember(target); m != nil { g.removeMember(target) - c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ + c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{ Group: g, Member: m, }) @@ -695,7 +695,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay case 0x83: if m := g.FindMember(target); m != nil { g.removeMember(target) - c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ + c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{ Group: g, Member: m, Operator: g.FindMember(operator), @@ -724,7 +724,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay if mem.Permission != newPermission { old := mem.Permission mem.Permission = newPermission - c.dispatchPermissionChanged(&MemberPermissionChangedEvent{ + c.GroupMemberPermissionChangedEvent.dispatch(c, &MemberPermissionChangedEvent{ Group: g, Member: mem, OldPermission: old, @@ -748,7 +748,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, pay } st := rsp.Friendmsgs[0] if st.Msg != nil { - c.dispatchNewFriendRequest(&NewFriendRequest{ + c.NewFriendRequestEvent.dispatch(c, &NewFriendRequest{ RequestId: st.MsgSeq, Message: st.Msg.MsgAdditional, RequesterUin: st.ReqUin, @@ -768,7 +768,7 @@ func decodeForceOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, payloa r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:]) tips := r.ReadString(2) c.Disconnect() - go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: tips}) + go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: tips}) return nil, nil } @@ -777,7 +777,7 @@ func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte // c.lastLostMsg = "服务器端强制下线." c.Disconnect() // 这个decoder不能消耗太多时间, event另起线程处理 - go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "服务端强制下线."}) + go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "服务端强制下线."}) return nil, nil } diff --git a/client/events.go b/client/events.go index 0a3634f7..3cc55ac7 100644 --- a/client/events.go +++ b/client/events.go @@ -8,12 +8,36 @@ import ( "github.com/Mrs4s/MiraiGo/message" ) +// protected all EventHandle, since write is very rare, use +// only one lock to save memory +var eventMu sync.RWMutex + +type EventHandle[T any] struct { + // QQClient? + handlers []func(client *QQClient, event T) +} + +func (handle *EventHandle[T]) Subscribe(handler func(client *QQClient, event T)) { + eventMu.Lock() + defer eventMu.Unlock() + handle.handlers = append(handle.handlers, handler) +} + +func (handle *EventHandle[T]) dispatch(client *QQClient, event T) { + eventMu.RLock() + defer eventMu.RUnlock() + defer func() { + if pan := recover(); pan != nil { + fmt.Printf("event error: %v\n%s", pan, debug.Stack()) + } + }() + for _, handler := range handle.handlers { + handler(client, event) + } +} + type eventHandlers struct { - privateMessageHandlers []func(*QQClient, *message.PrivateMessage) - tempMessageHandlers []func(*QQClient, *TempMessageEvent) - groupMessageHandlers []func(*QQClient, *message.GroupMessage) - selfPrivateMessageHandlers []func(*QQClient, *message.PrivateMessage) - selfGroupMessageHandlers []func(*QQClient, *message.GroupMessage) + // todo: move to event handle guildChannelMessageHandlers []func(*QQClient, *message.GuildChannelMessage) guildMessageReactionsUpdatedHandlers []func(*QQClient, *GuildMessageReactionsUpdatedEvent) guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent) @@ -21,58 +45,11 @@ type eventHandlers struct { guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent) guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent) memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent) - groupMuteEventHandlers []func(*QQClient, *GroupMuteEvent) - groupRecalledHandlers []func(*QQClient, *GroupMessageRecalledEvent) - friendRecalledHandlers []func(*QQClient, *FriendMessageRecalledEvent) - joinGroupHandlers []func(*QQClient, *GroupInfo) - leaveGroupHandlers []func(*QQClient, *GroupLeaveEvent) - memberJoinedHandlers []func(*QQClient, *MemberJoinGroupEvent) - memberLeavedHandlers []func(*QQClient, *MemberLeaveGroupEvent) - memberCardUpdatedHandlers []func(*QQClient, *MemberCardUpdatedEvent) - groupNameUpdatedHandlers []func(*QQClient, *GroupNameUpdatedEvent) - permissionChangedHandlers []func(*QQClient, *MemberPermissionChangedEvent) - groupInvitedHandlers []func(*QQClient, *GroupInvitedRequest) - joinRequestHandlers []func(*QQClient, *UserJoinGroupRequest) - friendRequestHandlers []func(*QQClient, *NewFriendRequest) - newFriendHandlers []func(*QQClient, *NewFriendEvent) - disconnectHandlers []func(*QQClient, *ClientDisconnectedEvent) - logHandlers []func(*QQClient, *LogEvent) - serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool - groupNotifyHandlers []func(*QQClient, INotifyEvent) - friendNotifyHandlers []func(*QQClient, INotifyEvent) - memberTitleUpdatedHandlers []func(*QQClient, *MemberSpecialTitleUpdatedEvent) - offlineFileHandlers []func(*QQClient, *OfflineFileEvent) - otherClientStatusChangedHandlers []func(*QQClient, *OtherClientStatusChangedEvent) - groupDigestHandlers []func(*QQClient, *GroupDigestEvent) - groupMessageReceiptHandlers sync.Map -} -func (c *QQClient) OnPrivateMessage(f func(*QQClient, *message.PrivateMessage)) { - c.eventHandlers.privateMessageHandlers = append(c.eventHandlers.privateMessageHandlers, f) -} - -func (c *QQClient) OnPrivateMessageF(filter func(*message.PrivateMessage) bool, f func(*QQClient, *message.PrivateMessage)) { - c.OnPrivateMessage(func(client *QQClient, msg *message.PrivateMessage) { - if filter(msg) { - f(client, msg) - } - }) -} - -func (c *QQClient) OnTempMessage(f func(*QQClient, *TempMessageEvent)) { - c.eventHandlers.tempMessageHandlers = append(c.eventHandlers.tempMessageHandlers, f) -} - -func (c *QQClient) OnGroupMessage(f func(*QQClient, *message.GroupMessage)) { - c.eventHandlers.groupMessageHandlers = append(c.eventHandlers.groupMessageHandlers, f) -} - -func (c *QQClient) OnSelfPrivateMessage(f func(*QQClient, *message.PrivateMessage)) { - c.eventHandlers.selfPrivateMessageHandlers = append(c.eventHandlers.selfPrivateMessageHandlers, f) -} - -func (c *QQClient) OnSelfGroupMessage(f func(*QQClient, *message.GroupMessage)) { - c.eventHandlers.selfGroupMessageHandlers = append(c.eventHandlers.selfGroupMessageHandlers, f) + // consider Logger interface? + logHandlers []func(*QQClient, *LogEvent) + serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool + groupMessageReceiptHandlers sync.Map } func (s *GuildService) OnGuildChannelMessage(f func(*QQClient, *message.GuildChannelMessage)) { @@ -103,99 +80,14 @@ func (s *GuildService) OnMemberJoinedGuild(f func(*QQClient, *MemberJoinGuildEve s.c.eventHandlers.memberJoinedGuildHandlers = append(s.c.eventHandlers.memberJoinedGuildHandlers, f) } -func (c *QQClient) OnGroupMuted(f func(*QQClient, *GroupMuteEvent)) { - c.eventHandlers.groupMuteEventHandlers = append(c.eventHandlers.groupMuteEventHandlers, f) -} - -func (c *QQClient) OnJoinGroup(f func(*QQClient, *GroupInfo)) { - c.eventHandlers.joinGroupHandlers = append(c.eventHandlers.joinGroupHandlers, f) -} - -func (c *QQClient) OnLeaveGroup(f func(*QQClient, *GroupLeaveEvent)) { - c.eventHandlers.leaveGroupHandlers = append(c.eventHandlers.leaveGroupHandlers, f) -} - -func (c *QQClient) OnGroupMemberJoined(f func(*QQClient, *MemberJoinGroupEvent)) { - c.eventHandlers.memberJoinedHandlers = append(c.eventHandlers.memberJoinedHandlers, f) -} - -func (c *QQClient) OnGroupMemberLeaved(f func(*QQClient, *MemberLeaveGroupEvent)) { - c.eventHandlers.memberLeavedHandlers = append(c.eventHandlers.memberLeavedHandlers, f) -} - -func (c *QQClient) OnGroupMemberCardUpdated(f func(*QQClient, *MemberCardUpdatedEvent)) { - c.eventHandlers.memberCardUpdatedHandlers = append(c.eventHandlers.memberCardUpdatedHandlers, f) -} - -func (c *QQClient) OnGroupNameUpdated(f func(*QQClient, *GroupNameUpdatedEvent)) { - c.eventHandlers.groupNameUpdatedHandlers = append(c.eventHandlers.groupNameUpdatedHandlers, f) -} - -func (c *QQClient) OnGroupMemberPermissionChanged(f func(*QQClient, *MemberPermissionChangedEvent)) { - c.eventHandlers.permissionChangedHandlers = append(c.eventHandlers.permissionChangedHandlers, f) -} - -func (c *QQClient) OnGroupMessageRecalled(f func(*QQClient, *GroupMessageRecalledEvent)) { - c.eventHandlers.groupRecalledHandlers = append(c.eventHandlers.groupRecalledHandlers, f) -} - -func (c *QQClient) OnFriendMessageRecalled(f func(*QQClient, *FriendMessageRecalledEvent)) { - c.eventHandlers.friendRecalledHandlers = append(c.eventHandlers.friendRecalledHandlers, f) -} - -func (c *QQClient) OnGroupInvited(f func(*QQClient, *GroupInvitedRequest)) { - c.eventHandlers.groupInvitedHandlers = append(c.eventHandlers.groupInvitedHandlers, f) -} - -func (c *QQClient) OnUserWantJoinGroup(f func(*QQClient, *UserJoinGroupRequest)) { - c.eventHandlers.joinRequestHandlers = append(c.eventHandlers.joinRequestHandlers, f) -} - -func (c *QQClient) OnNewFriendRequest(f func(*QQClient, *NewFriendRequest)) { - c.eventHandlers.friendRequestHandlers = append(c.eventHandlers.friendRequestHandlers, f) -} - -func (c *QQClient) OnNewFriendAdded(f func(*QQClient, *NewFriendEvent)) { - c.eventHandlers.newFriendHandlers = append(c.eventHandlers.newFriendHandlers, f) -} - -func (c *QQClient) OnDisconnected(f func(*QQClient, *ClientDisconnectedEvent)) { - c.eventHandlers.disconnectHandlers = append(c.eventHandlers.disconnectHandlers, f) -} - func (c *QQClient) OnServerUpdated(f func(*QQClient, *ServerUpdatedEvent) bool) { c.eventHandlers.serverUpdatedHandlers = append(c.eventHandlers.serverUpdatedHandlers, f) } -func (c *QQClient) OnReceivedOfflineFile(f func(*QQClient, *OfflineFileEvent)) { - c.eventHandlers.offlineFileHandlers = append(c.eventHandlers.offlineFileHandlers, f) -} - -func (c *QQClient) OnOtherClientStatusChanged(f func(*QQClient, *OtherClientStatusChangedEvent)) { - c.eventHandlers.otherClientStatusChangedHandlers = append(c.eventHandlers.otherClientStatusChangedHandlers, f) -} - func (c *QQClient) OnLog(f func(*QQClient, *LogEvent)) { c.eventHandlers.logHandlers = append(c.eventHandlers.logHandlers, f) } -func (c *QQClient) OnGroupNotify(f func(*QQClient, INotifyEvent)) { - c.eventHandlers.groupNotifyHandlers = append(c.eventHandlers.groupNotifyHandlers, f) -} - -func (c *QQClient) OnFriendNotify(f func(*QQClient, INotifyEvent)) { - c.eventHandlers.friendNotifyHandlers = append(c.eventHandlers.friendNotifyHandlers, f) -} - -func (c *QQClient) OnMemberSpecialTitleUpdated(f func(*QQClient, *MemberSpecialTitleUpdatedEvent)) { - c.eventHandlers.memberTitleUpdatedHandlers = append(c.eventHandlers.memberTitleUpdatedHandlers, f) -} - -// OnGroupDigest 群精华消息事件注册 -func (c *QQClient) OnGroupDigest(f func(*QQClient, *GroupDigestEvent)) { - c.eventHandlers.groupDigestHandlers = append(c.eventHandlers.groupDigestHandlers, f) -} - func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool { return func(msg *message.PrivateMessage) bool { return msg.Sender.Uin == uin @@ -210,61 +102,6 @@ func (c *QQClient) onGroupMessageReceipt(id string, f ...func(*QQClient, *groupM c.eventHandlers.groupMessageReceiptHandlers.LoadOrStore(id, f[0]) } -func (c *QQClient) dispatchPrivateMessage(msg *message.PrivateMessage) { - if msg == nil { - return - } - for _, f := range c.eventHandlers.privateMessageHandlers { - cover(func() { - f(c, msg) - }) - } -} - -func (c *QQClient) dispatchTempMessage(e *TempMessageEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.tempMessageHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchGroupMessage(msg *message.GroupMessage) { - if msg == nil { - return - } - for _, f := range c.eventHandlers.groupMessageHandlers { - cover(func() { - f(c, msg) - }) - } -} - -func (c *QQClient) dispatchPrivateMessageSelf(msg *message.PrivateMessage) { - if msg == nil { - return - } - for _, f := range c.eventHandlers.selfPrivateMessageHandlers { - cover(func() { - f(c, msg) - }) - } -} - -func (c *QQClient) dispatchGroupMessageSelf(msg *message.GroupMessage) { - if msg == nil { - return - } - for _, f := range c.eventHandlers.selfGroupMessageHandlers { - cover(func() { - f(c, msg) - }) - } -} - func (c *QQClient) dispatchGuildChannelMessage(msg *message.GuildChannelMessage) { if msg == nil { return @@ -342,116 +179,6 @@ func (c *QQClient) dispatchMemberJoinedGuildEvent(e *MemberJoinGuildEvent) { } } -func (c *QQClient) dispatchGroupMuteEvent(e *GroupMuteEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupMuteEventHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchGroupMessageRecalledEvent(e *GroupMessageRecalledEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupRecalledHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchFriendMessageRecalledEvent(e *FriendMessageRecalledEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.friendRecalledHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchJoinGroupEvent(group *GroupInfo) { - if group == nil { - return - } - for _, f := range c.eventHandlers.joinGroupHandlers { - cover(func() { - f(c, group) - }) - } -} - -func (c *QQClient) dispatchLeaveGroupEvent(e *GroupLeaveEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.leaveGroupHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchNewMemberEvent(e *MemberJoinGroupEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.memberJoinedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchMemberLeaveEvent(e *MemberLeaveGroupEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.memberLeavedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchMemberCardUpdatedEvent(e *MemberCardUpdatedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.memberCardUpdatedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchGroupNameUpdatedEvent(e *GroupNameUpdatedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupNameUpdatedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchPermissionChanged(e *MemberPermissionChangedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.permissionChangedHandlers { - cover(func() { - f(c, e) - }) - } -} - func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) { c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f interface{}) bool { go f.(func(*QQClient, *groupMessageReceiptEvent))(c, e) @@ -459,127 +186,6 @@ func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) }) } -func (c *QQClient) dispatchGroupInvitedEvent(e *GroupInvitedRequest) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupInvitedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchJoinGroupRequest(r *UserJoinGroupRequest) { - if r == nil { - return - } - for _, f := range c.eventHandlers.joinRequestHandlers { - cover(func() { - f(c, r) - }) - } -} - -func (c *QQClient) dispatchNewFriendRequest(r *NewFriendRequest) { - if r == nil { - return - } - for _, f := range c.eventHandlers.friendRequestHandlers { - cover(func() { - f(c, r) - }) - } -} - -func (c *QQClient) dispatchNewFriendEvent(e *NewFriendEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.newFriendHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchGroupNotifyEvent(e INotifyEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupNotifyHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchFriendNotifyEvent(e INotifyEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.friendNotifyHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchMemberSpecialTitleUpdateEvent(e *MemberSpecialTitleUpdatedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.memberTitleUpdatedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchDisconnectEvent(e *ClientDisconnectedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.disconnectHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchOfflineFileEvent(e *OfflineFileEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.offlineFileHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchOtherClientStatusChangedEvent(e *OtherClientStatusChangedEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.otherClientStatusChangedHandlers { - cover(func() { - f(c, e) - }) - } -} - -func (c *QQClient) dispatchGroupDigestEvent(e *GroupDigestEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.groupDigestHandlers { - cover(func() { - f(c, e) - }) - } -} - func (c *QQClient) dispatchLogEvent(e *LogEvent) { if e == nil { return diff --git a/client/group_msg.go b/client/group_msg.go index 56fd3f72..45960e09 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -317,17 +317,17 @@ func decodeGroupMessagePacket(c *QQClient, _ *network.IncomingPacketInfo, payloa if builder.len() >= pkt.Message.Content.GetPkgNum() { c.msgBuilders.Delete(seq) if pkt.Message.Head.GetFromUin() == c.Uin { - c.dispatchGroupMessageSelf(c.parseGroupMessage(builder.build())) + c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build())) } else { - c.dispatchGroupMessage(c.parseGroupMessage(builder.build())) + c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build())) } } return nil, nil } if pkt.Message.Head.GetFromUin() == c.Uin { - c.dispatchGroupMessageSelf(c.parseGroupMessage(pkt.Message)) + c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message)) } else { - c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message)) + c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message)) } return nil, nil } @@ -463,7 +463,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { mem = info group.Members = append(group.Members, mem) group.sort() - go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ + go c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{ Group: group, Member: info, }) @@ -531,7 +531,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { mem.CardName = groupCard } if old != mem.CardName { - go c.dispatchMemberCardUpdatedEvent(&MemberCardUpdatedEvent{ + c.MemberCardUpdatedEvent.dispatch(c, &MemberCardUpdatedEvent{ Group: group, OldCard: old, Member: mem, diff --git a/client/network.go b/client/network.go index d5cbf229..0554c816 100644 --- a/client/network.go +++ b/client/network.go @@ -102,15 +102,15 @@ func (c *QQClient) connect() error { return err } c.once.Do(func() { - c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) { + c.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) { c.stat.MessageReceived.Add(1) c.stat.LastMessageTime.Store(time.Now().Unix()) }) - c.OnPrivateMessage(func(_ *QQClient, _ *message.PrivateMessage) { + c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) { c.stat.MessageReceived.Add(1) c.stat.LastMessageTime.Store(time.Now().Unix()) }) - c.OnTempMessage(func(_ *QQClient, _ *TempMessageEvent) { + c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) { c.stat.MessageReceived.Add(1) c.stat.LastMessageTime.Store(time.Now().Unix()) }) @@ -130,13 +130,13 @@ func (c *QQClient) quickReconnect() { time.Sleep(time.Millisecond * 200) if err := c.connect(); err != nil { c.Error("connect server error: %v", err) - c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "quick reconnect failed"}) + c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"}) return } if err := c.registerClient(); err != nil { c.Error("register client failed: %v", err) c.Disconnect() - c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"}) + c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) return } } @@ -263,13 +263,13 @@ func (c *QQClient) unexpectedDisconnect(_ *network.TCPListener, e error) { c.Online.Store(false) if err := c.connect(); err != nil { c.Error("connect server error: %v", err) - c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "connection dropped by server."}) + c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."}) return } if err := c.registerClient(); err != nil { c.Error("register client failed: %v", err) c.Disconnect() - c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"}) + c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) return } } @@ -298,7 +298,7 @@ func (c *QQClient) netLoop() { c.Error("parse incoming packet error: %v", err) if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) { c.Disconnect() - go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"}) + go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"}) continue } errCount++ diff --git a/client/notify.go b/client/notify.go index 634f88cb..c9cda9e5 100644 --- a/client/notify.go +++ b/client/notify.go @@ -62,7 +62,7 @@ func (c *QQClient) grayTipProcessor(groupCode int64, tipInfo *notify.GeneralGray } } if sender != 0 { - c.dispatchGroupNotifyEvent(&GroupPokeNotifyEvent{ + c.GroupNotifyEvent.dispatch(c, &GroupPokeNotifyEvent{ GroupCode: groupCode, Sender: sender, Receiver: receiver, @@ -81,7 +81,7 @@ func (c *QQClient) grayTipProcessor(groupCode int64, tipInfo *notify.GeneralGray uin, _ = strconv.ParseInt(templ.Value, 10, 64) } } - c.dispatchGroupNotifyEvent(&MemberHonorChangedNotifyEvent{ + c.GroupNotifyEvent.dispatch(c, &MemberHonorChangedNotifyEvent{ GroupCode: groupCode, Honor: func() HonorType { switch tipInfo.TemplId { @@ -145,7 +145,7 @@ func (c *QQClient) msgGrayTipProcessor(groupCode int64, tipInfo *notify.AIOGrayT if mem := c.FindGroup(groupCode).FindMember(event.Uin); mem != nil { mem.SpecialTitle = event.NewTitle } - c.dispatchMemberSpecialTitleUpdateEvent(event) + c.MemberSpecialTitleUpdatedEvent.dispatch(c, event) } } diff --git a/client/online_push.go b/client/online_push.go index 64723ca0..f0f397d4 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -53,7 +53,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa r.ReadBytes(6) target := int64(uint32(r.ReadInt32())) t := r.ReadInt32() - c.dispatchGroupMuteEvent(&GroupMuteEvent{ + c.GroupMuteEvent.dispatch(c, &GroupMuteEvent{ GroupCode: groupCode, OperatorUin: operator, TargetUin: target, @@ -68,7 +68,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa if rm.MsgType == 2 { continue } - c.dispatchGroupMessageRecalledEvent(&GroupMessageRecalledEvent{ + c.GroupMessageRecalledEvent.dispatch(c, &GroupMessageRecalledEvent{ GroupCode: groupCode, OperatorUin: b.OptMsgRecall.Uin, AuthorUin: rm.AuthorUin, @@ -82,7 +82,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa } if b.OptMsgRedTips != nil { if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示 - c.dispatchGroupNotifyEvent(&GroupRedBagLuckyKingNotifyEvent{ + c.GroupNotifyEvent.dispatch(c, &GroupRedBagLuckyKingNotifyEvent{ GroupCode: groupCode, Sender: int64(b.OptMsgRedTips.SenderUin), LuckyKing: int64(b.OptMsgRedTips.LuckyUin), @@ -91,7 +91,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa } if b.QqGroupDigestMsg != nil { digest := b.QqGroupDigestMsg - c.dispatchGroupDigestEvent(&GroupDigestEvent{ + c.GroupDigestEvent.dispatch(c, &GroupDigestEvent{ GroupCode: int64(digest.GroupCode), MessageID: int32(digest.Seq), InternalMessageID: int32(digest.Random), @@ -132,7 +132,7 @@ func msgType0x210Sub8ADecoder(c *QQClient, protobuf []byte) error { } for _, m := range s8a.MsgInfo { if m.ToUin == c.Uin { - c.dispatchFriendMessageRecalledEvent(&FriendMessageRecalledEvent{ + c.FriendMessageRecalledEvent.dispatch(c, &FriendMessageRecalledEvent{ FriendUin: m.FromUin, MessageId: m.MsgSeq, Time: m.MsgTime, @@ -152,7 +152,7 @@ func msgType0x210SubB3Decoder(c *QQClient, protobuf []byte) error { Nickname: b3.MsgAddFrdNotify.Nick, } c.FriendList = append(c.FriendList, frd) - c.dispatchNewFriendEvent(&NewFriendEvent{Friend: frd}) + c.NewFriendEvent.dispatch(c, &NewFriendEvent{Friend: frd}) return nil } @@ -167,7 +167,7 @@ func msgType0x210SubD4Decoder(c *QQClient, protobuf []byte) error { groupLeaveLock.Unlock() return err } - c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) + c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g}) } groupLeaveLock.Unlock() return nil @@ -185,7 +185,7 @@ func msgType0x210Sub27Decoder(c *QQClient, protobuf []byte) error { if g := c.FindGroup(int64(m.ModGroupProfile.GetGroupCode())); g != nil { old := g.Name g.Name = string(info.Value) - c.dispatchGroupNameUpdatedEvent(&GroupNameUpdatedEvent{ + c.GroupNameUpdatedEvent.dispatch(c, &GroupNameUpdatedEvent{ Group: g, OldName: old, NewName: g.Name, @@ -221,7 +221,7 @@ func msgType0x210Sub122Decoder(c *QQClient, protobuf []byte) error { if sender == 0 { return nil } - c.dispatchFriendNotifyEvent(&FriendPokeNotifyEvent{ + c.FriendNotifyEvent.dispatch(c, &FriendPokeNotifyEvent{ Sender: sender, Receiver: receiver, }) @@ -257,7 +257,7 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error { group.Members = newMem for _, m := range newMem { if lastJoinTime < m.JoinTime { - go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ + c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{ Group: group, Member: m, }) diff --git a/client/private_msg.go b/client/private_msg.go index be1d1b66..7825f51b 100644 --- a/client/private_msg.go +++ b/client/private_msg.go @@ -59,7 +59,7 @@ func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) * }, Elements: m.Elements, } - go c.dispatchPrivateMessageSelf(ret) + go c.PrivateMessageEvent.dispatch(c, ret) return ret } diff --git a/client/sync.go b/client/sync.go index cdb48647..3a182114 100644 --- a/client/sync.go +++ b/client/sync.go @@ -443,7 +443,7 @@ func decodeLoginNotifyPacket(c *QQClient, _ *network.IncomingPacketInfo, payload t := ac if ac.AppId == notify.AppId { c.OnlineClients = append(c.OnlineClients, t) - c.dispatchOtherClientStatusChangedEvent(&OtherClientStatusChangedEvent{ + c.OtherClientStatusChangedEvent.dispatch(c, &OtherClientStatusChangedEvent{ Client: t, Online: true, }) @@ -462,7 +462,7 @@ func decodeLoginNotifyPacket(c *QQClient, _ *network.IncomingPacketInfo, payload if rmi != -1 { rmc := c.OnlineClients[rmi] c.OnlineClients = append(c.OnlineClients[:rmi], c.OnlineClients[rmi+1:]...) - c.dispatchOtherClientStatusChangedEvent(&OtherClientStatusChangedEvent{ + c.OtherClientStatusChangedEvent.dispatch(c, &OtherClientStatusChangedEvent{ Client: rmc, Online: false, }) diff --git a/client/system_msg.go b/client/system_msg.go index e6dbfdd6..e3a36255 100644 --- a/client/system_msg.go +++ b/client/system_msg.go @@ -86,12 +86,12 @@ func (c *QQClient) exceptAndDispatchGroupSysMsg() { } for _, msg := range msgs.JoinRequests { if !joinExists(msg.RequestId) { - c.dispatchJoinGroupRequest(msg) + c.UserWantJoinGroupEvent.dispatch(c, msg) } } for _, msg := range msgs.InvitedRequests { if !invExists(msg.RequestId) { - c.dispatchGroupInvitedEvent(msg) + c.GroupInvitedEvent.dispatch(c, msg) } } c.groupSysMsgCache = msgs diff --git a/utils/ttl.go b/utils/ttl.go index 6b43ca24..e81228af 100644 --- a/utils/ttl.go +++ b/utils/ttl.go @@ -55,19 +55,18 @@ func (cache *Cache[_]) Count() int { } // Get - return value from cache -func (cache *Cache[_]) Get(key string) (interface{}, bool) { +func (cache *Cache[T]) Get(key string) (value T, _ bool) { cache.lock.RLock() defer cache.lock.RUnlock() e, ok := cache.cache[key] - if ok && e.expiry.After(time.Now()) { return e.value, true } - return nil, false + return } -func (cache *Cache[_]) GetAndUpdate(key string, ttl time.Duration) (interface{}, bool) { +func (cache *Cache[T]) GetAndUpdate(key string, ttl time.Duration) (_ T, _ bool) { cache.lock.RLock() defer cache.lock.RUnlock() @@ -75,7 +74,7 @@ func (cache *Cache[_]) GetAndUpdate(key string, ttl time.Duration) (interface{}, e.expiry = time.Now().Add(ttl) return e.value, true } - return nil, false + return } // Add - add key/value in cache From ef65fd67e602723e97769899b3dca93e36223de6 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 16:45:01 +0800 Subject: [PATCH 13/44] client: use unpackOIDBPackage --- client/decoders.go | 9 +++------ client/events.go | 2 +- client/global.go | 6 +++--- client/group_file.go | 45 +++++++++++++++----------------------------- client/group_info.go | 9 +++------ client/group_msg.go | 18 ++++++------------ client/image.go | 9 +++------ client/security.go | 9 +++------ client/translate.go | 9 +++------ 9 files changed, 40 insertions(+), 76 deletions(-) diff --git a/client/decoders.go b/client/decoders.go index 5c180f6e..f1cb8ec2 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -783,13 +783,10 @@ func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte // OidbSvc.0xd79 func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := &oidb.D79RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.Content != nil { return rsp.Content.SliceContent, nil diff --git a/client/events.go b/client/events.go index 3cc55ac7..43a095ab 100644 --- a/client/events.go +++ b/client/events.go @@ -25,8 +25,8 @@ func (handle *EventHandle[T]) Subscribe(handler func(client *QQClient, event T)) func (handle *EventHandle[T]) dispatch(client *QQClient, event T) { eventMu.RLock() - defer eventMu.RUnlock() defer func() { + eventMu.RUnlock() if pan := recover(); pan != nil { fmt.Printf("event error: %v\n%s", pan, debug.Stack()) } diff --git a/client/global.go b/client/global.go index 909adde3..f54dac35 100644 --- a/client/global.go +++ b/client/global.go @@ -356,15 +356,15 @@ func (c *QQClient) packOIDBPackageProto(cmd, serviceType int32, msg proto.Messag return c.packOIDBPackage(cmd, serviceType, b) } -func unpackOIDBPackage(buff []byte, payload proto.Message) error { +func unpackOIDBPackage(payload []byte, rsp proto.Message) error { pkg := new(oidb.OIDBSSOPkg) - if err := proto.Unmarshal(buff, pkg); err != nil { + if err := proto.Unmarshal(payload, pkg); err != nil { return errors.Wrap(err, "failed to unmarshal protobuf message") } if pkg.Result != 0 { return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.Result, pkg.ErrorMsg) } - if err := proto.Unmarshal(pkg.Bodybuffer, payload); err != nil { + if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil { return errors.Wrap(err, "failed to unmarshal protobuf message") } return nil diff --git a/client/group_file.go b/client/group_file.go index c2799a15..05a6b217 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -441,26 +441,20 @@ func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId } func decodeOIDB6d81Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D8RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } return &rsp, nil } // OidbSvc.0x6d6_2 func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.DownloadFileRsp.DownloadUrl == nil { return nil, errors.New(rsp.DownloadFileRsp.GetClientWording()) @@ -471,13 +465,10 @@ func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload } func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.DeleteFileRsp == nil { return "", nil @@ -486,25 +477,19 @@ func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload } func decodeOIDB6d60Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D6RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } return rsp.UploadFileRsp, nil } func decodeOIDB6d7Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D6D7RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.CreateFolderRsp != nil && rsp.CreateFolderRsp.GetRetCode() != 0 { return nil, errors.Errorf("create folder error: %v", rsp.CreateFolderRsp.GetRetCode()) diff --git a/client/group_info.go b/client/group_info.go index 712076cf..968d3f00 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -227,13 +227,10 @@ func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo // OidbSvc.0x88d_0 func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D88DRspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if len(rsp.RspGroupInfo) == 0 { return nil, errors.New(string(rsp.StrErrorInfo)) diff --git a/client/group_msg.go b/client/group_msg.go index 45960e09..34f519aa 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -397,13 +397,10 @@ func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, pa } func decodeAtAllRemainResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.D8A7RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } return &AtAllRemainInfo{ CanAtAll: rsp.GetCanAtAll(), @@ -593,13 +590,10 @@ func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand // OidbSvc.0xeac_1/2 func decodeEssenceMsgResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := &oidb.EACRspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } return rsp, nil } diff --git a/client/image.go b/client/image.go index 51b45b76..1fd10409 100644 --- a/client/image.go +++ b/client/image.go @@ -405,13 +405,10 @@ func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo // OidbSvc.0xe07_0 func decodeImageOcrResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.DE07RspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.Wording != "" { if strings.Contains(rsp.Wording, "服务忙") { diff --git a/client/security.go b/client/security.go index 777a4031..3ac00921 100644 --- a/client/security.go +++ b/client/security.go @@ -49,13 +49,10 @@ func (c *QQClient) buildUrlCheckRequest(url string) (uint16, []byte) { } func decodeUrlCheckResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := &oidb.OIDBSSOPkg{} rsp := &oidb.DBCBRspBody{} - if err := proto.Unmarshal(payload, pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } if rsp.CheckUrlRsp == nil || len(rsp.CheckUrlRsp.Results) == 0 { return nil, errors.New("response is empty") diff --git a/client/translate.go b/client/translate.go index 946cec49..6049aca6 100644 --- a/client/translate.go +++ b/client/translate.go @@ -42,13 +42,10 @@ func (c *QQClient) Translate(src, dst, text string) (string, error) { // OidbSvc.0x990 func decodeTranslateResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { - pkg := oidb.OIDBSSOPkg{} rsp := oidb.TranslateRspBody{} - if err := proto.Unmarshal(payload, &pkg); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") - } - if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal protobuf message") + err := unpackOIDBPackage(payload, &rsp) + if err != nil { + return nil, err } return rsp.BatchTranslateRsp, nil } From a7098f600035a51d044ffe37d03b62d35380721d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 22:03:08 +0800 Subject: [PATCH 14/44] client: use packOIDBPackageProto --- client/group_file.go | 81 +++++++++++--------------------------------- client/group_info.go | 7 +--- client/translate.go | 9 +---- 3 files changed, 22 insertions(+), 75 deletions(-) diff --git a/client/group_file.go b/client/group_file.go index 05a6b217..eec1dac3 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -276,7 +276,7 @@ func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32 } func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) (uint16, []byte) { - b, _ := proto.Marshal(&oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{ + body := &oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{ GroupCode: &groupCode, AppId: proto.Int32(3), BusId: proto.Int32(102), @@ -288,14 +288,8 @@ func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string Sha: sha1, Md5: md5, SupportMultiUpload: proto.Bool(true), - }}) - req := &oidb.OIDBSSOPkg{ - Command: 1750, - ServiceType: 0, - Bodybuffer: b, - ClientVersion: "android 8.4.8", - } - payload, _ := proto.Marshal(req) + }} + payload := c.packOIDBPackageProto(1750, 0, body) return c.uniPacket("OidbSvc.0x6d6_0", payload) } @@ -328,31 +322,19 @@ func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID str StartIndex: &startIndex, Context: EmptyBytes, }} - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 1752, - ServiceType: 1, - Bodybuffer: b, - ClientVersion: "android 8.4.8", - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(1752, 1, body) return c.uniPacket("OidbSvc.0x6d8_1", payload) } func (c *QQClient) buildGroupFileCountRequestPacket(groupCode int64) (uint16, []byte) { - body := &oidb.D6D8ReqBody{GroupFileCountReq: &oidb.GetFileCountReqBody{ - GroupCode: proto.Uint64(uint64(groupCode)), - AppId: proto.Uint32(3), - BusId: proto.Uint32(0), - }} - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 1752, - ServiceType: 2, - Bodybuffer: b, - ClientVersion: "android 8.4.8", + body := &oidb.D6D8ReqBody{ + GroupFileCountReq: &oidb.GetFileCountReqBody{ + GroupCode: proto.Uint64(uint64(groupCode)), + AppId: proto.Uint32(3), + BusId: proto.Uint32(0), + }, } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(1752, 2, body) return c.uniPacket("OidbSvc.0x6d8_1", payload) } @@ -361,14 +343,7 @@ func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, [] GroupCode: proto.Uint64(uint64(groupCode)), AppId: proto.Uint32(3), }} - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 1752, - ServiceType: 3, - Bodybuffer: b, - ClientVersion: "android 8.4.8", - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(1752, 3, body) return c.uniPacket("OidbSvc.0x6d8_1", payload) } @@ -411,13 +386,7 @@ func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId strin FileId: &fileId, }, } - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 1750, - ServiceType: 2, - Bodybuffer: b, - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(1750, 2, body) return c.uniPacket("OidbSvc.0x6d6_2", payload) } @@ -429,14 +398,7 @@ func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId ParentFolderId: &parentFolderId, FileId: &fileId, }} - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 1750, - ServiceType: 3, - Bodybuffer: b, - ClientVersion: "android 8.4.8", - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(1750, 3, body) return c.uniPacket("OidbSvc.0x6d6_3", payload) } @@ -470,9 +432,6 @@ func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload if err != nil { return nil, err } - if rsp.DeleteFileRsp == nil { - return "", nil - } return rsp.DeleteFileRsp.GetClientWording(), nil } @@ -491,14 +450,14 @@ func decodeOIDB6d7Response(_ *QQClient, _ *network.IncomingPacketInfo, payload [ if err != nil { return nil, err } - if rsp.CreateFolderRsp != nil && rsp.CreateFolderRsp.GetRetCode() != 0 { - return nil, errors.Errorf("create folder error: %v", rsp.CreateFolderRsp.GetRetCode()) + if retCode := rsp.CreateFolderRsp.GetRetCode(); retCode != 0 { + return nil, errors.Errorf("create folder error: %v", retCode) } - if rsp.RenameFolderRsp != nil && rsp.RenameFolderRsp.GetRetCode() != 0 { - return nil, errors.Errorf("rename folder error: %v", rsp.CreateFolderRsp.GetRetCode()) + if retCode := rsp.RenameFolderRsp.GetRetCode(); retCode != 0 { + return nil, errors.Errorf("rename folder error: %v", retCode) } - if rsp.DeleteFolderRsp != nil && rsp.DeleteFolderRsp.GetRetCode() != 0 { - return nil, errors.Errorf("delete folder error: %v", rsp.CreateFolderRsp.GetRetCode()) + if retCode := rsp.DeleteFolderRsp.GetRetCode(); retCode != 0 { + return nil, errors.Errorf("delete folder error: %v", retCode) } return nil, nil } diff --git a/client/group_info.go b/client/group_info.go index 968d3f00..ed3e3b8d 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -116,12 +116,7 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) }, PcClientVersion: proto.Uint32(0), } - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 2189, - Bodybuffer: b, - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(2189, 0, body) return c.uniPacket("OidbSvc.0x88d_0", payload) } diff --git a/client/translate.go b/client/translate.go index 6049aca6..f1ccdbd9 100644 --- a/client/translate.go +++ b/client/translate.go @@ -5,7 +5,6 @@ import ( "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/pb/oidb" - "github.com/Mrs4s/MiraiGo/internal/proto" ) func (c *QQClient) buildTranslatePacket(src, dst, text string) (uint16, []byte) { @@ -16,13 +15,7 @@ func (c *QQClient) buildTranslatePacket(src, dst, text string) (uint16, []byte) SrcTextList: []string{text}, }, } - b, _ := proto.Marshal(body) - req := &oidb.OIDBSSOPkg{ - Command: 2448, - ServiceType: 2, - Bodybuffer: b, - } - payload, _ := proto.Marshal(req) + payload := c.packOIDBPackageProto(2448, 2, body) return c.uniPacket("OidbSvc.0x990", payload) } From abb3709a187cefb6d250f54cd32e604a8ae82e42 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 22:52:22 +0800 Subject: [PATCH 15/44] client: replace LogEvent with Logger --- client/c2c_processor.go | 16 +++++++-------- client/client.go | 7 ++++--- client/decoders.go | 10 ++++----- client/entities.go | 6 ------ client/events.go | 17 ---------------- client/global.go | 43 --------------------------------------- client/group_file.go | 2 +- client/group_msg.go | 18 ++++++++-------- client/guild.go | 6 +++--- client/guild_eventflow.go | 16 +++++++-------- client/image.go | 2 +- client/log.go | 33 ++++++++++++++++++++++++++++++ client/network.go | 42 +++++++++++++++++++------------------- client/notify.go | 2 +- client/offline_file.go | 6 +++--- client/online_push.go | 4 ++-- client/qidian.go | 2 +- client/sync.go | 2 +- client/system_msg.go | 6 +++--- client/tlv_decoders.go | 4 ++-- 20 files changed, 106 insertions(+), 138 deletions(-) create mode 100644 client/log.go diff --git a/client/c2c_processor.go b/client/c2c_processor.go index 8b44f8a7..064bdfd9 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -71,7 +71,7 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *ne _, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems)) } if rsp.GetSyncFlag() != msg.SyncFlag_STOP { - c.Debug("continue sync with flag: %v", rsp.SyncFlag) + c.debug("continue sync with flag: %v", rsp.SyncFlag) seq, pkt := c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix()) _, _ = c.sendAndWait(seq, pkt, info.Params) } @@ -80,12 +80,12 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *ne func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info *network.IncomingPacketInfo) { strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.GetFromUin(), pMsg.Head.GetToUin(), pMsg.Head.GetMsgSeq(), pMsg.Head.GetMsgUid()) if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Hour); ok { - c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid()) + c.debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid()) return } c.msgSvcCache.Add(strKey, unit{}, time.Hour) if c.lastC2CMsgTime > int64(pMsg.Head.GetMsgTime()) && (c.lastC2CMsgTime-int64(pMsg.Head.GetMsgTime())) > 60*10 { - c.Debug("c2c msg filtered by time. lastMsgTime: %v msgTime: %v", c.lastC2CMsgTime, pMsg.Head.GetMsgTime()) + c.debug("c2c msg filtered by time. lastMsgTime: %v msgTime: %v", c.lastC2CMsgTime, pMsg.Head.GetMsgTime()) return } c.lastC2CMsgTime = int64(pMsg.Head.GetMsgTime()) @@ -95,7 +95,7 @@ func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info *network.IncomingPac if decoder, _ := peekC2CDecoder(pMsg.Head.GetMsgType()); decoder != nil { decoder(c, pMsg, info) } else { - c.Debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.GetMsgType(), pMsg.Head.GetC2CCmd()) + c.debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.GetMsgType(), pMsg.Head.GetC2CCmd()) } } @@ -137,7 +137,7 @@ func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *network.IncomingPa } c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg)) default: - c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd()) + c.debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd()) } } @@ -225,7 +225,7 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *network.I if group != nil && group.FindMember(pMsg.Head.GetAuthUin()) == nil { mem, err := c.GetMemberInfo(group.Code, pMsg.Head.GetAuthUin()) if err != nil { - c.Debug("error to fetch new member info: %v", err) + c.debug("error to fetch new member info: %v", err) return } group.Update(func(info *GroupInfo) { @@ -255,7 +255,7 @@ func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *network.Inc reader := binary.NewReader(pMsg.Body.MsgContent) groupCode := uint32(reader.ReadInt32()) if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GetGroupName() != "" && info.Name != pMsg.Head.GetGroupName() { - c.Debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GetGroupName()) + c.debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GetGroupName()) info.Name = pMsg.Head.GetGroupName() } } @@ -267,7 +267,7 @@ func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info *network.IncomingP sub4 := msg.SubMsgType0X4Body{} if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil { err = errors.Wrap(err, "unmarshal sub msg 0x4 error") - c.Error("unmarshal sub msg 0x4 error: %v", err) + c.error("unmarshal sub msg 0x4 error: %v", err) return } if sub4.NotOnlineFile != nil && sub4.NotOnlineFile.GetSubcmd() == 1 { // subcmd: 1 -> sendPacket, 2-> recv diff --git a/client/client.go b/client/client.go index e5fe70d0..8aced500 100644 --- a/client/client.go +++ b/client/client.go @@ -55,6 +55,7 @@ type QQClient struct { transport *network.Transport oicq *oicq.Codec + logger Logger // internal state handlers HandlerMap @@ -420,7 +421,7 @@ func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) { func (c *QQClient) RequestSMS() bool { rsp, err := c.sendAndWait(c.buildSMSRequestPacket()) if err != nil { - c.Error("request sms error: %v", err) + c.error("request sms error: %v", err) return false } return rsp.(LoginResponse).Error == SMSNeededError @@ -428,7 +429,7 @@ func (c *QQClient) RequestSMS() bool { func (c *QQClient) init(tokenLogin bool) error { if len(c.sig.G) == 0 { - c.Warning("device lock is disable. http api may fail.") + c.warning("device lock is disable. http api may fail.") } c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) if err := c.registerClient(); err != nil { @@ -703,7 +704,7 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { func (c *QQClient) getSKey() string { if c.sig.SKeyExpiredTime < time.Now().Unix() && len(c.sig.G) > 0 { - c.Debug("skey expired. refresh...") + c.debug("skey expired. refresh...") _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket()) } return string(c.sig.SKey) diff --git a/client/decoders.go b/client/decoders.go index f1cb8ec2..2de61f18 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -171,9 +171,9 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b ErrorMessage: t146r.ReadStringShort(), }, nil } - c.Debug("unknown login response: %v", t) + c.debug("unknown login response: %v", t) for k, v := range m { - c.Debug("Type: %v Value: %v", strconv.FormatInt(int64(k), 16), hex.EncodeToString(v)) + c.debug("Type: %v Value: %v", strconv.FormatInt(int64(k), 16), hex.EncodeToString(v)) } return nil, errors.Errorf("unknown login response: %v", t) // ? } @@ -188,7 +188,7 @@ func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, pa svcRsp.ReadFrom(jce.NewJceReader(data.Map["SvcRespRegister"]["QQService.SvcRespRegister"][1:])) if svcRsp.Result != "" || svcRsp.ReplyCode != 0 { if svcRsp.Result != "" { - c.Error("reg error: %v", svcRsp.Result) + c.error("reg error: %v", svcRsp.Result) } return nil, errors.New("reg failed") } @@ -318,7 +318,7 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b if strings.Contains(s.Server, "com") { continue } - c.Debug("got new server addr: %v location: %v", s.Server, s.Location) + c.debug("got new server addr: %v location: %v", s.Server, s.Location) adds = append(adds, &net.TCPAddr{ IP: net.ParseIP(s.Server), Port: int(s.Port), @@ -341,7 +341,7 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b fmtPkt := jce.NewJceReader(jceBuf) list := &jce.FileStoragePushFSSvcList{} list.ReadFrom(fmtPkt) - c.Debug("got file storage svc push.") + c.debug("got file storage svc push.") // c.fileStorageInfo = list rsp := cmd0x6ff.C501RspBody{} if err := proto.Unmarshal(list.BigDataChannel.PbBuf, &rsp); err == nil && rsp.RspBody != nil { diff --git a/client/entities.go b/client/entities.go index d2351b56..6f12069b 100644 --- a/client/entities.go +++ b/client/entities.go @@ -175,12 +175,6 @@ type ( client *QQClient } - LogEvent struct { - Type string - Message string - Dump []byte - } - ServerUpdatedEvent struct { Servers []jce.SsoServerInfo } diff --git a/client/events.go b/client/events.go index 43a095ab..ba7071c9 100644 --- a/client/events.go +++ b/client/events.go @@ -46,8 +46,6 @@ type eventHandlers struct { guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent) memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent) - // consider Logger interface? - logHandlers []func(*QQClient, *LogEvent) serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool groupMessageReceiptHandlers sync.Map } @@ -84,10 +82,6 @@ func (c *QQClient) OnServerUpdated(f func(*QQClient, *ServerUpdatedEvent) bool) c.eventHandlers.serverUpdatedHandlers = append(c.eventHandlers.serverUpdatedHandlers, f) } -func (c *QQClient) OnLog(f func(*QQClient, *LogEvent)) { - c.eventHandlers.logHandlers = append(c.eventHandlers.logHandlers, f) -} - func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool { return func(msg *message.PrivateMessage) bool { return msg.Sender.Uin == uin @@ -186,17 +180,6 @@ func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) }) } -func (c *QQClient) dispatchLogEvent(e *LogEvent) { - if e == nil { - return - } - for _, f := range c.eventHandlers.logHandlers { - cover(func() { - f(c, e) - }) - } -} - func cover(f func()) { defer func() { if pan := recover(); pan != nil { diff --git a/client/global.go b/client/global.go index f54dac35..6af74243 100644 --- a/client/global.go +++ b/client/global.go @@ -369,46 +369,3 @@ func unpackOIDBPackage(payload []byte, rsp proto.Message) error { } return nil } - -func (c *QQClient) Error(msg string, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "ERROR", - Message: fmt.Sprintf(msg, args...), - }) -} - -func (c *QQClient) Warning(msg string, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "WARNING", - Message: fmt.Sprintf(msg, args...), - }) -} - -func (c *QQClient) Info(msg string, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "INFO", - Message: fmt.Sprintf(msg, args...), - }) -} - -func (c *QQClient) Debug(msg string, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "DEBUG", - Message: fmt.Sprintf(msg, args...), - }) -} - -func (c *QQClient) Trace(msg string, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "TRACE", - Message: fmt.Sprintf(msg, args...), - }) -} - -func (c *QQClient) Dump(msg string, data []byte, args ...interface{}) { - c.dispatchLogEvent(&LogEvent{ - Type: "DUMP", - Message: fmt.Sprintf(msg, args...), - Dump: data, - }) -} diff --git a/client/group_file.go b/client/group_file.go index eec1dac3..bcdc5078 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -71,7 +71,7 @@ func init() { func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err error) { defer func() { if pan := recover(); pan != nil { - c.Error("get group fs error: %v\n%s", pan, debug.Stack()) + c.error("get group fs error: %v\n%s", pan, debug.Stack()) err = errors.New("fs error") } }() diff --git a/client/group_msg.go b/client/group_msg.go index 34f519aa..acac1a86 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -61,7 +61,7 @@ func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, Message: m.Elements, })) if err != nil { - c.Error("%v", err) + c.error("%v", err) return nil } ret := c.sendGroupMessage(groupCode, false, &message.SendingMessage{Elements: []message.IMessageElement{lmsg}}) @@ -178,7 +178,7 @@ func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMes } err := c.highwaySession.Upload(addr, input) if err != nil { - c.Error("highway upload long message error: %v", err) + c.error("highway upload long message error: %v", err) continue } return genLongTemplate(rsp.MsgResid, m.Brief(), ts), nil @@ -340,9 +340,9 @@ func decodeMsgSendResponse(c *QQClient, _ *network.IncomingPacketInfo, payload [ switch rsp.GetResult() { case 0: // OK. case 55: - c.Error("sendPacket msg error: %v Bot has blocked target's content", rsp.GetResult()) + c.error("sendPacket msg error: %v Bot has blocked target's content", rsp.GetResult()) default: - c.Error("sendPacket msg error: %v %v", rsp.GetResult(), rsp.GetErrMsg()) + c.error("sendPacket msg error: %v %v", rsp.GetResult(), rsp.GetErrMsg()) } return nil, nil } @@ -353,7 +353,7 @@ func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, pa return nil, errors.Wrap(err, "failed to unmarshal protobuf message") } if rsp.GetResult() != 0 { - c.Error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg()) + c.error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg()) return nil, errors.Errorf("get msg error: %v msg: %v", rsp.GetResult(), rsp.GetErrmsg()) } var ret []*message.GroupMessage @@ -363,7 +363,7 @@ func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, pa } if m.Content != nil && m.Content.GetPkgNum() > 1 && !info.Params.Bool("raw") { if m.Content.GetPkgIndex() == 0 { - c.Debug("build fragmented message from history") + c.debug("build fragmented message from history") i := m.Head.GetMsgSeq() - m.Content.GetPkgNum() builder := &messageBuilder{} for { @@ -412,10 +412,10 @@ func decodeAtAllRemainResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { group := c.FindGroup(m.Head.GroupInfo.GetGroupCode()) if group == nil { - c.Debug("sync group %v.", m.Head.GroupInfo.GetGroupCode()) + c.debug("sync group %v.", m.Head.GroupInfo.GetGroupCode()) info, err := c.GetGroupInfo(m.Head.GroupInfo.GetGroupCode()) if err != nil { - c.Error("error to sync group %v : %+v", m.Head.GroupInfo.GetGroupCode(), err) + c.error("error to sync group %v : %+v", m.Head.GroupInfo.GetGroupCode(), err) return nil } group = info @@ -424,7 +424,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { if len(group.Members) == 0 { mem, err := c.GetGroupMembers(group) if err != nil { - c.Error("error to sync group %v member : %+v", m.Head.GroupInfo.GroupCode, err) + c.error("error to sync group %v member : %+v", m.Head.GroupInfo.GroupCode, err) return nil } group.Members = mem diff --git a/client/guild.go b/client/guild.go index 56d9791e..df4da040 100644 --- a/client/guild.go +++ b/client/guild.go @@ -710,7 +710,7 @@ func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo { func (c *QQClient) syncChannelFirstView() { rsp, err := c.sendAndWaitDynamic(c.buildSyncChannelFirstViewPacket()) if err != nil { - c.Error("sync channel error: %v", err) + c.error("sync channel error: %v", err) return } firstViewRsp := new(channel.FirstViewRsp) @@ -723,7 +723,7 @@ func (c *QQClient) syncChannelFirstView() { c.GuildService.Nickname = self.Nickname c.GuildService.AvatarUrl = self.AvatarUrl } else { - c.Error("get self guild profile error: %v", err) + c.error("get self guild profile error: %v", err) } } @@ -754,7 +754,7 @@ func decodeGuildPushFirstView(c *QQClient, _ *network.IncomingPacketInfo, payloa } channels, err := c.GuildService.FetchChannelList(info.GuildId) if err != nil { - c.Warning("waring: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err) + c.warning("waring: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err) for _, node := range guild.ChannelNodes { meta := new(channel.ChannelMsgMeta) _ = proto.Unmarshal(node.Meta, meta) diff --git a/client/guild_eventflow.go b/client/guild_eventflow.go index d40f559d..8ebd10e1 100644 --- a/client/guild_eventflow.go +++ b/client/guild_eventflow.go @@ -80,7 +80,7 @@ func decodeGuildEventFlowPacket(c *QQClient, _ *network.IncomingPacketInfo, payl } eventBody := new(channel.EventBody) if err := proto.Unmarshal(common.PbElem, eventBody); err != nil { - c.Error("failed to unmarshal guild channel event body: %v", err) + c.error("failed to unmarshal guild channel event body: %v", err) continue } c.processGuildEventBody(m, eventBody) @@ -106,7 +106,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody var guild *GuildInfo if m.Head.RoutingHead.GetGuildId() != 0 { if guild = c.GuildService.FindGuild(m.Head.RoutingHead.GetGuildId()); guild == nil { - c.Warning("process channel event error: guild not found.") + c.warning("process channel event error: guild not found.") return } } @@ -118,7 +118,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody } channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.GetChanId()) if err != nil { - c.Warning("process create channel event error: fetch channel info error: %v", err) + c.warning("process create channel event error: fetch channel info error: %v", err) continue } guild.Channels = append(guild.Channels, channelInfo) @@ -148,7 +148,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody if oldInfo == nil { info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId()) if err != nil { - c.Error("error to decode channel info updated event: fetch channel info failed: %v", err) + c.error("error to decode channel info updated event: fetch channel info failed: %v", err) return } guild.Channels = append(guild.Channels, info) @@ -159,7 +159,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody } newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId()) if err != nil { - c.Error("error to decode channel info updated event: fetch channel info failed: %v", err) + c.error("error to decode channel info updated event: fetch channel info failed: %v", err) return } for i := range guild.Channels { @@ -178,13 +178,13 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody case eventBody.JoinGuild != nil: /* 应该不会重复推送把, 不会吧不会吧 if mem := guild.FindMember(eventBody.JoinGuild.GetMemberTinyid()); mem != nil { - c.Info("ignore join guild event: member %v already exists", mem.TinyId) + c.info("ignore join guild event: member %v already exists", mem.TinyId) return } */ profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.GetMemberTinyid()) if err != nil { - c.Error("error to decode member join guild event: get member profile error: %v", err) + c.error("error to decode member join guild event: get member profile error: %v", err) return } info := &GuildMemberInfo{ @@ -210,7 +210,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody if eventBody.UpdateMsg.GetEventType() == 4 { // 消息贴表情更新 (包含添加或删除) t, err := c.GuildService.pullChannelMessages(m.Head.RoutingHead.GetGuildId(), m.Head.RoutingHead.GetChannelId(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetEventVersion()-1, false) if err != nil || len(t) == 0 { - c.Error("process guild event flow error: pull eventMsg message error: %v", err) + c.error("process guild event flow error: pull eventMsg message error: %v", err) return } // 自己的消息被贴表情会单独推送一个tips, 这里不需要解析 diff --git a/client/image.go b/client/image.go index 1fd10409..7a723e72 100644 --- a/client/image.go +++ b/client/image.go @@ -141,7 +141,7 @@ ok: width := int32(i.Width) height := int32(i.Height) if err != nil && target.SourceType != message.SourceGroup { - c.Warning("waring: decode image error: %v. this image will be displayed by wrong size in pc guild client", err) + c.warning("waring: decode image error: %v. this image will be displayed by wrong size in pc guild client", err) width = 200 height = 200 } diff --git a/client/log.go b/client/log.go new file mode 100644 index 00000000..f0299d2f --- /dev/null +++ b/client/log.go @@ -0,0 +1,33 @@ +package client + +type Logger interface { + Info(format string, args ...any) + Warning(format string, args ...any) + Error(format string, args ...any) + Debug(format string, args ...any) + Dump(dumped []byte, format string, args ...any) +} + +func (c *QQClient) SetLogger(logger Logger) { + c.logger = logger +} + +func (c *QQClient) info(msg string, args ...any) { + c.logger.Info(msg, args...) +} + +func (c *QQClient) warning(msg string, args ...any) { + c.logger.Warning(msg, args...) +} + +func (c *QQClient) error(msg string, args ...any) { + c.logger.Error(msg, args...) +} + +func (c *QQClient) debug(msg string, args ...any) { + c.logger.Debug(msg, args...) +} + +func (c *QQClient) dump(msg string, data []byte, args ...any) { + c.logger.Dump(data, msg, args...) +} diff --git a/client/network.go b/client/network.go index 0554c816..b826bae2 100644 --- a/client/network.go +++ b/client/network.go @@ -45,22 +45,22 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { var err error if r.ChatServerLatency, err = qualityTest(c.servers[c.currServerIndex].String()); err != nil { - c.Error("test chat server latency error: %v", err) + c.error("test chat server latency error: %v", err) r.ChatServerLatency = 9999 } if addr, err := net.ResolveIPAddr("ip", "ssl.htdata.qq.com"); err == nil { if r.LongMessageServerLatency, err = qualityTest((&net.TCPAddr{IP: addr.IP, Port: 443}).String()); err != nil { - c.Error("test long message server latency error: %v", err) + c.error("test long message server latency error: %v", err) r.LongMessageServerLatency = 9999 } } else { - c.Error("resolve long message server error: %v", err) + c.error("resolve long message server error: %v", err) r.LongMessageServerLatency = 9999 } if c.highwaySession.AddrLength() > 0 { if r.SrvServerLatency, err = qualityTest(c.highwaySession.SsoAddr[0].String()); err != nil { - c.Error("test srv server latency error: %v", err) + c.error("test srv server latency error: %v", err) r.SrvServerLatency = 9999 } } @@ -78,7 +78,7 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil { r.LongMessageServerResponseLatency = time.Since(start).Milliseconds() } else { - c.Error("test long message server response latency error: %v", err) + c.error("test long message server response latency error: %v", err) r.LongMessageServerResponseLatency = 9999 } wg.Wait() @@ -87,7 +87,7 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { // connect 连接到 QQClient.servers 中的服务器 func (c *QQClient) connect() error { - c.Info("connect to server: %v", c.servers[c.currServerIndex].String()) + c.info("connect to server: %v", c.servers[c.currServerIndex].String()) err := c.TCP.Connect(c.servers[c.currServerIndex]) c.currServerIndex++ if c.currServerIndex == len(c.servers) { @@ -98,7 +98,7 @@ func (c *QQClient) connect() error { if c.retryTimes > len(c.servers) { return errors.New("All servers are unreachable") } - c.Error("connect server error: %v", err) + c.error("connect server error: %v", err) return err } c.once.Do(func() { @@ -129,12 +129,12 @@ func (c *QQClient) quickReconnect() { c.Disconnect() time.Sleep(time.Millisecond * 200) if err := c.connect(); err != nil { - c.Error("connect server error: %v", err) + c.error("connect server error: %v", err) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"}) return } if err := c.registerClient(); err != nil { - c.Error("register client failed: %v", err) + c.error("register client failed: %v", err) c.Disconnect() c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) return @@ -251,23 +251,23 @@ func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) { // plannedDisconnect 计划中断线事件 func (c *QQClient) plannedDisconnect(_ *network.TCPListener) { - c.Debug("planned disconnect.") + c.debug("planned disconnect.") c.stat.DisconnectTimes.Add(1) c.Online.Store(false) } // unexpectedDisconnect 非预期断线事件 func (c *QQClient) unexpectedDisconnect(_ *network.TCPListener, e error) { - c.Error("unexpected disconnect: %v", e) + c.error("unexpected disconnect: %v", e) c.stat.DisconnectTimes.Add(1) c.Online.Store(false) if err := c.connect(); err != nil { - c.Error("connect server error: %v", err) + c.error("connect server error: %v", err) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."}) return } if err := c.registerClient(); err != nil { - c.Error("register client failed: %v", err) + c.error("register client failed: %v", err) c.Disconnect() c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"}) return @@ -284,7 +284,7 @@ func (c *QQClient) netLoop() { continue } if l < 4 || l > 1024*1024*10 { // max 10MB - c.Error("parse incoming packet error: invalid packet length %v", l) + c.error("parse incoming packet error: invalid packet length %v", l) errCount++ if errCount > 2 { go c.quickReconnect() @@ -295,7 +295,7 @@ func (c *QQClient) netLoop() { resp, err := c.transport.ReadResponse(data) // pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key) if err != nil { - c.Error("parse incoming packet error: %v", err) + c.error("parse incoming packet error: %v", err) if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) { c.Disconnect() go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"}) @@ -310,7 +310,7 @@ func (c *QQClient) netLoop() { if resp.EncryptType == network.EncryptTypeEmptyKey { m, err := c.oicq.Unmarshal(resp.Body) if err != nil { - c.Error("decrypt payload error: %v", err) + c.error("decrypt payload error: %v", err) if errors.Is(err, oicq.ErrUnknownFlag) { go c.quickReconnect() } @@ -319,7 +319,7 @@ func (c *QQClient) netLoop() { resp.Body = m.Body } errCount = 0 - c.Debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID) + c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID) c.stat.PacketReceived.Add(1) pkt := &packets.IncomingPacket{ SequenceId: uint16(resp.SequenceID), @@ -329,8 +329,8 @@ func (c *QQClient) netLoop() { go func(pkt *packets.IncomingPacket) { defer func() { if pan := recover(); pan != nil { - c.Error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack()) - c.Dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan) + c.error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack()) + c.dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan) } }() @@ -346,7 +346,7 @@ func (c *QQClient) netLoop() { Params: info.getParams(), }, pkt.Payload) if err != nil { - c.Debug("decode pkt %v error: %+v", pkt.CommandName, err) + c.debug("decode pkt %v error: %+v", pkt.CommandName, err) } } if ok { @@ -358,7 +358,7 @@ func (c *QQClient) netLoop() { // does not need decoder f.fun(pkt.Payload, nil) } else { - c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) + c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) } }(pkt) } diff --git a/client/notify.go b/client/notify.go index c9cda9e5..df2e8aa0 100644 --- a/client/notify.go +++ b/client/notify.go @@ -139,7 +139,7 @@ func (c *QQClient) msgGrayTipProcessor(groupCode int64, tipInfo *notify.AIOGrayT } } if event.Uin == 0 { - c.Error("process special title updated tips error: missing cmd") + c.error("process special title updated tips error: missing cmd") return } if mem := c.FindGroup(groupCode).FindMember(event.Uin); mem != nil { diff --git a/client/offline_file.go b/client/offline_file.go index 0fd8099e..f02223c3 100644 --- a/client/offline_file.go +++ b/client/offline_file.go @@ -36,15 +36,15 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [ func decodeOfflineFileDownloadResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { rsp := cmd0x346.C346RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { - c.Error("unmarshal cmd0x346 rsp body error: %v", err) + c.error("unmarshal cmd0x346 rsp body error: %v", err) return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error") } if rsp.ApplyDownloadRsp == nil { - c.Error("decode apply download 1200 error: apply rsp is nil.") + c.error("decode apply download 1200 error: apply rsp is nil.") return nil, errors.New("apply rsp is nil") } if rsp.ApplyDownloadRsp.RetCode != 0 { - c.Error("decode apply download 1200 error: %v", rsp.ApplyDownloadRsp.RetCode) + c.error("decode apply download 1200 error: %v", rsp.ApplyDownloadRsp.RetCode) return nil, errors.Errorf("apply download rsp error: %d", rsp.ApplyDownloadRsp.RetCode) } return "http://" + rsp.ApplyDownloadRsp.DownloadInfo.DownloadDomain + rsp.ApplyDownloadRsp.DownloadInfo.DownloadUrl, nil diff --git a/client/online_push.go b/client/online_push.go index f0f397d4..b843e858 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -118,7 +118,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa return nil, errors.Wrap(err, "decode online push 0x210 error") } } else { - c.Debug("unknown online push 0x210 sub type 0x%v", strconv.FormatInt(subType, 16)) + c.debug("unknown online push 0x210 sub type 0x%v", strconv.FormatInt(subType, 16)) } } } @@ -241,7 +241,7 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error { if s44.GroupSyncMsg.GrpCode == 0 { // member sync return errors.New("invalid group code") } - c.Debug("syncing members.") + c.debug("syncing members.") if group := c.FindGroup(s44.GroupSyncMsg.GrpCode); group != nil { group.lock.Lock() defer group.lock.Unlock() diff --git a/client/qidian.go b/client/qidian.go index 1054ac5d..ca07c707 100644 --- a/client/qidian.go +++ b/client/qidian.go @@ -52,7 +52,7 @@ func (c *QQClient) getQiDianAddressDetailList() ([]*FriendInfo, error) { ret := []*FriendInfo{} for _, detail := range rsp.GetAddressDetailListRspBody.AddressDetail { if len(detail.Qq) == 0 { - c.Warning("address detail %v QQ is 0", string(detail.Name)) + c.warning("address detail %v QQ is 0", string(detail.Name)) continue } ret = append(ret, &FriendInfo{ diff --git a/client/sync.go b/client/sync.go index 3a182114..ae0bf24f 100644 --- a/client/sync.go +++ b/client/sync.go @@ -90,7 +90,7 @@ func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) { if e.GroupNum != -1 { groupNum = e.GroupNum } - c.Debug("sync session %v/%v", len(ret.GroupSessions), groupNum) + c.debug("sync session %v/%v", len(ret.GroupSessions), groupNum) if groupNum != -1 && len(ret.GroupSessions) >= int(groupNum) { notifyChan <- true } diff --git a/client/system_msg.go b/client/system_msg.go index e3a36255..c01a5999 100644 --- a/client/system_msg.go +++ b/client/system_msg.go @@ -60,7 +60,7 @@ func (c *QQClient) GetGroupSystemMessages() (*GroupSystemMessages, error) { func (c *QQClient) exceptAndDispatchGroupSysMsg() { if c.groupSysMsgCache == nil { - c.Error("warning: groupSysMsgCache is nil") + c.error("warning: groupSysMsgCache is nil") c.groupSysMsgCache, _ = c.GetGroupSystemMessages() return } @@ -243,12 +243,12 @@ func decodeSystemMsgGroupPacket(c *QQClient, _ *network.IncomingPacketInfo, payl ActionUinNick: st.Msg.ActionUinQqNick, }) default: - c.Debug("unknown group system message type: %v", st.Msg.GroupMsgType) + c.debug("unknown group system message type: %v", st.Msg.GroupMsgType) } case 3: // ? case 5: // 自身状态变更(管理员/加群退群) default: - c.Debug("unknown group system msg: %v", st.Msg.SubType) + c.debug("unknown group system msg: %v", st.Msg.SubType) } } return ret, nil diff --git a/client/tlv_decoders.go b/client/tlv_decoders.go index 6337abf4..ee8a37e3 100644 --- a/client/tlv_decoders.go +++ b/client/tlv_decoders.go @@ -139,11 +139,11 @@ func (c *QQClient) decodeT119R(data []byte) { if t120, ok := m[0x120]; ok { c.sig.SKey = t120 c.sig.SKeyExpiredTime = time.Now().Unix() + 21600 - c.Debug("skey updated: %v", c.sig.SKey) + c.debug("skey updated: %v", c.sig.SKey) } if t11a, ok := m[0x11a]; ok { c.Nickname, c.Age, c.Gender = readT11A(t11a) - c.Debug("account info updated: " + c.Nickname) + c.debug("account info updated: " + c.Nickname) } } From 7699c3225855fbaa3cf6df8f1bb4bd3c6a9674c4 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 2 Mar 2022 18:03:08 +0800 Subject: [PATCH 16/44] client: only log when logger is non-nil --- client/log.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/client/log.go b/client/log.go index f0299d2f..3aa6a1e4 100644 --- a/client/log.go +++ b/client/log.go @@ -13,21 +13,31 @@ func (c *QQClient) SetLogger(logger Logger) { } func (c *QQClient) info(msg string, args ...any) { - c.logger.Info(msg, args...) + if c.logger != nil { + c.logger.Info(msg, args...) + } } func (c *QQClient) warning(msg string, args ...any) { - c.logger.Warning(msg, args...) + if c.logger != nil { + c.logger.Warning(msg, args...) + } } func (c *QQClient) error(msg string, args ...any) { - c.logger.Error(msg, args...) + if c.logger != nil { + c.logger.Error(msg, args...) + } } func (c *QQClient) debug(msg string, args ...any) { - c.logger.Debug(msg, args...) + if c.logger != nil { + c.logger.Debug(msg, args...) + } } func (c *QQClient) dump(msg string, data []byte, args ...any) { - c.logger.Dump(data, msg, args...) + if c.logger != nil { + c.logger.Dump(data, msg, args...) + } } From 348e317a34003358d4542f8e73152dfda055138c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 2 Mar 2022 21:41:46 +0800 Subject: [PATCH 17/44] client: shrink event handler slice --- binary/pool.go | 2 +- client/client.go | 2 -- client/decoders.go | 8 +++--- client/events.go | 6 ++++- client/tlv_decoders.go | 58 ++++++++++++++++++++++-------------------- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/binary/pool.go b/binary/pool.go index 3673b414..8d14a444 100644 --- a/binary/pool.go +++ b/binary/pool.go @@ -24,7 +24,7 @@ func SelectWriter() *Writer { // PutWriter 将 Writer 放回池中 func PutWriter(w *Writer) { // See https://golang.org/issue/23199 - const maxSize = 1 << 16 + const maxSize = 32 * 1024 if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃 w.Reset() bufferPool.Put(w) diff --git a/client/client.go b/client/client.go index 8aced500..f8ff2f81 100644 --- a/client/client.go +++ b/client/client.go @@ -49,7 +49,6 @@ type QQClient struct { // protocol public field SequenceId atomic.Int32 SessionId []byte - RandomKey []byte TCP *network.TCPListener // todo: combine other protocol state into one struct ConnectTime time.Time @@ -269,7 +268,6 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { } cli.TCP.PlannedDisconnect(cli.plannedDisconnect) cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect) - rand.Read(cli.RandomKey) return cli } diff --git a/client/decoders.go b/client/decoders.go index 2de61f18..aa646348 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -45,11 +45,11 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b } if t == 0 { // login success // if t150, ok := m[0x150]; ok { - // c.t150 = t150 + // c.t150 = t150 + // } + // if t161, ok := m[0x161]; ok { + // c.decodeT161(t161) // } - if t161, ok := m[0x161]; ok { - c.decodeT161(t161) - } if m.Exists(0x403) { c.sig.RandSeed = m[0x403] } diff --git a/client/events.go b/client/events.go index ba7071c9..cdcc5bcf 100644 --- a/client/events.go +++ b/client/events.go @@ -20,7 +20,11 @@ type EventHandle[T any] struct { func (handle *EventHandle[T]) Subscribe(handler func(client *QQClient, event T)) { eventMu.Lock() defer eventMu.Unlock() - handle.handlers = append(handle.handlers, handler) + // shrink the slice + newHandlers := make([]func(client *QQClient, event T), len(handle.handlers)+1) + copy(newHandlers, handle.handlers) + newHandlers[len(handle.handlers)] = handler + handle.handlers = newHandlers } func (handle *EventHandle[T]) dispatch(client *QQClient, event T) { diff --git a/client/tlv_decoders.go b/client/tlv_decoders.go index ee8a37e3..030e7ee0 100644 --- a/client/tlv_decoders.go +++ b/client/tlv_decoders.go @@ -12,16 +12,16 @@ import ( // --- tlv decoders for qq client --- +/* func (c *QQClient) decodeT161(data []byte) { - /* - reader := binary.NewReader(data) - reader.ReadBytes(2) - t := reader.ReadTlvMap(2) - if t172, ok := t[0x172]; ok { - c.rollbackSig = t172 - } - */ + reader := binary.NewReader(data) + reader.ReadBytes(2) + t := reader.ReadTlvMap(2) + if t172, ok := t[0x172]; ok { + c.rollbackSig = t172 + } } +*/ func (c *QQClient) decodeT119(data, ek []byte) { tea := binary.NewTeaCipher(ek) @@ -63,32 +63,32 @@ func (c *QQClient) decodeT119(data, ek []byte) { pt4TokenMap map[string][]byte ) - if _, ok := m[0x125]; ok { - // openId, openKey = readT125(t125) - } - if t186, ok := m[0x186]; ok { - c.decodeT186(t186) - } if t11a, ok := m[0x11a]; ok { nick, age, gender = readT11A(t11a) } - if _, ok := m[0x199]; ok { - // openId, payToken = readT199(t199) - } - if _, ok := m[0x200]; ok { - // pf, pfkey = readT200(t200) - } + /* + if _, ok := m[0x125]; ok { + openId, openKey = readT125(t125) + } + if t186, ok := m[0x186]; ok { + c.decodeT186(t186) + } + if _, ok := m[0x199]; ok { + openId, payToken = readT199(t199) + } + if _, ok := m[0x200]; ok { + pf, pfkey = readT200(t200) + } + if _, ok := m[0x531]; ok { + a1, noPicSig = readT531(t531) + } + if _, ok := m[0x138]; ok { + readT138(t138) // chg time + } + */ if t512, ok := m[0x512]; ok { psKeyMap, pt4TokenMap = readT512(t512) } - if _, ok := m[0x531]; ok { - // a1, noPicSig = readT531(t531) - } - - if _, ok := m[0x138]; ok { - // readT138(t138) // chg time - } - c.oicq.WtSessionTicketKey = utils.Select(m[0x134], c.oicq.WtSessionTicketKey) // we don't use `c.sigInfo = &auth.SigInfo{...}` here, @@ -160,9 +160,11 @@ func (c *QQClient) decodeT113(data []byte) { fmt.Println("got t113 uin:", uin) } +/* func (c *QQClient) decodeT186(data []byte) { // c.pwdFlag = data[1] == 1 } +*/ // --- tlv readers --- From 764a5a0c7761ba009f2d31fab0b317f81c977a34 Mon Sep 17 00:00:00 2001 From: icarus-ai <82353054+icarus-ai@users.noreply.github.com> Date: Sat, 12 Mar 2022 11:55:14 +0800 Subject: [PATCH 18/44] =?UTF-8?q?fix:=20=E5=90=88=E5=B9=B6=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=20(#259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 似在commit e287cbfabd0438955a7415a430f3ebfb3762cfca的时候复制粘贴错了 xml格式不符导致合并转发发送失败? 228行 群聊的聊天记录 改成聊天记录能发了 --- client/multimsg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/multimsg.go b/client/multimsg.go index 0348f903..489a6ffe 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -225,6 +225,6 @@ func forwardDisplay(resID, fileName, preview, summary string) string { sb.WriteString(`
`) sb.WriteString(summary) // todo: 私聊的聊天记录? - sb.WriteString(``) + sb.WriteString(``) return sb.String() } From dc9ecd65ea4e930140bf682d05d31e5f82fb0a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Sat, 12 Mar 2022 11:55:31 +0800 Subject: [PATCH 19/44] refactor: delete tag `14` in jce of `getSSOAddress` (#257) synchronize with source code --- client/global.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/global.go b/client/global.go index 909adde3..5f29a6ba 100644 --- a/client/global.go +++ b/client/global.go @@ -122,7 +122,7 @@ func getSSOAddress() ([]*net.TCPAddr, error) { WriteString("00000", 4).WriteInt32(100, 5). WriteInt32(int32(protocol.AppId), 6).WriteString(SystemDeviceInfo.IMEI, 7). WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10). - WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).WriteByte(1, 14).Bytes() + WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).Bytes() buf := &jce.RequestDataVersion3{ Map: map[string][]byte{"HttpServerListReq": packUniRequestData(payload)}, } @@ -140,7 +140,7 @@ func getSSOAddress() ([]*net.TCPAddr, error) { tea := binary.NewTeaCipher(key) encpkt := tea.Encrypt(b) cl() - rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", encpkt) + rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp?mType=getssolist", encpkt) if err != nil { return nil, errors.Wrap(err, "unable to fetch server list") } From ea0237538a15dc9cfbdb2b8ab996466b4accd550 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 16 Mar 2022 14:03:51 +0800 Subject: [PATCH 20/44] ci: update to go1.18 --- .github/workflows/go.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8083ec49..720f0273 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,8 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - stable: false - go-version: 1.18.0-rc1 + go-version: 1.18 - name: Check out code into the Go module directory uses: actions/checkout@v2 From 4a007cfcf9c7b4de1409ac7cb577eab96e5c2058 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 17 Mar 2022 10:52:03 +0800 Subject: [PATCH 21/44] all: rewrite interface{} to any generated by `gofmt -w -r 'interface{} -> any' .` --- binary/jce/reader_test.go | 8 +++--- binary/pool.go | 6 ++--- binary/utils.go | 2 +- client/client.go | 10 +++---- client/decoders.go | 42 +++++++++++++++--------------- client/events.go | 2 +- client/face.go | 2 +- client/group_file.go | 10 +++---- client/group_info.go | 8 +++--- client/group_msg.go | 10 +++---- client/guild.go | 2 +- client/guild_eventflow.go | 4 +-- client/guild_msg.go | 2 +- client/image.go | 10 +++---- client/internal/network/packet.go | 2 +- client/multimsg.go | 4 +-- client/network.go | 18 ++++++------- client/offline_file.go | 2 +- client/online_push.go | 2 +- client/ptt.go | 4 +-- client/qidian.go | 4 +-- client/recall.go | 2 +- client/security.go | 2 +- client/sync.go | 14 +++++----- client/system_msg.go | 2 +- client/translate.go | 2 +- internal/generator/jce_gen/main.go | 2 +- internal/proto/dynamic.go | 2 +- internal/proto/wrapper.go | 2 +- topic/feed.go | 2 +- 30 files changed, 92 insertions(+), 92 deletions(-) diff --git a/binary/jce/reader_test.go b/binary/jce/reader_test.go index 4fad5585..3a430eae 100644 --- a/binary/jce/reader_test.go +++ b/binary/jce/reader_test.go @@ -99,7 +99,7 @@ func TestJceReader_ReadBytes(t *testing.T) { assert.Equal(t, b, rb) } -func (w *JceWriter) WriteObject(i interface{}, tag byte) { +func (w *JceWriter) WriteObject(i any, tag byte) { t := reflect.TypeOf(i) if t.Kind() == reflect.Map { w.WriteMap(i, tag) @@ -192,7 +192,7 @@ type decoder struct { var decoderCache = sync.Map{} // WriteJceStructRaw 写入 Jce 结构体 -func (w *JceWriter) WriteJceStructRaw(s interface{}) { +func (w *JceWriter) WriteJceStructRaw(s any) { t := reflect.TypeOf(s) if t.Kind() != reflect.Ptr { return @@ -234,7 +234,7 @@ func (w *JceWriter) WriteJceStruct(s IJceStruct, tag byte) { w.writeHead(11, 0) } -func (w *JceWriter) WriteSlice(i interface{}, tag byte) { +func (w *JceWriter) WriteSlice(i any, tag byte) { va := reflect.ValueOf(i) if va.Kind() != reflect.Slice { panic("JceWriter.WriteSlice: not a slice") @@ -270,7 +270,7 @@ func (w *JceWriter) WriteJceStructSlice(l []IJceStruct, tag byte) { } } -func (w *JceWriter) WriteMap(m interface{}, tag byte) { +func (w *JceWriter) WriteMap(m any, tag byte) { va := reflect.ValueOf(m) if va.Kind() != reflect.Map { panic("JceWriter.WriteMap: not a map") diff --git a/binary/pool.go b/binary/pool.go index 8d14a444..287b0c9f 100644 --- a/binary/pool.go +++ b/binary/pool.go @@ -8,7 +8,7 @@ import ( ) var bufferPool = sync.Pool{ - New: func() interface{} { + New: func() any { return new(Writer) }, } @@ -32,7 +32,7 @@ func PutWriter(w *Writer) { } var gzipPool = sync.Pool{ - New: func() interface{} { + New: func() any { buf := new(bytes.Buffer) w := gzip.NewWriter(buf) return &GzipWriter{ @@ -64,7 +64,7 @@ type zlibWriter struct { } var zlibPool = sync.Pool{ - New: func() interface{} { + New: func() any { buf := new(bytes.Buffer) w := zlib.NewWriter(buf) return &zlibWriter{ diff --git a/binary/utils.go b/binary/utils.go index 999aa19b..07134191 100644 --- a/binary/utils.go +++ b/binary/utils.go @@ -118,7 +118,7 @@ func ToChunkedBytesF(b []byte, size int, f func([]byte)) { } } -func ToBytes(i interface{}) []byte { +func ToBytes(i any) []byte { return NewWriterF(func(w *Writer) { // TODO: more types switch t := i.(type) { diff --git a/client/client.go b/client/client.go index f8ff2f81..fb1decd9 100644 --- a/client/client.go +++ b/client/client.go @@ -132,7 +132,7 @@ type QiDianAccountInfo struct { } type handlerInfo struct { - fun func(i interface{}, err error) + fun func(i any, err error) dynamic bool params network.RequestParams } @@ -144,7 +144,7 @@ func (h *handlerInfo) getParams() network.RequestParams { return h.params } -var decoders = map[string]func(*QQClient, *network.IncomingPacketInfo, []byte) (interface{}, error){ +var decoders = map[string]func(*QQClient, *network.IncomingPacketInfo, []byte) (any, error){ "wtlogin.login": decodeLoginResponse, "wtlogin.exchange_emp": decodeExchangeEmpResponse, "wtlogin.trans_emp": decodeTransEmpResponse, @@ -435,10 +435,10 @@ func (c *QQClient) init(tokenLogin bool) error { } if tokenLogin { notify := make(chan struct{}) - d := c.waitPacket("StatSvc.ReqMSFOffline", func(i interface{}, err error) { + d := c.waitPacket("StatSvc.ReqMSFOffline", func(i any, err error) { notify <- struct{}{} }) - d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i interface{}, err error) { + d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i any, err error) { notify <- struct{}{} }) select { @@ -673,7 +673,7 @@ func (c *QQClient) FindGroup(code int64) *GroupInfo { return nil } -func (c *QQClient) SolveGroupJoinRequest(i interface{}, accept, block bool, reason string) { +func (c *QQClient) SolveGroupJoinRequest(i any, accept, block bool, reason string) { if accept { block = false reason = "" diff --git a/client/decoders.go b/client/decoders.go index aa646348..fa20b0ff 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -31,7 +31,7 @@ var ( ) // wtlogin.login -func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { reader := binary.NewReader(payload) reader.ReadUInt16() // sub command t := reader.ReadByte() @@ -179,7 +179,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b } // StatSvc.register -func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -196,7 +196,7 @@ func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, pa } // wtlogin.exchange_emp -func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { reader := binary.NewReader(payload) cmd := reader.ReadUInt16() t := reader.ReadByte() @@ -216,7 +216,7 @@ func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, paylo } // wtlogin.trans_emp -func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { if len(payload) < 48 { return nil, errors.New("missing payload length") } @@ -299,7 +299,7 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload } // ConfigPushSvc.PushReq -func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -372,7 +372,7 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b } // MessageSvc.PbGetMsg -func decodeMessageSvcPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMessageSvcPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := msg.GetMessageResponse{} err := proto.Unmarshal(payload, &rsp) if err != nil { @@ -383,7 +383,7 @@ func decodeMessageSvcPacket(c *QQClient, info *network.IncomingPacketInfo, paylo } // MessageSvc.PushNotify -func decodeSvcNotify(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeSvcNotify(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload[4:])) data := &jce.RequestDataVersion2{} @@ -410,7 +410,7 @@ func decodeSvcNotify(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) } // SummaryCard.ReqSummaryCard -func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -458,7 +458,7 @@ func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo } // friendlist.getFriendGroupList -func decodeFriendGroupListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeFriendGroupListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -483,7 +483,7 @@ func decodeFriendGroupListResponse(_ *QQClient, _ *network.IncomingPacketInfo, p } // friendlist.delFriend -func decodeFriendDeleteResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeFriendDeleteResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -496,7 +496,7 @@ func decodeFriendDeleteResponse(_ *QQClient, _ *network.IncomingPacketInfo, payl } // friendlist.GetTroopListReqV2 -func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -528,7 +528,7 @@ func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload } // friendlist.GetTroopMemberListReq -func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion3{} @@ -564,7 +564,7 @@ func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, p } // group_member_card.get_group_member_card_info -func decodeGroupMemberInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupMemberInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := pb.GroupMemberRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -597,7 +597,7 @@ func decodeGroupMemberInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, p } // LongConn.OffPicUp -func decodeOffPicUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOffPicUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := cmd0x352.RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -635,7 +635,7 @@ func decodeOffPicUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload } // OnlinePush.PbPushTransMsg -func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { info := msg.TransMsgInfo{} err := proto.Unmarshal(payload, &info) if err != nil { @@ -738,7 +738,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay } // ProfileService.Pb.ReqSystemMsgNew.Friend -func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := structmsg.RspSystemMsgNew{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -760,7 +760,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, pay } // MessageSvc.PushForceOffline -func decodeForceOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeForceOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -773,7 +773,7 @@ func decodeForceOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, payloa } // StatSvc.ReqMSFOffline -func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte) (interface{}, error) { +func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte) (any, error) { // c.lastLostMsg = "服务器端强制下线." c.Disconnect() // 这个decoder不能消耗太多时间, event另起线程处理 @@ -782,7 +782,7 @@ func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte } // OidbSvc.0xd79 -func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := &oidb.D79RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -794,7 +794,7 @@ func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload return nil, errors.New("no word received") } -func decodeSidExpiredPacket(c *QQClient, i *network.IncomingPacketInfo, _ []byte) (interface{}, error) { +func decodeSidExpiredPacket(c *QQClient, i *network.IncomingPacketInfo, _ []byte) (any, error) { _, err := c.sendAndWait(c.buildRequestChangeSigPacket(3554528)) if err != nil { return nil, errors.Wrap(err, "resign client error") @@ -824,6 +824,6 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) ( } */ -func ignoreDecoder(_ *QQClient, _ *network.IncomingPacketInfo, _ []byte) (interface{}, error) { +func ignoreDecoder(_ *QQClient, _ *network.IncomingPacketInfo, _ []byte) (any, error) { return nil, nil } diff --git a/client/events.go b/client/events.go index cdcc5bcf..f71c34a7 100644 --- a/client/events.go +++ b/client/events.go @@ -178,7 +178,7 @@ func (c *QQClient) dispatchMemberJoinedGuildEvent(e *MemberJoinGuildEvent) { } func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) { - c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f interface{}) bool { + c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f any) bool { go f.(func(*QQClient, *groupMessageReceiptEvent))(c, e) return true }) diff --git a/client/face.go b/client/face.go index dca3a22f..dec2ab4d 100644 --- a/client/face.go +++ b/client/face.go @@ -41,7 +41,7 @@ func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) { return c.uniPacket("Faceroam.OpReq", payload) } -func decodeFaceroamResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeFaceroamResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := faceroam.FaceroamRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/group_file.go b/client/group_file.go index bcdc5078..1e050cd7 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -402,7 +402,7 @@ func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId return c.uniPacket("OidbSvc.0x6d6_3", payload) } -func decodeOIDB6d81Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOIDB6d81Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D6D8RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -412,7 +412,7 @@ func decodeOIDB6d81Response(_ *QQClient, _ *network.IncomingPacketInfo, payload } // OidbSvc.0x6d6_2 -func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D6D6RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -426,7 +426,7 @@ func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil } -func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D6D6RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -435,7 +435,7 @@ func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload return rsp.DeleteFileRsp.GetClientWording(), nil } -func decodeOIDB6d60Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOIDB6d60Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D6D6RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -444,7 +444,7 @@ func decodeOIDB6d60Response(_ *QQClient, _ *network.IncomingPacketInfo, payload return rsp.UploadFileRsp, nil } -func decodeOIDB6d7Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOIDB6d7Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D6D7RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { diff --git a/client/group_info.go b/client/group_info.go index ed3e3b8d..f40e82d2 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -183,7 +183,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) { } // SummaryCard.ReqSearch -func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -221,7 +221,7 @@ func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo } // OidbSvc.0x88d_0 -func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D88DRspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -328,7 +328,7 @@ func (g *GroupInfo) AdministratorOrOwner() bool { } func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo { - r := g.Read(func(info *GroupInfo) interface{} { + r := g.Read(func(info *GroupInfo) any { return info.FindMemberWithoutLock(uin) }) if r == nil { @@ -360,7 +360,7 @@ func (g *GroupInfo) Update(f func(*GroupInfo)) { f(g) } -func (g *GroupInfo) Read(f func(*GroupInfo) interface{}) interface{} { +func (g *GroupInfo) Read(f func(*GroupInfo) any) any { g.lock.RLock() defer g.lock.RUnlock() return f(g) diff --git a/client/group_msg.go b/client/group_msg.go index acac1a86..18216c09 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -297,7 +297,7 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt } // OnlinePush.PbPushGroupMsg -func decodeGroupMessagePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupMessagePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { pkt := msg.PushMessagePacket{} err := proto.Unmarshal(payload, &pkt) if err != nil { @@ -332,7 +332,7 @@ func decodeGroupMessagePacket(c *QQClient, _ *network.IncomingPacketInfo, payloa return nil, nil } -func decodeMsgSendResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMsgSendResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := msg.SendMessageResponse{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -347,7 +347,7 @@ func decodeMsgSendResponse(c *QQClient, _ *network.IncomingPacketInfo, payload [ return nil, nil } -func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := msg.GetGroupMsgResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -396,7 +396,7 @@ func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, pa return ret, nil } -func decodeAtAllRemainResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeAtAllRemainResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.D8A7RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { @@ -589,7 +589,7 @@ func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand } // OidbSvc.0xeac_1/2 -func decodeEssenceMsgResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeEssenceMsgResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := &oidb.EACRspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { diff --git a/client/guild.go b/client/guild.go index df4da040..e9735b42 100644 --- a/client/guild.go +++ b/client/guild.go @@ -737,7 +737,7 @@ func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) { return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload) } -func decodeGuildPushFirstView(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGuildPushFirstView(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { firstViewMsg := new(channel.FirstViewMsg) if err := proto.Unmarshal(payload, firstViewMsg); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/guild_eventflow.go b/client/guild_eventflow.go index 8ebd10e1..ff1465b2 100644 --- a/client/guild_eventflow.go +++ b/client/guild_eventflow.go @@ -27,7 +27,7 @@ type tipsPushInfo struct { ChannelId uint64 } -func decodeGuildEventFlowPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGuildEventFlowPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { push := new(channel.MsgOnlinePush) if err := proto.Unmarshal(payload, push); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -223,7 +223,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody MessageId: t[0].Head.ContentHead.GetSeq(), CurrentReactions: decodeGuildMessageEmojiReactions(t[0]), } - tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i interface{}) bool { + tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i any) bool { if i == nil { return false } diff --git a/client/guild_msg.go b/client/guild_msg.go index fe0bd6a8..6f514101 100644 --- a/client/guild_msg.go +++ b/client/guild_msg.go @@ -230,7 +230,7 @@ func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []* return } -func decodeGuildImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGuildImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { body := new(cmd0x388.D388RspBody) if err := proto.Unmarshal(payload, body); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/image.go b/client/image.go index 7a723e72..d0d97186 100644 --- a/client/image.go +++ b/client/image.go @@ -90,7 +90,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee }.Encode() } - var r interface{} + var r any var err error var input highway.BdhInput switch target.SourceType { @@ -203,7 +203,7 @@ func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int return e, nil } -func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { +func (c *QQClient) ImageOcr(img any) (*OcrResponse, error) { url := "" switch e := img.(type) { case *message.GroupImageElement: @@ -362,7 +362,7 @@ func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, hei } // ImgStore.GroupPicUp -func decodeGroupImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { pkt := cmd0x388.D388RspBody{} err := proto.Unmarshal(payload, &pkt) if err != nil { @@ -389,7 +389,7 @@ func decodeGroupImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, p }, nil } -func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { pkt := cmd0x388.D388RspBody{} if err := proto.Unmarshal(payload, &pkt); err != nil { return nil, errors.Wrap(err, "unmarshal protobuf message error") @@ -404,7 +404,7 @@ func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo } // OidbSvc.0xe07_0 -func decodeImageOcrResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeImageOcrResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.DE07RspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { diff --git a/client/internal/network/packet.go b/client/internal/network/packet.go index bccfe152..492791ef 100644 --- a/client/internal/network/packet.go +++ b/client/internal/network/packet.go @@ -6,7 +6,7 @@ type IncomingPacketInfo struct { Params RequestParams } -type RequestParams map[string]interface{} +type RequestParams map[string]any func (p RequestParams) Bool(k string) bool { if p == nil { diff --git a/client/multimsg.go b/client/multimsg.go index 0348f903..f8f4b437 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -47,7 +47,7 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou } // MultiMsg.ApplyUp -func decodeMultiApplyUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMultiApplyUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { body := multimsg.MultiRspBody{} if err := proto.Unmarshal(payload, &body); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -87,7 +87,7 @@ func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) { } // MultiMsg.ApplyDown -func decodeMultiApplyDownResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMultiApplyDownResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { body := multimsg.MultiRspBody{} if err := proto.Unmarshal(payload, &body); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/network.go b/client/network.go index b826bae2..f1adba6f 100644 --- a/client/network.go +++ b/client/network.go @@ -148,9 +148,9 @@ func (c *QQClient) Disconnect() { } // sendAndWait 向服务器发送一个数据包, 并等待返回 -func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (interface{}, error) { +func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) { type T struct { - Response interface{} + Response any Error error } ch := make(chan T, 1) @@ -160,7 +160,7 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.Request p = params[0] } - c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) { + c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- T{ Response: i, Error: err, @@ -204,7 +204,7 @@ func (c *QQClient) sendPacket(pkt []byte) error { // waitPacket // 等待一个或多个数据包解析, 优先级低于 sendAndWait // 返回终止解析函数 -func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() { +func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() { c.waiters.Store(cmd, f) return func() { c.waiters.Delete(cmd) @@ -213,9 +213,9 @@ func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() { // waitPacketTimeoutSyncF // 等待一个数据包解析, 优先级低于 sendAndWait -func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(interface{}) bool) (r interface{}, e error) { +func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(any) bool) (r any, e error) { notifyChan := make(chan bool) - defer c.waitPacket(cmd, func(i interface{}, err error) { + defer c.waitPacket(cmd, func(i any, err error) { if filter(i) { r = i e = err @@ -234,7 +234,7 @@ func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, fil // 发送数据包并返回需要解析的 response func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) { ch := make(chan []byte, 1) - c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) { ch <- i.([]byte) }, dynamic: true}) + c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- i.([]byte) }, dynamic: true}) err := c.sendPacket(pkt) if err != nil { c.handlers.Delete(seq) @@ -337,7 +337,7 @@ func (c *QQClient) netLoop() { if decoder, ok := decoders[pkt.CommandName]; ok { // found predefined decoder info, ok := c.handlers.LoadAndDelete(pkt.SequenceId) - var decoded interface{} + var decoded any decoded = pkt.Payload if info == nil || !info.dynamic { decoded, err = decoder(c, &network.IncomingPacketInfo{ @@ -352,7 +352,7 @@ func (c *QQClient) netLoop() { if ok { info.fun(decoded, err) } else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait - f.(func(interface{}, error))(decoded, err) + f.(func(any, error))(decoded, err) } } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { // does not need decoder diff --git a/client/offline_file.go b/client/offline_file.go index f02223c3..a4b7e531 100644 --- a/client/offline_file.go +++ b/client/offline_file.go @@ -33,7 +33,7 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [ return seq, packet } -func decodeOfflineFileDownloadResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOfflineFileDownloadResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := cmd0x346.C346RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { c.error("unmarshal cmd0x346 rsp body error: %v", err) diff --git a/client/online_push.go b/client/online_push.go index b843e858..5b96c995 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -23,7 +23,7 @@ var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{ } // OnlinePush.ReqPush -func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} diff --git a/client/ptt.go b/client/ptt.go index 17c4185a..f53e5ac8 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -324,7 +324,7 @@ func (c *QQClient) buildC2CPttStoreBDHExt(target int64, md5 []byte, size, voiceL } // PttCenterSvr.ShortVideoDownReq -func decodePttShortVideoDownResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodePttShortVideoDownResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := pttcenter.ShortVideoRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -336,7 +336,7 @@ func decodePttShortVideoDownResponse(_ *QQClient, _ *network.IncomingPacketInfo, } // PttCenterSvr.GroupShortVideoUpReq -func decodeGroupShortVideoUploadResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeGroupShortVideoUploadResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := pttcenter.ShortVideoRspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/qidian.go b/client/qidian.go index ca07c707..37f0b148 100644 --- a/client/qidian.go +++ b/client/qidian.go @@ -152,7 +152,7 @@ func (c *QQClient) bigDataRequest(subCmd uint32, req proto.Message) ([]byte, err return tea.Decrypt(payload), nil } -func decodeLoginExtraResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeLoginExtraResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := cmd0x3f6.C3F6RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -168,7 +168,7 @@ func decodeLoginExtraResponse(c *QQClient, _ *network.IncomingPacketInfo, payloa return nil, nil } -func decodeConnKeyResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeConnKeyResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := cmd0x6ff.C501RspBody{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/recall.go b/client/recall.go index a65104e9..a6f3546c 100644 --- a/client/recall.go +++ b/client/recall.go @@ -92,7 +92,7 @@ func (c *QQClient) buildPrivateRecallPacket(uin, ts int64, msgSeq, random int32) return c.uniPacket("PbMessageSvc.PbMsgWithDraw", payload) } -func decodeMsgWithDrawResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMsgWithDrawResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := msg.MsgWithDrawResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") diff --git a/client/security.go b/client/security.go index 3ac00921..e7bc3e5e 100644 --- a/client/security.go +++ b/client/security.go @@ -48,7 +48,7 @@ func (c *QQClient) buildUrlCheckRequest(url string) (uint16, []byte) { return c.uniPacket("OidbSvc.0xbcb_0", payload) } -func decodeUrlCheckResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeUrlCheckResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := &oidb.DBCBRspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { diff --git a/client/sync.go b/client/sync.go index ae0bf24f..6f14a4c1 100644 --- a/client/sync.go +++ b/client/sync.go @@ -79,7 +79,7 @@ func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) { ret := &SessionSyncResponse{} notifyChan := make(chan bool) var groupNum int32 = -1 - stop := c.waitPacket("RegPrxySvc.PbSyncMsg", func(i interface{}, err error) { + stop := c.waitPacket("RegPrxySvc.PbSyncMsg", func(i any, err error) { if err != nil { return } @@ -285,7 +285,7 @@ func (c *QQClient) buildPrivateMsgReadedPacket(uin, time int64) (uint16, []byte) } // StatSvc.GetDevLoginInfo -func decodeDevListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeDevListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -307,7 +307,7 @@ func decodeDevListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload [ } // RegPrxySvc.PushParam -func decodePushParamPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodePushParamPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} @@ -352,7 +352,7 @@ func decodePushParamPacket(c *QQClient, _ *network.IncomingPacketInfo, payload [ } // RegPrxySvc.PbSyncMsg -func decodeMsgSyncResponse(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMsgSyncResponse(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := &msf.SvcRegisterProxyMsgResp{} if err := proto.Unmarshal(payload, rsp); err != nil { return nil, err @@ -396,7 +396,7 @@ func decodeMsgSyncResponse(c *QQClient, info *network.IncomingPacketInfo, payloa } // OnlinePush.PbC2CMsgSync -func decodeC2CSyncPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeC2CSyncPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (any, error) { m := msg.PbPushMsg{} if err := proto.Unmarshal(payload, &m); err != nil { return nil, err @@ -406,7 +406,7 @@ func decodeC2CSyncPacket(c *QQClient, info *network.IncomingPacketInfo, payload return nil, nil } -func decodeMsgReadedResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeMsgReadedResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := msg.PbMsgReadedReportResp{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, errors.Wrap(err, "failed to unmarshal protobuf message") @@ -420,7 +420,7 @@ func decodeMsgReadedResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload var loginNotifyLock sync.Mutex // StatSvc.SvcReqMSFLoginNotify -func decodeLoginNotifyPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeLoginNotifyPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { request := &jce.RequestPacket{} request.ReadFrom(jce.NewJceReader(payload)) data := &jce.RequestDataVersion2{} diff --git a/client/system_msg.go b/client/system_msg.go index c01a5999..c580df53 100644 --- a/client/system_msg.go +++ b/client/system_msg.go @@ -190,7 +190,7 @@ func (c *QQClient) buildSystemMsgFriendActionPacket(reqID, requester int64, acce } // ProfileService.Pb.ReqSystemMsgNew.Group -func decodeSystemMsgGroupPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeSystemMsgGroupPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := structmsg.RspSystemMsgNew{} if err := proto.Unmarshal(payload, &rsp); err != nil { return nil, err diff --git a/client/translate.go b/client/translate.go index f1ccdbd9..e0a4bb6b 100644 --- a/client/translate.go +++ b/client/translate.go @@ -34,7 +34,7 @@ func (c *QQClient) Translate(src, dst, text string) (string, error) { } // OidbSvc.0x990 -func decodeTranslateResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) { +func decodeTranslateResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (any, error) { rsp := oidb.TranslateRspBody{} err := unpackOIDBPackage(payload, &rsp) if err != nil { diff --git a/internal/generator/jce_gen/main.go b/internal/generator/jce_gen/main.go index ed418bd0..3439c0b2 100644 --- a/internal/generator/jce_gen/main.go +++ b/internal/generator/jce_gen/main.go @@ -173,7 +173,7 @@ func (g Generator) Generate(w io.Writer) { } } -func assert(cond bool, val interface{}) { +func assert(cond bool, val any) { if !cond { panic("assertion failed: " + fmt.Sprint(val)) } diff --git a/internal/proto/dynamic.go b/internal/proto/dynamic.go index c211c05d..a00339de 100644 --- a/internal/proto/dynamic.go +++ b/internal/proto/dynamic.go @@ -6,7 +6,7 @@ import ( "math" ) -type DynamicMessage map[uint64]interface{} +type DynamicMessage map[uint64]any type encoder struct { bytes.Buffer diff --git a/internal/proto/wrapper.go b/internal/proto/wrapper.go index c5865421..b43a2ac9 100644 --- a/internal/proto/wrapper.go +++ b/internal/proto/wrapper.go @@ -2,7 +2,7 @@ package proto import "github.com/RomiChan/protobuf/proto" -type Message = interface{} +type Message = any func Marshal(m Message) ([]byte, error) { return proto.Marshal(m) diff --git a/topic/feed.go b/topic/feed.go index b7bfa146..5472131e 100644 --- a/topic/feed.go +++ b/topic/feed.go @@ -55,7 +55,7 @@ type ( pack(patternId string, isPatternData bool) content } - content map[string]interface{} + content map[string]any ) var globalBlockId int64 = 0 From f7ced299d9ce46e2bbe07f22c1c2eeb28d3e86f2 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 17 Mar 2022 16:59:41 +0800 Subject: [PATCH 22/44] Update goimports.yml --- .github/workflows/goimports.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goimports.yml b/.github/workflows/goimports.yml index 8d8ae0a6..adc39586 100644 --- a/.github/workflows/goimports.yml +++ b/.github/workflows/goimports.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.13 + go-version: 1.18 - name: Check out code into the Go module directory uses: actions/checkout@v2 From d185826bee4ad5ed71b1185f1fef29b0c9581071 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 17:34:47 +0800 Subject: [PATCH 23/44] utils: avoid importing encoding/xml package --- utils/string.go | 66 ++++++++++++++++++++++++++++++++++++++++---- utils/string_test.go | 14 ++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 utils/string_test.go diff --git a/utils/string.go b/utils/string.go index eb702fe4..aa8b236d 100644 --- a/utils/string.go +++ b/utils/string.go @@ -1,11 +1,11 @@ package utils import ( - "encoding/xml" "math/rand" "reflect" "strconv" "strings" + "unicode/utf8" "unsafe" ) @@ -69,9 +69,63 @@ func S2B(s string) (b []byte) { return } -// XmlEscape xml escape string -func XmlEscape(c string) string { - buf := new(strings.Builder) - _ = xml.EscapeText(buf, []byte(c)) - return buf.String() +const ( + escQuot = """ // shorter than """ + escApos = "'" // shorter than "'" + escAmp = "&" + escLT = "<" + escGT = ">" + escTab = " " + escNL = " " + escCR = " " + escFFFD = "\uFFFD" // Unicode replacement character +) + +func isInCharacterRange(r rune) (inrange bool) { + return r == 0x09 || + r == 0x0A || + r == 0x0D || + r >= 0x20 && r <= 0xD7FF || + r >= 0xE000 && r <= 0xFFFD || + r >= 0x10000 && r <= 0x10FFFF +} + +// XmlEscape xml escape string +func XmlEscape(s string) string { + var esc string + var sb strings.Builder + sb.Grow(len(s)) + last := 0 + for i, r := range s { + width := utf8.RuneLen(r) + switch r { + case '"': + esc = escQuot + case '\'': + esc = escApos + case '&': + esc = escAmp + case '<': + esc = escLT + case '>': + esc = escGT + case '\t': + esc = escTab + case '\n': + esc = escNL + case '\r': + esc = escCR + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = escFFFD + break + } + continue + } + sb.WriteString(s[last:i]) + sb.WriteString(esc) + last = i + width + } + sb.WriteString(s[last:]) + return sb.String() } diff --git a/utils/string_test.go b/utils/string_test.go new file mode 100644 index 00000000..23310da9 --- /dev/null +++ b/utils/string_test.go @@ -0,0 +1,14 @@ +package utils + +import ( + "testing" +) + +func TestXmlEscape(t *testing.T) { + input := "A \x00 terminated string." + expected := "A \uFFFD terminated string." + text := XmlEscape(input) + if text != expected { + t.Errorf("have %v, want %v", text, expected) + } +} From f5950d72fa77ee3a80986644bea1dc4f0220da44 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 19:34:09 +0800 Subject: [PATCH 24/44] dep: update protobuf --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6e26ead4..915578c7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/MiraiGo go 1.18 require ( - github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 + github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 github.com/fumiama/imgsz v0.0.2 github.com/pierrec/lz4/v4 v4.1.11 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index ce0a0e55..14044917 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= -github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= +github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 8fa49fedb9948aa5ca3895d0db48a2f5e45619d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Fri, 18 Mar 2022 20:36:13 +0800 Subject: [PATCH 25/44] feat: support get group announcement (#258) * feat: support get group announcement * refactor: avoid importing new dependent package * refactor: prettify --- client/http_api.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++ utils/http.go | 8 ++--- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/client/http_api.go b/client/http_api.go index 118daf0d..3d37f998 100644 --- a/client/http_api.go +++ b/client/http_api.go @@ -165,6 +165,32 @@ func (c *QQClient) GetTts(text string) ([]byte, error) { /* -------- GroupNotice -------- */ +type groupNoticeRsp struct { + Feeds []*struct { + SenderId uint32 `json:"u"` + PublishTime uint64 `json:"pubt"` + Message struct { + Text string `json:"text"` + Images []noticeImage `json:"pics"` + } `json:"msg"` + } `json:"feeds"` +} + +type GroupNoticeMessage struct { + SenderId uint32 `json:"sender_id"` + PublishTime uint64 `json:"publish_time"` + Message struct { + Text string `json:"text"` + Images []GroupNoticeImage `json:"images"` + } `json:"message"` +} + +type GroupNoticeImage struct { + Height string `json:"height"` + Width string `json:"width"` + ID string `json:"id"` +} + type noticePicUpResponse struct { ErrorCode int `json:"ec"` ErrorMessage string `json:"em"` @@ -177,6 +203,66 @@ type noticeImage struct { ID string `json:"id"` } +func (c *QQClient) GetGroupNotice(groupCode int64) (l []*GroupNoticeMessage, err error) { + + v := url.Values{} + v.Set("bkn", strconv.Itoa(c.getCSRFToken())) + v.Set("qid", strconv.FormatInt(groupCode, 10)) + v.Set("ft", "23") + v.Set("ni", "1") + v.Set("n", "1") + v.Set("i", "1") + v.Set("log_read", "1") + v.Set("platform", "1") + v.Set("s", "-1") + v.Set("n", "20") + + req, _ := http.NewRequest(http.MethodGet, "https://web.qun.qq.com/cgi-bin/announce/get_t_list?"+v.Encode(), nil) + req.Header.Set("Cookie", c.getCookies()) + rsp, err := utils.Client.Do(req) + if err != nil { + return + } + defer rsp.Body.Close() + + r := groupNoticeRsp{} + err = json.NewDecoder(rsp.Body).Decode(&r) + if err != nil { + return + } + + return c.parseGroupNoticeJson(&r), nil +} + +func (c *QQClient) parseGroupNoticeJson(s *groupNoticeRsp) []*GroupNoticeMessage { + o := make([]*GroupNoticeMessage, 0, len(s.Feeds)) + for _, v := range s.Feeds { + + ims := make([]GroupNoticeImage, 0, len(v.Message.Images)) + for i := 0; i < len(v.Message.Images); i++ { + ims = append(ims, GroupNoticeImage{ + Height: v.Message.Images[i].Height, + Width: v.Message.Images[i].Width, + ID: v.Message.Images[i].ID, + }) + } + + o = append(o, &GroupNoticeMessage{ + SenderId: v.SenderId, + PublishTime: v.PublishTime, + Message: struct { + Text string `json:"text"` + Images []GroupNoticeImage `json:"images"` + }{ + Text: v.Message.Text, + Images: ims, + }, + }) + } + + return o +} + func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) { buf := new(bytes.Buffer) w := multipart.NewWriter(buf) diff --git a/utils/http.go b/utils/http.go index ea594e84..df1caa6c 100644 --- a/utils/http.go +++ b/utils/http.go @@ -8,7 +8,7 @@ import ( "strings" ) -var client = &http.Client{ +var Client = &http.Client{ Transport: &http.Transport{ ForceAttemptHTTP2: true, MaxConnsPerHost: 0, @@ -34,7 +34,7 @@ func HttpPostBytes(url string, data []byte) ([]byte, error) { } req.Header["User-Agent"] = []string{"QQ/8.2.0.1296 CFNetwork/1126"} req.Header["Net-Type"] = []string{"Wifi"} - resp, err := client.Do(req) + resp, err := Client.Do(req) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func HttpPostBytesWithCookie(url string, data []byte, cookie string, contentType if cookie != "" { req.Header["Cookie"] = []string{cookie} } - resp, err := client.Do(req) + resp, err := Client.Do(req) if err != nil { return nil, err } @@ -125,7 +125,7 @@ func HTTPGetReadCloser(url string, cookie string) (io.ReadCloser, error) { if cookie != "" { req.Header["Cookie"] = []string{cookie} } - resp, err := client.Do(req) + resp, err := Client.Do(req) if err != nil { return nil, err } From bc4e9f3c9589597d4afcfb4537660e03ff58b110 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 21:14:45 +0800 Subject: [PATCH 26/44] client: delete GetVipInfo This api is broken. --- client/http_api.go | 46 ---------------------------------- client/internal/highway/bdh.go | 11 +++++--- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/client/http_api.go b/client/http_api.go index 3d37f998..35ae0b63 100644 --- a/client/http_api.go +++ b/client/http_api.go @@ -11,7 +11,6 @@ import ( "net/textproto" "net/url" "strconv" - "strings" "github.com/pkg/errors" @@ -21,51 +20,6 @@ import ( "github.com/Mrs4s/MiraiGo/utils" ) -/* -------- VipInfo -------- */ - -type VipInfo struct { - Uin int64 - Name string - Level int - LevelSpeed float64 - VipLevel string - VipGrowthSpeed int - VipGrowthTotal int -} - -func (c *QQClient) GetVipInfo(target int64) (*VipInfo, error) { - b, err := utils.HttpGetBytes(fmt.Sprintf("https://h5.vip.qq.com/p/mc/cardv2/other?platform=1&qq=%d&adtag=geren&aid=mvip.pingtai.mobileqq.androidziliaoka.fromqita", target), c.getCookiesWithDomain("h5.vip.qq.com")) - if err != nil { - return nil, err - } - ret := VipInfo{Uin: target} - b = b[bytes.Index(b, []byte(``))+24:] - t := b[:bytes.Index(b, []byte(``))] - ret.Name = string(t) - b = b[bytes.Index(b, []byte(`LV`))+17:] - t = b[:bytes.Index(b, []byte(`

`))] - ret.Level, _ = strconv.Atoi(string(t)) - b = b[bytes.Index(b, []byte(`
`))+35:] - b = b[bytes.Index(b, []byte(`

`))+3:] - t = b[:bytes.Index(b, []byte(`倍`))] - ret.LevelSpeed, _ = strconv.ParseFloat(string(t), 64) - b = b[bytes.Index(b, []byte(`

`))+35:] - b = b[bytes.Index(b, []byte(`

`))+3:] - st := string(b[:bytes.Index(b, []byte(`

`))]) - st = strings.Replace(st, "", "", 1) - st = strings.Replace(st, "", "", 1) - ret.VipLevel = st - b = b[bytes.Index(b, []byte(`
`))+35:] - b = b[bytes.Index(b, []byte(`

`))+3:] - t = b[:bytes.Index(b, []byte(`

`))] - ret.VipGrowthSpeed, _ = strconv.Atoi(string(t)) - b = b[bytes.Index(b, []byte(`
`))+35:] - b = b[bytes.Index(b, []byte(`

`))+3:] - t = b[:bytes.Index(b, []byte(`

`))] - ret.VipGrowthTotal, _ = strconv.Atoi(string(t)) - return &ret, nil -} - /* -------- GroupHonorInfo -------- */ type ( diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 3b1e8fc6..97d571f3 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -57,11 +57,14 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { } const chunkSize = 256 * 1024 - var rspExt []byte + var rspExt, chunk []byte offset := 0 - chunk := make([]byte, chunkSize) + if input.Size > chunkSize { + chunk = make([]byte, chunkSize) + } else { + chunk = make([]byte, input.Size) + } for { - chunk = chunk[:chunkSize] rl, err := io.ReadFull(input.Body, chunk) if errors.Is(err, io.EOF) { break @@ -121,7 +124,7 @@ func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, return nil, err } - const blockSize int64 = 1024 * 512 + const blockSize int64 = 256 * 1024 var ( rspExt []byte completedThread uint32 From 87e53dd6e2f22d80cf4e5bc9722dcf22e743b07e Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 21:32:22 +0800 Subject: [PATCH 27/44] client: update ShutUpTimestamp when receive mute event --- client/online_push.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/online_push.go b/client/online_push.go index 5b96c995..e237e0d1 100644 --- a/client/online_push.go +++ b/client/online_push.go @@ -53,6 +53,16 @@ func decodeOnlinePushReqPacket(c *QQClient, info *network.IncomingPacketInfo, pa r.ReadBytes(6) target := int64(uint32(r.ReadInt32())) t := r.ReadInt32() + + if target != 0 { + member := c.FindGroup(groupCode).FindMember(target) + if t > 0 { + member.ShutUpTimestamp = time.Now().Add(time.Second * time.Duration(t)).Unix() + } else { + member.ShutUpTimestamp = 0 + } + } + c.GroupMuteEvent.dispatch(c, &GroupMuteEvent{ GroupCode: groupCode, OperatorUin: operator, From 35f774b86afa4b61522e5ade35462dfcdcdc08db Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 22:51:14 +0800 Subject: [PATCH 28/44] client: fix wrong event dispatch --- client/private_msg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/private_msg.go b/client/private_msg.go index 7825f51b..13afd518 100644 --- a/client/private_msg.go +++ b/client/private_msg.go @@ -59,7 +59,7 @@ func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) * }, Elements: m.Elements, } - go c.PrivateMessageEvent.dispatch(c, ret) + go c.SelfPrivateMessageEvent.dispatch(c, ret) return ret } From 2dfc8f0cbc9167139bb6c0aea4ab131c02a390f5 Mon Sep 17 00:00:00 2001 From: lz1998 <875543533@qq.com> Date: Fri, 18 Mar 2022 23:22:11 +0800 Subject: [PATCH 29/44] delete useless code (#262) --- client/internal/highway/highway.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index 83023c8f..f641c94d 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -66,8 +66,6 @@ func (s *Session) Upload(addr Addr, input Input) error { chunk := make([]byte, chunkSize) offset := 0 reader := binary.NewNetworkReader(conn) - w := binary.SelectWriter() - defer binary.PutWriter(w) for { chunk = chunk[:chunkSize] rl, err := io.ReadFull(input.Body, chunk) From 5903226f253482238db087b2308532c90185cf97 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Mar 2022 10:03:10 +0800 Subject: [PATCH 30/44] client: remove some err check `(*bytes.Buffer).Write` always return nil error, so we don't need error check --- client/http_api.go | 40 ++++++++-------------------------------- client/image.go | 1 - client/private_msg.go | 1 - client/ptt.go | 1 - message/elements.go | 6 ++++-- message/message.go | 2 +- 6 files changed, 13 insertions(+), 38 deletions(-) diff --git a/client/http_api.go b/client/http_api.go index 35ae0b63..98a18afb 100644 --- a/client/http_api.go +++ b/client/http_api.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "html" - "io" "mime/multipart" "net/http" "net/textproto" @@ -158,7 +157,6 @@ type noticeImage struct { } func (c *QQClient) GetGroupNotice(groupCode int64) (l []*GroupNoticeMessage, err error) { - v := url.Values{} v.Set("bkn", strconv.Itoa(c.getCSRFToken())) v.Set("qid", strconv.FormatInt(groupCode, 10)) @@ -220,33 +218,15 @@ func (c *QQClient) parseGroupNoticeJson(s *groupNoticeRsp) []*GroupNoticeMessage func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) { buf := new(bytes.Buffer) w := multipart.NewWriter(buf) - err := w.WriteField("bkn", strconv.Itoa(c.getCSRFToken())) - if err != nil { - return nil, errors.Wrap(err, "write multipart failed") - } - err = w.WriteField("source", "troopNotice") - if err != nil { - return nil, errors.Wrap(err, "write multipart failed") - } - err = w.WriteField("m", "0") - if err != nil { - return nil, errors.Wrap(err, "write multipart failed") - } + _ = w.WriteField("bkn", strconv.Itoa(c.getCSRFToken())) + _ = w.WriteField("source", "troopNotice") + _ = w.WriteField("m", "0") h := make(textproto.MIMEHeader) h.Set("Content-Disposition", `form-data; name="pic_up"; filename="temp_uploadFile.png"`) h.Set("Content-Type", "image/png") - fw, err := w.CreatePart(h) - if err != nil { - return nil, errors.Wrap(err, "create multipart field failed") - } - _, err = fw.Write(img) - if err != nil { - return nil, errors.Wrap(err, "write multipart failed") - } - err = w.Close() - if err != nil { - return nil, errors.Wrap(err, "close multipart failed") - } + fw, _ := w.CreatePart(h) + _, _ = fw.Write(img) + _ = w.Close() req, err := http.NewRequest("POST", "https://web.qun.qq.com/cgi-bin/announce/upload_img", buf) if err != nil { return nil, errors.Wrap(err, "new request error") @@ -258,12 +238,8 @@ func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) { return nil, errors.Wrap(err, "post error") } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, errors.Wrap(err, "read body error") - } - res := noticePicUpResponse{} - err = json.Unmarshal(body, &res) + var res noticePicUpResponse + err = json.NewDecoder(resp.Body).Decode(&res) if err != nil { return nil, errors.Wrap(err, "failed to unmarshal json") } diff --git a/client/image.go b/client/image.go index d0d97186..a4e2a3eb 100644 --- a/client/image.go +++ b/client/image.go @@ -329,7 +329,6 @@ func (c *QQClient) uploadOcrImage(img io.Reader, size int32, sum []byte) (string Sum: sum, Ticket: c.highwaySession.SigSession, Ext: ext, - Encrypt: false, }) if err != nil { return "", errors.Wrap(err, "upload ocr image error") diff --git a/client/private_msg.go b/client/private_msg.go index 13afd518..a899089f 100644 --- a/client/private_msg.go +++ b/client/private_msg.go @@ -192,7 +192,6 @@ func (c *QQClient) buildGroupTempSendingPacket(groupUin, target int64, msgSeq, r } func (c *QQClient) buildWPATempSendingPacket(uin int64, sig []byte, msgSeq, r int32, time int64, m *message.SendingMessage) (uint16, []byte) { - req := &msg.SendMessageRequest{ RoutingHead: &msg.RoutingHead{WpaTmp: &msg.WPATmp{ ToUin: proto.Uint64(uint64(uin)), diff --git a/client/ptt.go b/client/ptt.go index f53e5ac8..7ed1ea93 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -79,7 +79,6 @@ func (c *QQClient) UploadVoice(target message.Source, voice io.ReadSeeker) (*mes Size: length, Ticket: c.highwaySession.SigSession, Ext: ext, - Encrypt: false, }) if err != nil { return nil, err diff --git a/message/elements.go b/message/elements.go index c1d02ee0..c90e6015 100644 --- a/message/elements.go +++ b/message/elements.go @@ -103,8 +103,10 @@ type AnimatedSticker struct { Name string } -type RedBagMessageType int -type AtType int +type ( + RedBagMessageType int + AtType int +) // /com/tencent/mobileqq/data/MessageForQQWalletMsg.java const ( diff --git a/message/message.go b/message/message.go index 201fd169..2cb9673b 100644 --- a/message/message.go +++ b/message/message.go @@ -778,7 +778,7 @@ func splitPlainMessage(content string) []IMessageElement { } } if last != len(content) { - splittedMessage = append(splittedMessage, NewText(content[last:len(content)])) + splittedMessage = append(splittedMessage, NewText(content[last:])) } return splittedMessage From 79f34a47b833524a454b50663965af4e8a46ce1f Mon Sep 17 00:00:00 2001 From: lz1998 <875543533@qq.com> Date: Sun, 20 Mar 2022 01:06:05 +0800 Subject: [PATCH 31/44] =?UTF-8?q?=E7=AE=80=E5=8C=96URL=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=20(#263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update image url * %X --- message/image.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/message/image.go b/message/image.go index 6e596b7e..64300b5d 100644 --- a/message/image.go +++ b/message/image.go @@ -1,9 +1,7 @@ package message import ( - "strings" - - "github.com/Mrs4s/MiraiGo/binary" + "fmt" "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/internal/proto" ) @@ -73,7 +71,7 @@ func NewGroupImage(id string, md5 []byte, fid int64, size, width, height, imageT ImageType: imageType, Width: width, Height: height, - Url: "https://gchat.qpic.cn/gchatpic_new/1/0-0-" + strings.ReplaceAll(binary.CalculateImageResourceId(md5)[1:37], "-", "") + "/0?term=2", + Url: fmt.Sprintf("https://gchat.qpic.cn/gchatpic_new/1/0-0-%X/0?term=2", md5), } } From 436482c7db40aafda5b301b44455d0b71b0819a1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Mar 2022 17:06:33 +0000 Subject: [PATCH 32/44] ci(chore): Fix stylings --- message/image.go | 1 + 1 file changed, 1 insertion(+) diff --git a/message/image.go b/message/image.go index 64300b5d..173153d2 100644 --- a/message/image.go +++ b/message/image.go @@ -2,6 +2,7 @@ package message import ( "fmt" + "github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/internal/proto" ) From 714961d68f3dcd6956771d7b8bdea70d96ad65fd Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 20 Mar 2022 13:48:02 +0800 Subject: [PATCH 33/44] client: use `%x` placeholder --- client/builders.go | 4 ++-- client/decoders.go | 3 +-- client/group_file.go | 2 +- client/guild_msg.go | 6 +++--- client/image.go | 8 ++++---- client/ptt.go | 5 +++-- message/pack.go | 3 +-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/client/builders.go b/client/builders.go index 0154ae6a..ca9e85c4 100644 --- a/client/builders.go +++ b/client/builders.go @@ -2,7 +2,7 @@ package client import ( "crypto/md5" - "encoding/hex" + "fmt" "math/rand" "time" @@ -903,7 +903,7 @@ func (c *QQClient) buildOffPicUpPacket(target int64, md5 []byte, size int32) (ui DstUin: proto.Uint64(uint64(target)), FileMd5: md5, FileSize: proto.Uint64(uint64(size)), - FileName: []byte(hex.EncodeToString(md5) + ".jpg"), + FileName: []byte(fmt.Sprintf("%x.jpg", md5)), SrcTerm: proto.Uint32(5), PlatformType: proto.Uint32(9), BuType: proto.Uint32(1), diff --git a/client/decoders.go b/client/decoders.go index fa20b0ff..f8452d4b 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -2,7 +2,6 @@ package client import ( "crypto/md5" - "encoding/hex" "net" "strconv" "strings" @@ -173,7 +172,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b } c.debug("unknown login response: %v", t) for k, v := range m { - c.debug("Type: %v Value: %v", strconv.FormatInt(int64(k), 16), hex.EncodeToString(v)) + c.debug("Type: %d Value: %x", k, v) } return nil, errors.Errorf("unknown login response: %v", t) // ? } diff --git a/client/group_file.go b/client/group_file.go index 1e050cd7..bb3c428a 100644 --- a/client/group_file.go +++ b/client/group_file.go @@ -104,7 +104,7 @@ func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32) return "" } url := i.(string) - url += "?fname=" + hex.EncodeToString([]byte(fileId)) + url += fmt.Sprintf("?fname=%x", fileId) return url } diff --git a/client/guild_msg.go b/client/guild_msg.go index 6f514101..378a8125 100644 --- a/client/guild_msg.go +++ b/client/guild_msg.go @@ -1,7 +1,7 @@ package client import ( - "encoding/hex" + "fmt" "io" "math/rand" "strconv" @@ -88,7 +88,7 @@ func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size u if body.IsExists { return &message.GuildImageElement{ FileId: body.FileId, - FilePath: hex.EncodeToString(hash) + ".jpg", + FilePath: fmt.Sprintf("%x.jpg", hash), Size: int32(size), DownloadIndex: body.DownloadIndex, Width: body.Width, @@ -177,7 +177,7 @@ func (c *QQClient) buildGuildImageStorePacket(guildId, channelId uint64, hash [] FileId: proto.Uint64(0), FileMd5: hash, FileSize: &size, - FileName: []byte(hex.EncodeToString(hash) + ".jpg"), + FileName: []byte(fmt.Sprintf("%x.jpg", hash)), SrcTerm: proto.Uint32(5), PlatformType: proto.Uint32(9), BuType: proto.Uint32(211), diff --git a/client/image.go b/client/image.go index a4e2a3eb..84b8e810 100644 --- a/client/image.go +++ b/client/image.go @@ -1,7 +1,7 @@ package client import ( - "encoding/hex" + "fmt" "io" "math/rand" "strings" @@ -154,7 +154,7 @@ ok: } return &message.GuildImageElement{ FileId: rsp.FileId, - FilePath: hex.EncodeToString(fh) + ".jpg", + FilePath: fmt.Sprintf("%x.jpg", fh), Size: int32(length), DownloadIndex: rsp.DownloadIndex, Width: width, @@ -214,7 +214,7 @@ func (c *QQClient) ImageOcr(img any) (*OcrResponse, error) { } _ = b.Close() } - rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height)) + rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(url, fmt.Sprintf("%X", e.Md5), e.Size, e.Width, e.Height)) if err != nil { return nil, err } @@ -399,7 +399,7 @@ func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo if len(pkt.GetimgUrlRsp[0].FailMsg) != 0 { return nil, errors.New(utils.B2S(pkt.GetimgUrlRsp[0].FailMsg)) } - return "https://" + utils.B2S(pkt.GetimgUrlRsp[0].DownDomain) + utils.B2S(pkt.GetimgUrlRsp[0].BigDownPara), nil + return fmt.Sprintf("https://%s%s", pkt.GetimgUrlRsp[0].DownDomain, pkt.GetimgUrlRsp[0].BigDownPara), nil } // OidbSvc.0xe07_0 diff --git a/client/ptt.go b/client/ptt.go index 7ed1ea93..024d3097 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -3,6 +3,7 @@ package client import ( "crypto/md5" "encoding/hex" + "fmt" "io" "github.com/pkg/errors" @@ -90,7 +91,7 @@ func (c *QQClient) UploadVoice(target message.Source, voice io.ReadSeeker) (*mes FileType: proto.Int32(4), SrcUin: &c.Uin, FileMd5: fh, - FileName: proto.String(hex.EncodeToString(fh) + ".amr"), + FileName: proto.String(fmt.Sprintf("%x.amr", fh)), FileSize: proto.Int32(int32(length)), BoolValid: proto.Bool(true), } @@ -259,7 +260,7 @@ func (c *QQClient) buildPttShortVideoProto(target message.Source, videoHash, thu ChatType: chatType, ClientType: 2, Info: &pttcenter.ShortVideoFileInfo{ - FileName: hex.EncodeToString(videoHash) + ".mp4", + FileName: fmt.Sprintf("%x.mp4", videoHash), FileMd5: videoHash, ThumbFileMd5: thumbHash, FileSize: videoSize, diff --git a/message/pack.go b/message/pack.go index 37af9a7c..bb56ad70 100644 --- a/message/pack.go +++ b/message/pack.go @@ -1,7 +1,6 @@ package message import ( - "encoding/hex" "fmt" "github.com/Mrs4s/MiraiGo/binary" @@ -126,7 +125,7 @@ func (e *ShortVideoElement) Pack() (r []*msg.Elem) { video := &msg.VideoFile{ FileUuid: e.Uuid, FileMd5: e.Md5, - FileName: []byte(hex.EncodeToString(e.Md5) + ".mp4"), + FileName: []byte(fmt.Sprintf("%x.mp4", e.Md5)), FileFormat: proto.Int32(3), FileTime: proto.Int32(10), FileSize: proto.Int32(e.Size), From 3dc7dc4fdd66c3ce5dd1445b976f68c0a893ba8d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 20 Mar 2022 13:55:08 +0800 Subject: [PATCH 34/44] client: use buffered channel this channel use receive with timeout, so we need use buffered to avoid memory leak. --- client/client.go | 2 +- client/network.go | 2 +- client/richmsg.go | 2 +- client/sync.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/client.go b/client/client.go index fb1decd9..430cb000 100644 --- a/client/client.go +++ b/client/client.go @@ -434,7 +434,7 @@ func (c *QQClient) init(tokenLogin bool) error { return errors.Wrap(err, "register error") } if tokenLogin { - notify := make(chan struct{}) + notify := make(chan struct{}, 2) d := c.waitPacket("StatSvc.ReqMSFOffline", func(i any, err error) { notify <- struct{}{} }) diff --git a/client/network.go b/client/network.go index f1adba6f..9fab6e76 100644 --- a/client/network.go +++ b/client/network.go @@ -214,7 +214,7 @@ func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() { // waitPacketTimeoutSyncF // 等待一个数据包解析, 优先级低于 sendAndWait func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(any) bool) (r any, e error) { - notifyChan := make(chan bool) + notifyChan := make(chan bool, 4) defer c.waitPacket(cmd, func(i any, err error) { if filter(i) { r = i diff --git a/client/richmsg.go b/client/richmsg.go index efa3ba69..88356aaf 100644 --- a/client/richmsg.go +++ b/client/richmsg.go @@ -66,7 +66,7 @@ var musicType = [...]musicTypeInfo{ // SendGroupMusicShare 发送群聊音乐卡片 func (c *QQClient) SendGroupMusicShare(target int64, msg *message.MusicShareElement) (*message.GroupMessage, error) { - ch := make(chan *message.GroupMessage) + ch := make(chan *message.GroupMessage, 2) eid := utils.RandomString(6) c.onGroupMessageReceipt(eid, func(c *QQClient, e *groupMessageReceiptEvent) { for _, elem := range e.Msg.Elements { diff --git a/client/sync.go b/client/sync.go index 6f14a4c1..2a75a986 100644 --- a/client/sync.go +++ b/client/sync.go @@ -77,7 +77,7 @@ func (c *QQClient) RefreshStatus() error { // SyncSessions 同步会话列表 func (c *QQClient) SyncSessions() (*SessionSyncResponse, error) { ret := &SessionSyncResponse{} - notifyChan := make(chan bool) + notifyChan := make(chan bool, 4) var groupNum int32 = -1 stop := c.waitPacket("RegPrxySvc.PbSyncMsg", func(i any, err error) { if err != nil { From 38990f6e1cf9ca0785709d03b66237a713338d0b Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 20 Mar 2022 15:07:54 +0800 Subject: [PATCH 35/44] client: use generic version sync.Map --- client/client.go | 10 +- client/global.go | 7 +- client/handler_map_gen.go | 387 -------------------------------------- client/network.go | 2 +- go.mod | 1 + go.sum | 2 + 6 files changed, 13 insertions(+), 396 deletions(-) delete mode 100644 client/handler_map_gen.go diff --git a/client/client.go b/client/client.go index 430cb000..8b747945 100644 --- a/client/client.go +++ b/client/client.go @@ -13,6 +13,8 @@ import ( "github.com/pkg/errors" "go.uber.org/atomic" + "github.com/RomiChan/syncx" + "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client/internal/auth" "github.com/Mrs4s/MiraiGo/client/internal/highway" @@ -23,8 +25,6 @@ import ( "github.com/Mrs4s/MiraiGo/utils" ) -//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]*handlerInfo" - type QQClient struct { Uin int64 PasswordMd5 [16]byte @@ -57,8 +57,8 @@ type QQClient struct { logger Logger // internal state - handlers HandlerMap - waiters sync.Map + handlers syncx.Map[uint16, *handlerInfo] + waiters syncx.Map[string, func(any, error)] servers []*net.TCPAddr currServerIndex int retryTimes int @@ -111,7 +111,7 @@ type QQClient struct { lastC2CMsgTime int64 transCache *utils.Cache[unit] groupSysMsgCache *GroupSystemMessages - msgBuilders sync.Map + msgBuilders syncx.Map[int32, *messageBuilder] onlinePushCache *utils.Cache[unit] heartbeatEnabled bool requestPacketRequestID atomic.Int32 diff --git a/client/global.go b/client/global.go index 514473eb..d1c5db79 100644 --- a/client/global.go +++ b/client/global.go @@ -244,14 +244,15 @@ func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage { } func (c *QQClient) messageBuilder(seq int32) *messageBuilder { - builder := &messageBuilder{} - actual, ok := c.msgBuilders.LoadOrStore(seq, builder) + actual, ok := c.msgBuilders.Load(seq) if !ok { + builder := &messageBuilder{} + actual, _ = c.msgBuilders.LoadOrStore(seq, builder) time.AfterFunc(time.Minute, func() { c.msgBuilders.Delete(seq) // delete avoid memory leak }) } - return actual.(*messageBuilder) + return actual } type messageBuilder struct { diff --git a/client/handler_map_gen.go b/client/handler_map_gen.go deleted file mode 100644 index d88a52fb..00000000 --- a/client/handler_map_gen.go +++ /dev/null @@ -1,387 +0,0 @@ -// Code generated by syncmap; DO NOT EDIT. - -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package client - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -// Map is like a Go map[interface{}]interface{} but is safe for concurrent use -// by multiple goroutines without additional locking or coordination. -// Loads, stores, and deletes run in amortized constant time. -// -// The Map type is specialized. Most code should use a plain Go map instead, -// with separate locking or coordination, for better type safety and to make it -// easier to maintain other invariants along with the map content. -// -// The Map type is optimized for two common use cases: (1) when the entry for a given -// key is only ever written once but read many times, as in caches that only grow, -// or (2) when multiple goroutines read, write, and overwrite entries for disjoint -// sets of keys. In these two cases, use of a Map may significantly reduce lock -// contention compared to a Go map paired with a separate Mutex or RWMutex. -// -// The zero Map is empty and ready for use. A Map must not be copied after first use. -type HandlerMap struct { - mu sync.Mutex - - // read contains the portion of the map's contents that are safe for - // concurrent access (with or without mu held). - // - // The read field itself is always safe to load, but must only be stored with - // mu held. - // - // Entries stored in read may be updated concurrently without mu, but updating - // a previously-expunged entry requires that the entry be copied to the dirty - // map and unexpunged with mu held. - read atomic.Value // readOnly - - // dirty contains the portion of the map's contents that require mu to be - // held. To ensure that the dirty map can be promoted to the read map quickly, - // it also includes all of the non-expunged entries in the read map. - // - // Expunged entries are not stored in the dirty map. An expunged entry in the - // clean map must be unexpunged and added to the dirty map before a new value - // can be stored to it. - // - // If the dirty map is nil, the next write to the map will initialize it by - // making a shallow copy of the clean map, omitting stale entries. - dirty map[uint16]*entryHandlerMap - - // misses counts the number of loads since the read map was last updated that - // needed to lock mu to determine whether the key was present. - // - // Once enough misses have occurred to cover the cost of copying the dirty - // map, the dirty map will be promoted to the read map (in the unamended - // state) and the next store to the map will make a new dirty copy. - misses int -} - -// readOnly is an immutable struct stored atomically in the Map.read field. -type readOnlyHandlerMap struct { - m map[uint16]*entryHandlerMap - amended bool // true if the dirty map contains some key not in m. -} - -// expunged is an arbitrary pointer that marks entries which have been deleted -// from the dirty map. -var expungedHandlerMap = unsafe.Pointer(new(*handlerInfo)) - -// An entry is a slot in the map corresponding to a particular key. -type entryHandlerMap struct { - // p points to the interface{} value stored for the entry. - // - // If p == nil, the entry has been deleted and m.dirty == nil. - // - // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry - // is missing from m.dirty. - // - // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty - // != nil, in m.dirty[key]. - // - // An entry can be deleted by atomic replacement with nil: when m.dirty is - // next created, it will atomically replace nil with expunged and leave - // m.dirty[key] unset. - // - // An entry's associated value can be updated by atomic replacement, provided - // p != expunged. If p == expunged, an entry's associated value can be updated - // only after first setting m.dirty[key] = e so that lookups using the dirty - // map find the entry. - p unsafe.Pointer // *interface{} -} - -func newEntryHandlerMap(i *handlerInfo) *entryHandlerMap { - return &entryHandlerMap{p: unsafe.Pointer(&i)} -} - -// Load returns the value stored in the map for a key, or nil if no -// value is present. -// The ok result indicates whether value was found in the map. -func (m *HandlerMap) Load(key uint16) (value *handlerInfo, ok bool) { - read, _ := m.read.Load().(readOnlyHandlerMap) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - // Avoid reporting a spurious miss if m.dirty got promoted while we were - // blocked on m.mu. (If further loads of the same key will not miss, it's - // not worth copying the dirty map for this key.) - read, _ = m.read.Load().(readOnlyHandlerMap) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - if !ok { - return value, false - } - return e.load() -} - -func (e *entryHandlerMap) load() (value *handlerInfo, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedHandlerMap { - return value, false - } - return *(**handlerInfo)(p), true -} - -// Store sets the value for a key. -func (m *HandlerMap) Store(key uint16, value *handlerInfo) { - read, _ := m.read.Load().(readOnlyHandlerMap) - if e, ok := read.m[key]; ok && e.tryStore(&value) { - return - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyHandlerMap) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - // The entry was previously expunged, which implies that there is a - // non-nil dirty map and this entry is not in it. - m.dirty[key] = e - } - e.storeLocked(&value) - } else if e, ok := m.dirty[key]; ok { - e.storeLocked(&value) - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(readOnlyHandlerMap{m: read.m, amended: true}) - } - m.dirty[key] = newEntryHandlerMap(value) - } - m.mu.Unlock() -} - -// tryStore stores a value if the entry has not been expunged. -// -// If the entry is expunged, tryStore returns false and leaves the entry -// unchanged. -func (e *entryHandlerMap) tryStore(i **handlerInfo) bool { - for { - p := atomic.LoadPointer(&e.p) - if p == expungedHandlerMap { - return false - } - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { - return true - } - } -} - -// unexpungeLocked ensures that the entry is not marked as expunged. -// -// If the entry was previously expunged, it must be added to the dirty map -// before m.mu is unlocked. -func (e *entryHandlerMap) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expungedHandlerMap, nil) -} - -// storeLocked unconditionally stores a value to the entry. -// -// The entry must be known not to be expunged. -func (e *entryHandlerMap) storeLocked(i **handlerInfo) { - atomic.StorePointer(&e.p, unsafe.Pointer(i)) -} - -// LoadOrStore returns the existing value for the key if present. -// Otherwise, it stores and returns the given value. -// The loaded result is true if the value was loaded, false if stored. -func (m *HandlerMap) LoadOrStore(key uint16, value *handlerInfo) (actual *handlerInfo, loaded bool) { - // Avoid locking if it's a clean hit. - read, _ := m.read.Load().(readOnlyHandlerMap) - if e, ok := read.m[key]; ok { - actual, loaded, ok := e.tryLoadOrStore(value) - if ok { - return actual, loaded - } - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyHandlerMap) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - actual, loaded, _ = e.tryLoadOrStore(value) - } else if e, ok := m.dirty[key]; ok { - actual, loaded, _ = e.tryLoadOrStore(value) - m.missLocked() - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(readOnlyHandlerMap{m: read.m, amended: true}) - } - m.dirty[key] = newEntryHandlerMap(value) - actual, loaded = value, false - } - m.mu.Unlock() - - return actual, loaded -} - -// tryLoadOrStore atomically loads or stores a value if the entry is not -// expunged. -// -// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and -// returns with ok==false. -func (e *entryHandlerMap) tryLoadOrStore(i *handlerInfo) (actual *handlerInfo, loaded, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == expungedHandlerMap { - return actual, false, false - } - if p != nil { - return *(**handlerInfo)(p), true, true - } - - // Copy the interface after the first load to make this method more amenable - // to escape analysis: if we hit the "load" path or the entry is expunged, we - // shouldn't bother heap-allocating. - ic := i - for { - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { - return i, false, true - } - p = atomic.LoadPointer(&e.p) - if p == expungedHandlerMap { - return actual, false, false - } - if p != nil { - return *(**handlerInfo)(p), true, true - } - } -} - -// LoadAndDelete deletes the value for a key, returning the previous value if any. -// The loaded result reports whether the key was present. -func (m *HandlerMap) LoadAndDelete(key uint16) (value *handlerInfo, loaded bool) { - read, _ := m.read.Load().(readOnlyHandlerMap) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read, _ = m.read.Load().(readOnlyHandlerMap) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - delete(m.dirty, key) - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - if ok { - return e.delete() - } - return value, false -} - -// Delete deletes the value for a key. -func (m *HandlerMap) Delete(key uint16) { - m.LoadAndDelete(key) -} - -func (e *entryHandlerMap) delete() (value *handlerInfo, ok bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedHandlerMap { - return value, false - } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(**handlerInfo)(p), true - } - } -} - -// Range calls f sequentially for each key and value present in the map. -// If f returns false, range stops the iteration. -// -// Range does not necessarily correspond to any consistent snapshot of the Map's -// contents: no key will be visited more than once, but if the value for any key -// is stored or deleted concurrently, Range may reflect any mapping for that key -// from any point during the Range call. -// -// Range may be O(N) with the number of elements in the map even if f returns -// false after a constant number of calls. -func (m *HandlerMap) Range(f func(key uint16, value *handlerInfo) bool) { - // We need to be able to iterate over all of the keys that were already - // present at the start of the call to Range. - // If read.amended is false, then read.m satisfies that property without - // requiring us to hold m.mu for a long time. - read, _ := m.read.Load().(readOnlyHandlerMap) - if read.amended { - // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) - // (assuming the caller does not break out early), so a call to Range - // amortizes an entire copy of the map: we can promote the dirty copy - // immediately! - m.mu.Lock() - read, _ = m.read.Load().(readOnlyHandlerMap) - if read.amended { - read = readOnlyHandlerMap{m: m.dirty} - m.read.Store(read) - m.dirty = nil - m.misses = 0 - } - m.mu.Unlock() - } - - for k, e := range read.m { - v, ok := e.load() - if !ok { - continue - } - if !f(k, v) { - break - } - } -} - -func (m *HandlerMap) missLocked() { - m.misses++ - if m.misses < len(m.dirty) { - return - } - m.read.Store(readOnlyHandlerMap{m: m.dirty}) - m.dirty = nil - m.misses = 0 -} - -func (m *HandlerMap) dirtyLocked() { - if m.dirty != nil { - return - } - - read, _ := m.read.Load().(readOnlyHandlerMap) - m.dirty = make(map[uint16]*entryHandlerMap, len(read.m)) - for k, e := range read.m { - if !e.tryExpungeLocked() { - m.dirty[k] = e - } - } -} - -func (e *entryHandlerMap) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) - for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expungedHandlerMap) { - return true - } - p = atomic.LoadPointer(&e.p) - } - return p == expungedHandlerMap -} diff --git a/client/network.go b/client/network.go index 9fab6e76..4a546155 100644 --- a/client/network.go +++ b/client/network.go @@ -352,7 +352,7 @@ func (c *QQClient) netLoop() { if ok { info.fun(decoded, err) } else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait - f.(func(any, error))(decoded, err) + f(decoded, err) } } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { // does not need decoder diff --git a/go.mod b/go.mod index 915578c7..23143ef4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 + github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 github.com/fumiama/imgsz v0.0.2 github.com/pierrec/lz4/v4 v4.1.11 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 14044917..a10b7bb7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 h1:fdMod+DEoiICoTtS1Wij/wl1d57FPvKVmretLi2sGD8= +github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 8b86fe4d9cd0833342d2494acb6beb684f986330 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 21 Mar 2022 21:37:12 +0800 Subject: [PATCH 36/44] client: refactor forward message api --- client/global.go | 5 --- client/group_msg.go | 28 --------------- client/multimsg.go | 83 +++++++++++++++++++++++++++++++++++++++++++++ message/forward.go | 26 ++------------ 4 files changed, 86 insertions(+), 56 deletions(-) diff --git a/client/global.go b/client/global.go index d1c5db79..5b4cc48e 100644 --- a/client/global.go +++ b/client/global.go @@ -296,11 +296,6 @@ func packUniRequestData(data []byte) []byte { func genForwardTemplate(resID, preview, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement { template := forwardDisplay(resID, strconv.FormatInt(ts, 10), preview, summary) - for _, item := range items { - if item.GetFileName() == "MultiMsg" { - *item.FileName = strconv.FormatInt(ts, 10) - } - } return &message.ForwardElement{ FileName: strconv.FormatInt(ts, 10), Content: template, diff --git a/client/group_msg.go b/client/group_msg.go index 18216c09..9c1a5578 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/base64" "encoding/json" - "fmt" "math" "math/rand" "strconv" @@ -186,33 +185,6 @@ func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMes return nil, errors.New("upload long message error: highway server list is empty or not available server.") } -func (c *QQClient) UploadGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.ForwardElement { - if m.Length() > 200 { - return nil - } - ts := time.Now().UnixNano() - seq := c.nextGroupSeq() - data, hash, items := m.CalculateValidationDataForward(seq, rand.Int31(), groupCode) - rsp, body, err := c.multiMsgApplyUp(groupCode, data, hash, 2) - if err != nil { - return nil - } - for i, ip := range rsp.Uint32UpIp { - addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])} - input := highway.Input{ - CommandID: 27, - Key: rsp.MsgSig, - Body: bytes.NewReader(body), - } - err := c.highwaySession.Upload(addr, input) - if err != nil { - continue - } - return genForwardTemplate(rsp.MsgResid, m.Preview(), fmt.Sprintf("查看 %d 条转发消息", m.Length()), ts, items) - } - return nil -} - func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, buType int32) (*multimsg.MultiMsgApplyUpRsp, []byte, error) { i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode))) if err != nil { diff --git a/client/multimsg.go b/client/multimsg.go index f72a6941..1050a17b 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -2,14 +2,18 @@ package client import ( "bytes" + "crypto/md5" "fmt" "math" + "math/rand" + "strconv" "strings" "time" "github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/client/internal/highway" "github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/pb/longmsg" "github.com/Mrs4s/MiraiGo/client/pb/msg" @@ -228,3 +232,82 @@ func forwardDisplay(resID, fileName, preview, summary string) string { sb.WriteString(``) return sb.String() } + +func (c *QQClient) NewForwardMessageBuilder(groupCode int64) *ForwardMessageBuilder { + return &ForwardMessageBuilder{ + c: c, + groupCode: groupCode, + } +} + +type ForwardMessageBuilder struct { + c *QQClient + groupCode int64 + objs []*msg.PbMultiMsgItem +} + +// NestedNode 返回一个嵌套转发节点,其内容将会被 Builder 重定位 +func (builder *ForwardMessageBuilder) NestedNode() *message.ForwardElement { + filename := strconv.FormatInt(time.Now().UnixNano(), 10) // 大概率不会重复 + return &message.ForwardElement{FileName: filename} +} + +// Link 将真实的消息内容填充 reloc +func (builder *ForwardMessageBuilder) Link(reloc *message.ForwardElement, fmsg *message.ForwardMessage) { + seq := builder.c.nextGroupSeq() + m := fmsg.PackForwardMessage(seq, rand.Int31(), builder.groupCode) + builder.objs = append(builder.objs, &msg.PbMultiMsgItem{ + FileName: proto.String(reloc.FileName), + Buffer: &msg.PbMultiMsgNew{ + Msg: m, + }, + }) + reloc.Content = forwardDisplay("", reloc.FileName, fmsg.Preview(), fmt.Sprintf("查看 %d 条转发消息", fmsg.Length())) +} + +// Main 最外层的转发消息, 调用该方法后即上传消息 +func (builder *ForwardMessageBuilder) Main(m *message.ForwardMessage) *message.ForwardElement { + if m.Length() > 200 { + return nil + } + c := builder.c + seq := c.nextGroupSeq() + fm := m.PackForwardMessage(seq, rand.Int31(), builder.groupCode) + const filename = "MultiMsg" + builder.objs = append(builder.objs, &msg.PbMultiMsgItem{ + FileName: proto.String(filename), + Buffer: &msg.PbMultiMsgNew{ + Msg: fm, + }, + }) + trans := &msg.PbMultiMsgTransmit{ + Msg: fm, + PbItemList: builder.objs, + } + b, _ := proto.Marshal(trans) + data := binary.GZipCompress(b) + hash := md5.Sum(data) + rsp, body, err := c.multiMsgApplyUp(builder.groupCode, data, hash[:], 2) + if err != nil { + return nil + } + content := forwardDisplay(rsp.MsgResid, filename, m.Preview(), fmt.Sprintf("查看 %d 条转发消息", m.Length())) + for i, ip := range rsp.Uint32UpIp { + addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])} + input := highway.Input{ + CommandID: 27, + Key: rsp.MsgSig, + Body: bytes.NewReader(body), + } + err := c.highwaySession.Upload(addr, input) + if err != nil { + continue + } + return &message.ForwardElement{ + FileName: filename, + Content: content, + ResId: rsp.MsgResid, + } + } + return nil +} diff --git a/message/forward.go b/message/forward.go index 9062f11d..689d4cd8 100644 --- a/message/forward.go +++ b/message/forward.go @@ -14,10 +14,9 @@ import ( // *----- Definitions -----* // -// ForwardMessage 添加 Node 请用 AddNode 方法 +// ForwardMessage 合并转发消息 type ForwardMessage struct { Nodes []*ForwardNode - items []*msg.PbMultiMsgItem } type ForwardNode struct { @@ -72,9 +71,6 @@ func (f *ForwardMessage) AddNode(node *ForwardNode) *ForwardMessage { if item.Type() != Forward { // quick path continue } - if forward, ok := item.(*ForwardElement); ok { - f.items = append(f.items, forward.Items...) - } } return f } @@ -109,7 +105,7 @@ func (f *ForwardMessage) Preview() string { } func (f *ForwardMessage) CalculateValidationData(seq, random int32, groupCode int64) ([]byte, []byte) { - msgs := f.packForwardMsg(seq, random, groupCode) + msgs := f.PackForwardMessage(seq, random, groupCode) trans := &msg.PbMultiMsgTransmit{Msg: msgs, PbItemList: []*msg.PbMultiMsgItem{ { FileName: proto.String("MultiMsg"), @@ -122,23 +118,7 @@ func (f *ForwardMessage) CalculateValidationData(seq, random int32, groupCode in return data, hash[:] } -// CalculateValidationDataForward 屎代码 -func (f *ForwardMessage) CalculateValidationDataForward(seq, random int32, groupCode int64) ([]byte, []byte, []*msg.PbMultiMsgItem) { - msgs := f.packForwardMsg(seq, random, groupCode) - trans := &msg.PbMultiMsgTransmit{Msg: msgs, PbItemList: []*msg.PbMultiMsgItem{ - { - FileName: proto.String("MultiMsg"), - Buffer: &msg.PbMultiMsgNew{Msg: msgs}, - }, - }} - trans.PbItemList = append(trans.PbItemList, f.items...) - b, _ := proto.Marshal(trans) - data := binary.GZipCompress(b) - hash := md5.Sum(data) - return data, hash[:], trans.PbItemList -} - -func (f *ForwardMessage) packForwardMsg(seq int32, random int32, groupCode int64) []*msg.Message { +func (f *ForwardMessage) PackForwardMessage(seq int32, random int32, groupCode int64) []*msg.Message { ml := make([]*msg.Message, 0, len(f.Nodes)) for _, node := range f.Nodes { ml = append(ml, &msg.Message{ From aa657c0f09d06365ea9bb87aff735029a6fe0030 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 21 Mar 2022 21:39:15 +0800 Subject: [PATCH 37/44] dep: update syncx --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 23143ef4..7c3d577d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 - github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 + github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/fumiama/imgsz v0.0.2 github.com/pierrec/lz4/v4 v4.1.11 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index a10b7bb7..d00f0272 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= -github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 h1:fdMod+DEoiICoTtS1Wij/wl1d57FPvKVmretLi2sGD8= -github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= +github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= +github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 4314fdcb3963e7aa864bd0e878b0a02d9a4ff081 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 22 Mar 2022 21:34:05 +0800 Subject: [PATCH 38/44] binary: avoid alloc in reading integer --- binary/jce/reader.go | 25 ++++++++++--------------- binary/reader.go | 12 ++++++++---- client/model_show.go | 3 +-- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/binary/jce/reader.go b/binary/jce/reader.go index 9fd750d1..2c7cd45a 100644 --- a/binary/jce/reader.go +++ b/binary/jce/reader.go @@ -49,7 +49,7 @@ func (r *JceReader) skipHead() { } func (r *JceReader) skip(l int) { - r.skipBytes(l) + r.off += l } func (r *JceReader) skipField(t byte) { @@ -105,17 +105,6 @@ func (r *JceReader) readBytes(n int) []byte { return b } -func (r *JceReader) skipBytes(n int) { - if r.off+n > len(r.buf) { - panic("skipBytes: EOF") - } - lremain := len(r.buf[r.off:]) - if lremain < n { - n = lremain - } - r.off += n -} - func (r *JceReader) readByte() byte { if r.off >= len(r.buf) { panic("readByte: EOF") @@ -126,15 +115,21 @@ func (r *JceReader) readByte() byte { } func (r *JceReader) readUInt16() uint16 { - return goBinary.BigEndian.Uint16(r.readBytes(2)) + b := make([]byte, 2) + r.off += copy(b, r.buf[r.off:]) + return goBinary.BigEndian.Uint16(b) } func (r *JceReader) readUInt32() uint32 { - return goBinary.BigEndian.Uint32(r.readBytes(4)) + b := make([]byte, 4) + r.off += copy(b, r.buf[r.off:]) + return goBinary.BigEndian.Uint32(b) } func (r *JceReader) readUInt64() uint64 { - return goBinary.BigEndian.Uint64(r.readBytes(8)) + b := make([]byte, 8) + r.off += copy(b, r.buf[r.off:]) + return goBinary.BigEndian.Uint64(b) } func (r *JceReader) readFloat32() float32 { diff --git a/binary/reader.go b/binary/reader.go index f11c608f..3fcaa701 100644 --- a/binary/reader.go +++ b/binary/reader.go @@ -52,17 +52,20 @@ func (r *Reader) ReadBytesShort() []byte { } func (r *Reader) ReadUInt16() uint16 { - b := r.ReadBytes(2) + b := make([]byte, 2) + _, _ = r.buf.Read(b) return binary.BigEndian.Uint16(b) } func (r *Reader) ReadInt32() int32 { - b := r.ReadBytes(4) + b := make([]byte, 4) + _, _ = r.buf.Read(b) return int32(binary.BigEndian.Uint32(b)) } func (r *Reader) ReadInt64() int64 { - b := r.ReadBytes(8) + b := make([]byte, 8) + _, _ = r.buf.Read(b) return int64(binary.BigEndian.Uint64(b)) } @@ -154,7 +157,8 @@ func (r *NetworkReader) ReadBytes(len int) ([]byte, error) { } func (r *NetworkReader) ReadInt32() (int32, error) { - b, err := r.ReadBytes(4) + b := make([]byte, 4) + _, err := r.conn.Read(b) if err != nil { return 0, err } diff --git a/client/model_show.go b/client/model_show.go index 6b77a43b..c4160dd3 100644 --- a/client/model_show.go +++ b/client/model_show.go @@ -48,9 +48,8 @@ func (c *QQClient) getGtk(domain string) int { accu = accu + (accu << 5) + int(b) } return 2147483647 & accu - } else { - return 0 } + return 0 } func (c *QQClient) GetModelShow(modelName string) ([]*ModelVariant, error) { From 665c6acf024ac5071d7f28579d3e73e8fa721c76 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 22 Mar 2022 22:44:37 +0800 Subject: [PATCH 39/44] client: use `net/netip` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit netip包中的数据结构更省内存,而且不需要堆分配 --- client/client.go | 31 +++++++++---------- client/decoders.go | 12 ++++---- client/global.go | 13 ++++---- client/internal/network/conn.go | 4 +-- client/network.go | 11 ++++--- topic/feed.go | 2 +- utils/sys.go | 53 --------------------------------- 7 files changed, 39 insertions(+), 87 deletions(-) diff --git a/client/client.go b/client/client.go index 8b747945..bdd0fad2 100644 --- a/client/client.go +++ b/client/client.go @@ -5,6 +5,7 @@ import ( "fmt" "math/rand" "net" + "net/netip" "sort" "strconv" "sync" @@ -59,7 +60,7 @@ type QQClient struct { // internal state handlers syncx.Map[uint16, *handlerInfo] waiters syncx.Map[string, func(any, error)] - servers []*net.TCPAddr + servers []netip.AddrPort currServerIndex int retryTimes int version *auth.AppVersion @@ -196,7 +197,6 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { msgSvcCache: utils.NewCache[unit](time.Second * 15), transCache: utils.NewCache[unit](time.Second * 15), onlinePushCache: utils.NewCache[unit](time.Second * 15), - servers: []*net.TCPAddr{}, alive: true, highwaySession: new(highway.Session), @@ -226,28 +226,29 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { } adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers if err == nil && len(adds) > 0 { - var hostAddrs []*net.TCPAddr + var hostAddrs []netip.AddrPort for _, addr := range adds { - hostAddrs = append(hostAddrs, &net.TCPAddr{ - IP: addr, - Port: 8080, - }) + ip, ok := netip.AddrFromSlice(addr.To4()) + if ok { + hostAddrs = append(hostAddrs, netip.AddrPortFrom(ip, 8080)) + } } cli.servers = append(hostAddrs, cli.servers...) } if len(cli.servers) == 0 { - cli.servers = []*net.TCPAddr{ // default servers - {IP: net.IP{42, 81, 172, 81}, Port: 80}, - {IP: net.IP{114, 221, 148, 59}, Port: 14000}, - {IP: net.IP{42, 81, 172, 147}, Port: 443}, - {IP: net.IP{125, 94, 60, 146}, Port: 80}, - {IP: net.IP{114, 221, 144, 215}, Port: 80}, - {IP: net.IP{42, 81, 172, 22}, Port: 80}, + cli.servers = []netip.AddrPort{ // default servers + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 81}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 148, 59}), 14000), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 147}), 443), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{125, 94, 60, 146}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 144, 215}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 22}), 80), } } pings := make([]int64, len(cli.servers)) wg := sync.WaitGroup{} wg.Add(len(cli.servers)) + println(len(cli.servers)) for i := range cli.servers { go func(index int) { defer wg.Done() @@ -783,7 +784,7 @@ func (c *QQClient) UpdateProfile(profile ProfileDetailUpdate) { _, _ = c.sendAndWait(c.buildUpdateProfileDetailPacket(profile)) } -func (c *QQClient) SetCustomServer(servers []*net.TCPAddr) { +func (c *QQClient) SetCustomServer(servers []netip.AddrPort) { c.servers = append(servers, c.servers...) } diff --git a/client/decoders.go b/client/decoders.go index f8452d4b..d5e5427d 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -2,7 +2,7 @@ package client import ( "crypto/md5" - "net" + "net/netip" "strconv" "strings" "sync" @@ -312,16 +312,16 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b ssoPkt := jce.NewJceReader(jceBuf) servers := ssoPkt.ReadSsoServerInfos(1) if len(servers) > 0 { - var adds []*net.TCPAddr + var adds []netip.AddrPort for _, s := range servers { if strings.Contains(s.Server, "com") { continue } c.debug("got new server addr: %v location: %v", s.Server, s.Location) - adds = append(adds, &net.TCPAddr{ - IP: net.ParseIP(s.Server), - Port: int(s.Port), - }) + addr, err := netip.ParseAddr(s.Server) + if err == nil { + adds = append(adds, netip.AddrPortFrom(addr, uint16(s.Port))) + } } f := true for _, e := range c.eventHandlers.serverUpdatedHandlers { diff --git a/client/global.go b/client/global.go index 5b4cc48e..ca623397 100644 --- a/client/global.go +++ b/client/global.go @@ -6,6 +6,7 @@ import ( "fmt" "math/rand" "net" + "net/netip" "sort" "strconv" "strings" @@ -114,7 +115,7 @@ func GenIMEI() string { return final.String() } -func getSSOAddress() ([]*net.TCPAddr, error) { +func getSSOAddress() ([]netip.AddrPort, error) { protocol := SystemDeviceInfo.Protocol.Version() key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411") payload := jce.NewJceWriter(). // see ServerConfig.d @@ -150,15 +151,15 @@ func getSSOAddress() ([]*net.TCPAddr, error) { data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer)) reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:]) servers := reader.ReadSsoServerInfos(2) - adds := make([]*net.TCPAddr, 0, len(servers)) + adds := make([]netip.AddrPort, 0, len(servers)) for _, s := range servers { if strings.Contains(s.Server, "com") { continue } - adds = append(adds, &net.TCPAddr{ - IP: net.ParseIP(s.Server), - Port: int(s.Port), - }) + ip, ok := netip.AddrFromSlice(net.ParseIP(s.Server)) + if ok { + adds = append(adds, netip.AddrPortFrom(ip, uint16(s.Port))) + } } return adds, nil } diff --git a/client/internal/network/conn.go b/client/internal/network/conn.go index a43614c1..682b3034 100644 --- a/client/internal/network/conn.go +++ b/client/internal/network/conn.go @@ -34,9 +34,9 @@ func (t *TCPListener) UnexpectedDisconnect(f func(*TCPListener, error)) { t.unexpectedDisconnect = f } -func (t *TCPListener) Connect(addr *net.TCPAddr) error { +func (t *TCPListener) Connect(addr string) error { t.Close() - conn, err := net.DialTCP("tcp", nil, addr) + conn, err := net.Dial("tcp", addr) if err != nil { return errors.Wrap(err, "dial tcp error") } diff --git a/client/network.go b/client/network.go index 4a546155..868ebd03 100644 --- a/client/network.go +++ b/client/network.go @@ -40,11 +40,13 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { r := &ConnectionQualityInfo{} wg := sync.WaitGroup{} wg.Add(2) + + currentServerAddr := c.servers[c.currServerIndex].String() go func() { defer wg.Done() var err error - if r.ChatServerLatency, err = qualityTest(c.servers[c.currServerIndex].String()); err != nil { + if r.ChatServerLatency, err = qualityTest(currentServerAddr); err != nil { c.error("test chat server latency error: %v", err) r.ChatServerLatency = 9999 } @@ -67,7 +69,7 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { }() go func() { defer wg.Done() - res := utils.RunTCPPingLoop(c.servers[c.currServerIndex].String(), 10) + res := utils.RunTCPPingLoop(currentServerAddr, 10) r.ChatServerPacketLoss = res.PacketsLoss if c.highwaySession.AddrLength() > 0 { res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10) @@ -87,8 +89,9 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { // connect 连接到 QQClient.servers 中的服务器 func (c *QQClient) connect() error { - c.info("connect to server: %v", c.servers[c.currServerIndex].String()) - err := c.TCP.Connect(c.servers[c.currServerIndex]) + addr := c.servers[c.currServerIndex].String() + c.info("connect to server: %v", addr) + err := c.TCP.Connect(addr) c.currServerIndex++ if c.currServerIndex == len(c.servers) { c.currServerIndex = 0 diff --git a/topic/feed.go b/topic/feed.go index 5472131e..8cb65363 100644 --- a/topic/feed.go +++ b/topic/feed.go @@ -58,7 +58,7 @@ type ( content map[string]any ) -var globalBlockId int64 = 0 +var globalBlockId int64 func genBlockId() string { id := atomic.AddInt64(&globalBlockId, 1) diff --git a/utils/sys.go b/utils/sys.go index 7ef22b45..b9d78312 100644 --- a/utils/sys.go +++ b/utils/sys.go @@ -44,59 +44,6 @@ func MultiReadSeeker(r ...io.ReadSeeker) io.ReadSeeker { } } -type multiReadAt struct { - first io.ReadSeeker - second io.ReadSeeker - firstSize int64 - secondSize int64 -} - -func (m *multiReadAt) ReadAt(p []byte, off int64) (n int, err error) { - if m.second == nil { // quick path - _, _ = m.first.Seek(off, io.SeekStart) - return m.first.Read(p) - } - if off < m.firstSize && off+int64(len(p)) < m.firstSize { - _, err = m.first.Seek(off, io.SeekStart) - if err != nil { - return - } - return m.first.Read(p) - } else if off < m.firstSize && off+int64(len(p)) >= m.firstSize { - _, _ = m.first.Seek(off, io.SeekStart) - _, _ = m.second.Seek(0, io.SeekStart) - n, err = m.first.Read(p[:m.firstSize-off]) - if err != nil { - return - } - n2, err := m.second.Read(p[m.firstSize-off:]) - return n + n2, err - } - _, err = m.second.Seek(off-m.firstSize, io.SeekStart) - if err != nil { - return - } - return m.second.Read(p) -} - -func ReaderAtFrom2ReadSeeker(first, second io.ReadSeeker) io.ReaderAt { - firstSize, _ := first.Seek(0, io.SeekEnd) - if second == nil { - return &multiReadAt{ - first: first, - firstSize: firstSize, - secondSize: 0, - } - } - secondSize, _ := second.Seek(0, io.SeekEnd) - return &multiReadAt{ - first: first, - second: second, - firstSize: firstSize, - secondSize: secondSize, - } -} - // Select 如果A为nil 将会返回 B 否则返回A // 对应 ?? 语法 func Select(a, b []byte) []byte { From 868828f3da83a8d59fad4b154af20ddebf2d2df2 Mon Sep 17 00:00:00 2001 From: icarus-ai <82353054+icarus-ai@users.noreply.github.com> Date: Wed, 23 Mar 2022 12:48:57 +0800 Subject: [PATCH 40/44] =?UTF-8?q?=E6=B3=A8=E9=87=8Aclient251=E8=A1=8Cprint?= =?UTF-8?q?ln=20(#264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 谁忘了注释这个println(≖_≖ ) --- client/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.go b/client/client.go index bdd0fad2..b8b5ebb4 100644 --- a/client/client.go +++ b/client/client.go @@ -248,7 +248,7 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { pings := make([]int64, len(cli.servers)) wg := sync.WaitGroup{} wg.Add(len(cli.servers)) - println(len(cli.servers)) + // println(len(cli.servers)) for i := range cli.servers { go func(index int) { defer wg.Done() From 9422ce4751bb9104c41ca0be1f50b3df7f453c0f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Mar 2022 22:42:21 +0800 Subject: [PATCH 41/44] all: optimize detected by go-perfguard --- binary/utils.go | 11 ++++++----- binary/writer.go | 3 ++- client/internal/oicq/oicq.go | 3 ++- message/forward.go | 6 +++--- utils/sys.go | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/binary/utils.go b/binary/utils.go index 07134191..58fa592c 100644 --- a/binary/utils.go +++ b/binary/utils.go @@ -6,7 +6,6 @@ import ( "compress/zlib" binary2 "encoding/binary" "encoding/hex" - "io" "net" "github.com/Mrs4s/MiraiGo/utils" @@ -34,7 +33,7 @@ func ZlibUncompress(src []byte) []byte { var out bytes.Buffer r, _ := zlib.NewReader(b) defer r.Close() - io.Copy(&out, r) + _, _ = out.ReadFrom(r) return out.Bytes() } @@ -42,7 +41,8 @@ func ZlibCompress(data []byte) []byte { zw := acquireZlibWriter() _, _ = zw.w.Write(data) _ = zw.w.Close() - ret := append([]byte(nil), zw.buf.Bytes()...) + ret := make([]byte, len(zw.buf.Bytes())) + copy(ret, zw.buf.Bytes()) releaseZlibWriter(zw) return ret } @@ -51,7 +51,8 @@ func GZipCompress(data []byte) []byte { gw := AcquireGzipWriter() _, _ = gw.Write(data) _ = gw.Close() - ret := append([]byte(nil), gw.buf.Bytes()...) + ret := make([]byte, len(gw.buf.Bytes())) + copy(ret, gw.buf.Bytes()) ReleaseGzipWriter(gw) return ret } @@ -61,7 +62,7 @@ func GZipUncompress(src []byte) []byte { var out bytes.Buffer r, _ := gzip.NewReader(b) defer r.Close() - _, _ = io.Copy(&out, r) + _, _ = out.ReadFrom(r) return out.Bytes() } diff --git a/binary/writer.go b/binary/writer.go index d8a8703a..837cd862 100644 --- a/binary/writer.go +++ b/binary/writer.go @@ -12,7 +12,8 @@ type Writer bytes.Buffer func NewWriterF(f func(writer *Writer)) []byte { w := SelectWriter() f(w) - b := append([]byte(nil), w.Bytes()...) + b := make([]byte, len(w.Bytes())) + copy(b, w.Bytes()) w.put() return b } diff --git a/client/internal/oicq/oicq.go b/client/internal/oicq/oicq.go index c4469a40..9abe167f 100644 --- a/client/internal/oicq/oicq.go +++ b/client/internal/oicq/oicq.go @@ -84,7 +84,8 @@ func (c *Codec) Marshal(m *Message) []byte { } w.WriteByte(0x03) - buf := append([]byte(nil), w.Bytes()...) + buf := make([]byte, len(w.Bytes())) + copy(buf, w.Bytes()) goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf))) return buf } diff --git a/message/forward.go b/message/forward.go index 689d4cd8..a1226bb2 100644 --- a/message/forward.go +++ b/message/forward.go @@ -1,9 +1,9 @@ package message import ( - "bytes" "crypto/md5" "regexp" + "strings" "sync" "github.com/Mrs4s/MiraiGo/binary" @@ -79,7 +79,7 @@ func (f *ForwardMessage) AddNode(node *ForwardNode) *ForwardMessage { func (f *ForwardMessage) Length() int { return len(f.Nodes) } func (f *ForwardMessage) Brief() string { - var brief bytes.Buffer + var brief strings.Builder for _, n := range f.Nodes { brief.WriteString(ToReadableString(n.Message)) if brief.Len() >= 27 { @@ -90,7 +90,7 @@ func (f *ForwardMessage) Brief() string { } func (f *ForwardMessage) Preview() string { - var pv bytes.Buffer + var pv strings.Builder for i, node := range f.Nodes { if i >= 4 { break diff --git a/utils/sys.go b/utils/sys.go index b9d78312..cdd8b69b 100644 --- a/utils/sys.go +++ b/utils/sys.go @@ -20,7 +20,7 @@ func ComputeMd5AndLength(r io.Reader) ([]byte, int64) { func (r *multiReadSeeker) Read(p []byte) (int, error) { if r.multiReader == nil { - var readers []io.Reader + readers := make([]io.Reader, len(r.readers)) for i := range r.readers { _, _ = r.readers[i].Seek(0, io.SeekStart) readers = append(readers, r.readers[i]) From 33590e4b32fd58fff2445e9e4e2ae3b8c9fd19dc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 28 Mar 2022 13:13:06 +0800 Subject: [PATCH 42/44] highway: rename BdhInput and Input to Transaction --- client/group_msg.go | 8 ++++++-- client/image.go | 6 +++--- client/internal/highway/bdh.go | 8 ++++---- client/internal/highway/highway.go | 16 ++++------------ client/multimsg.go | 7 +++++-- client/ptt.go | 4 ++-- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/client/group_msg.go b/client/group_msg.go index 9c1a5578..1183ad39 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "crypto/md5" "encoding/base64" "encoding/json" "math" @@ -170,10 +171,13 @@ func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMes } for i, ip := range rsp.Uint32UpIp { addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])} - input := highway.Input{ + hash := md5.Sum(body) + input := highway.Transaction{ CommandID: 27, - Key: rsp.MsgSig, + Ticket: rsp.MsgSig, Body: bytes.NewReader(body), + Size: int64(len(body)), + Sum: hash[:], } err := c.highwaySession.Upload(addr, input) if err != nil { diff --git a/client/image.go b/client/image.go index 84b8e810..0764f203 100644 --- a/client/image.go +++ b/client/image.go @@ -92,7 +92,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee var r any var err error - var input highway.BdhInput + var input highway.Transaction switch target.SourceType { case message.SourceGroup: r, err = c.sendAndWait(c.buildGroupImageStorePacket(target.PrimaryID, fh, int32(length))) @@ -115,7 +115,7 @@ func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSee } } - input = highway.BdhInput{ + input = highway.Transaction{ CommandID: cmd, Body: img, Size: length, @@ -322,7 +322,7 @@ func (c *QQClient) uploadOcrImage(img io.Reader, size int32, sum []byte) (string Uuid: binary.GenUUID(r), }) - rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ + rsp, err := c.highwaySession.UploadBDH(highway.Transaction{ CommandID: 76, Body: img, Size: int64(size), diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 97d571f3..54622a7f 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -16,7 +16,7 @@ import ( "github.com/Mrs4s/MiraiGo/internal/proto" ) -type BdhInput struct { +type Transaction struct { CommandID int32 Body io.Reader Sum []byte // md5 sum of body @@ -26,7 +26,7 @@ type BdhInput struct { Encrypt bool } -func (bdh *BdhInput) encrypt(key []byte) error { +func (bdh *Transaction) encrypt(key []byte) error { if bdh.Encrypt { if len(key) == 0 { return errors.New("session key not found. maybe miss some packet?") @@ -36,7 +36,7 @@ func (bdh *BdhInput) encrypt(key []byte) error { return nil } -func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { +func (s *Session) UploadBDH(input Transaction) ([]byte, error) { if len(s.SsoAddr) == 0 { return nil, errors.New("srv addrs not found. maybe miss some packet?") } @@ -108,7 +108,7 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { return rspExt, nil } -func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, error) { +func (s *Session) UploadBDHMultiThread(input Transaction, threadCount int) ([]byte, error) { // for small file and small thread count, // use UploadBDH instead of UploadBDHMultiThread if input.Size < 1024*1024*3 || threadCount < 2 { diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index f641c94d..9447d407 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -47,15 +47,7 @@ func (s *Session) AppendAddr(ip, port uint32) { s.SsoAddr = append(s.SsoAddr, addr) } -type Input struct { - CommandID int32 - Key []byte - Body io.ReadSeeker -} - -func (s *Session) Upload(addr Addr, input Input) error { - fh, length := utils.ComputeMd5AndLength(input.Body) - _, _ = input.Body.Seek(0, io.SeekStart) +func (s *Session) Upload(addr Addr, input Transaction) error { conn, err := net.DialTimeout("tcp", addr.String(), time.Second*3) if err != nil { return errors.Wrap(err, "connect error") @@ -79,12 +71,12 @@ func (s *Session) Upload(addr Addr, input Input) error { head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), MsgSeghead: &pb.SegHead{ - Filesize: length, + Filesize: input.Size, Dataoffset: int64(offset), Datalength: int32(rl), - Serviceticket: input.Key, + Serviceticket: input.Ticket, Md5: ch[:], - FileMd5: fh, + FileMd5: input.Sum, }, ReqExtendinfo: []byte{}, }) diff --git a/client/multimsg.go b/client/multimsg.go index 1050a17b..8ca22486 100644 --- a/client/multimsg.go +++ b/client/multimsg.go @@ -294,10 +294,13 @@ func (builder *ForwardMessageBuilder) Main(m *message.ForwardMessage) *message.F content := forwardDisplay(rsp.MsgResid, filename, m.Preview(), fmt.Sprintf("查看 %d 条转发消息", m.Length())) for i, ip := range rsp.Uint32UpIp { addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])} - input := highway.Input{ + hash := md5.Sum(body) + input := highway.Transaction{ CommandID: 27, - Key: rsp.MsgSig, + Ticket: rsp.MsgSig, Body: bytes.NewReader(body), + Sum: hash[:], + Size: int64(len(body)), } err := c.highwaySession.Upload(addr, input) if err != nil { diff --git a/client/ptt.go b/client/ptt.go index 024d3097..6ea82280 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -73,7 +73,7 @@ func (c *QQClient) UploadVoice(target message.Source, voice io.ReadSeeker) (*mes ext = c.buildGroupPttStoreBDHExt(target.PrimaryID, fh, int32(length), 0, int32(length)) } // multi-thread upload is no need - rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{ + rsp, err := c.highwaySession.UploadBDH(highway.Transaction{ CommandID: cmd, Body: voice, Sum: fh, @@ -157,7 +157,7 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS } ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoSum, thumbSum, videoLen, thumbLen).PttShortVideoUploadReq) combined := utils.MultiReadSeeker(thumb, video) - input := highway.BdhInput{ + input := highway.Transaction{ CommandID: cmd, Body: combined, Size: videoLen + thumbLen, From 4f05838b6c18a98f5d13e1961b81ed4199c13f44 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 28 Mar 2022 13:35:58 +0800 Subject: [PATCH 43/44] fix nil in multi reader --- client/ptt.go | 2 +- utils/sys.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ptt.go b/client/ptt.go index 6ea82280..3521b9aa 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -156,7 +156,7 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS cmd = 89 } ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoSum, thumbSum, videoLen, thumbLen).PttShortVideoUploadReq) - combined := utils.MultiReadSeeker(thumb, video) + combined := io.MultiReader(thumb, video) input := highway.Transaction{ CommandID: cmd, Body: combined, diff --git a/utils/sys.go b/utils/sys.go index cdd8b69b..b3b11654 100644 --- a/utils/sys.go +++ b/utils/sys.go @@ -23,7 +23,7 @@ func (r *multiReadSeeker) Read(p []byte) (int, error) { readers := make([]io.Reader, len(r.readers)) for i := range r.readers { _, _ = r.readers[i].Seek(0, io.SeekStart) - readers = append(readers, r.readers[i]) + readers[i] = r.readers[i] } r.multiReader = io.MultiReader(readers...) } From e2a42e542581810801a2f5b9c456feb314c0bc4d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 28 Mar 2022 15:27:59 +0800 Subject: [PATCH 44/44] client: seek start 0 before upload --- client/internal/highway/bdh.go | 61 ++++++++++++++---------------- client/internal/highway/highway.go | 12 +++--- client/ptt.go | 2 + 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/client/internal/highway/bdh.go b/client/internal/highway/bdh.go index 54622a7f..352cd49f 100644 --- a/client/internal/highway/bdh.go +++ b/client/internal/highway/bdh.go @@ -36,13 +36,13 @@ func (bdh *Transaction) encrypt(key []byte) error { return nil } -func (s *Session) UploadBDH(input Transaction) ([]byte, error) { +func (s *Session) UploadBDH(trans Transaction) ([]byte, error) { if len(s.SsoAddr) == 0 { return nil, errors.New("srv addrs not found. maybe miss some packet?") } addr := s.SsoAddr[0].String() - if err := input.encrypt(s.SessionKey); err != nil { + if err := trans.encrypt(s.SessionKey); err != nil { return nil, err } conn, err := net.DialTimeout("tcp", addr, time.Second*20) @@ -59,14 +59,15 @@ func (s *Session) UploadBDH(input Transaction) ([]byte, error) { const chunkSize = 256 * 1024 var rspExt, chunk []byte offset := 0 - if input.Size > chunkSize { + if trans.Size > chunkSize { chunk = make([]byte, chunkSize) } else { - chunk = make([]byte, input.Size) + chunk = make([]byte, trans.Size) } for { - rl, err := io.ReadFull(input.Body, chunk) - if errors.Is(err, io.EOF) { + chunk = chunk[:cap(chunk)] + rl, err := io.ReadFull(trans.Body, chunk) + if rl == 0 { break } if errors.Is(err, io.ErrUnexpectedEOF) { @@ -74,16 +75,16 @@ func (s *Session) UploadBDH(input Transaction) ([]byte, error) { } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, trans.CommandID, 2052), MsgSeghead: &pb.SegHead{ - Filesize: input.Size, + Filesize: trans.Size, Dataoffset: int64(offset), Datalength: int32(rl), - Serviceticket: input.Ticket, + Serviceticket: trans.Ticket, Md5: ch[:], - FileMd5: input.Sum, + FileMd5: trans.Sum, }, - ReqExtendinfo: input.Ext, + ReqExtendinfo: trans.Ext, }) offset += rl frame := newFrame(head, chunk) @@ -102,17 +103,17 @@ func (s *Session) UploadBDH(input Transaction) ([]byte, error) { rspExt = rspHead.RspExtendinfo } if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil { - input.Ticket = rspHead.MsgSeghead.Serviceticket + trans.Ticket = rspHead.MsgSeghead.Serviceticket } } return rspExt, nil } -func (s *Session) UploadBDHMultiThread(input Transaction, threadCount int) ([]byte, error) { +func (s *Session) UploadBDHMultiThread(trans Transaction, threadCount int) ([]byte, error) { // for small file and small thread count, // use UploadBDH instead of UploadBDHMultiThread - if input.Size < 1024*1024*3 || threadCount < 2 { - return s.UploadBDH(input) + if trans.Size < 1024*1024*3 || threadCount < 2 { + return s.UploadBDH(trans) } if len(s.SsoAddr) == 0 { @@ -120,7 +121,7 @@ func (s *Session) UploadBDHMultiThread(input Transaction, threadCount int) ([]by } addr := s.SsoAddr[0].String() - if err := input.encrypt(s.SessionKey); err != nil { + if err := trans.encrypt(s.SessionKey); err != nil { return nil, err } @@ -130,7 +131,7 @@ func (s *Session) UploadBDHMultiThread(input Transaction, threadCount int) ([]by completedThread uint32 cond = sync.NewCond(&sync.Mutex{}) offset = int64(0) - count = (input.Size + blockSize - 1) / blockSize + count = (trans.Size + blockSize - 1) / blockSize id = 0 ) doUpload := func() error { @@ -165,31 +166,27 @@ func (s *Session) UploadBDHMultiThread(input Transaction, threadCount int) ([]by break } chunk = chunk[:blockSize] - n, err := io.ReadFull(input.Body, chunk) + n, err := io.ReadFull(trans.Body, chunk) cond.L.Unlock() - if err != nil { - if err == io.EOF { - break - } - if err == io.ErrUnexpectedEOF { - chunk = chunk[:n] - } else { - return err - } + if n == 0 { + break + } + if errors.Is(err, io.ErrUnexpectedEOF) { + chunk = chunk[:n] } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, trans.CommandID, 2052), MsgSeghead: &pb.SegHead{ - Filesize: input.Size, + Filesize: trans.Size, Dataoffset: off, Datalength: int32(n), - Serviceticket: input.Ticket, + Serviceticket: trans.Ticket, Md5: ch[:], - FileMd5: input.Sum, + FileMd5: trans.Sum, }, - ReqExtendinfo: input.Ext, + ReqExtendinfo: trans.Ext, }) frame := newFrame(head, chunk) _, err = frame.WriteTo(conn) diff --git a/client/internal/highway/highway.go b/client/internal/highway/highway.go index 9447d407..05b53018 100644 --- a/client/internal/highway/highway.go +++ b/client/internal/highway/highway.go @@ -47,7 +47,7 @@ func (s *Session) AppendAddr(ip, port uint32) { s.SsoAddr = append(s.SsoAddr, addr) } -func (s *Session) Upload(addr Addr, input Transaction) error { +func (s *Session) Upload(addr Addr, trans Transaction) error { conn, err := net.DialTimeout("tcp", addr.String(), time.Second*3) if err != nil { return errors.Wrap(err, "connect error") @@ -60,7 +60,7 @@ func (s *Session) Upload(addr Addr, input Transaction) error { reader := binary.NewNetworkReader(conn) for { chunk = chunk[:chunkSize] - rl, err := io.ReadFull(input.Body, chunk) + rl, err := io.ReadFull(trans.Body, chunk) if errors.Is(err, io.EOF) { break } @@ -69,14 +69,14 @@ func (s *Session) Upload(addr Addr, input Transaction) error { } ch := md5.Sum(chunk) head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ - MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, input.CommandID, 2052), + MsgBasehead: s.dataHighwayHead(_REQ_CMD_DATA, 4096, trans.CommandID, 2052), MsgSeghead: &pb.SegHead{ - Filesize: input.Size, + Filesize: trans.Size, Dataoffset: int64(offset), Datalength: int32(rl), - Serviceticket: input.Ticket, + Serviceticket: trans.Ticket, Md5: ch[:], - FileMd5: input.Sum, + FileMd5: trans.Sum, }, ReqExtendinfo: []byte{}, }) diff --git a/client/ptt.go b/client/ptt.go index 3521b9aa..4fb33873 100644 --- a/client/ptt.go +++ b/client/ptt.go @@ -156,6 +156,8 @@ func (c *QQClient) UploadShortVideo(target message.Source, video, thumb io.ReadS cmd = 89 } ext, _ := proto.Marshal(c.buildPttShortVideoProto(target, videoSum, thumbSum, videoLen, thumbLen).PttShortVideoUploadReq) + _, _ = thumb.Seek(0, io.SeekStart) + _, _ = video.Seek(0, io.SeekStart) combined := io.MultiReader(thumb, video) input := highway.Transaction{ CommandID: cmd,