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,