From 31b3fd2bd61374b649a3cce7a8ce1cee586b3425 Mon Sep 17 00:00:00 2001 From: Redmomn <109732988+Redmomn@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:02:05 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/auth/app.go | 33 ++++-- client/base.go | 23 +--- client/internal/network/transport.go | 6 +- client/packet.go | 2 +- client/sign/http.go | 27 ----- client/sign/provider.go | 25 +++-- client/sign/sign.go | 157 ++++++--------------------- main.go | 3 +- 8 files changed, 78 insertions(+), 198 deletions(-) diff --git a/client/auth/app.go b/client/auth/app.go index c50751fd..d84c3fbb 100644 --- a/client/auth/app.go +++ b/client/auth/app.go @@ -21,7 +21,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_LNX_NQ_3.1.2-13107_RDM_B", AppID: 1600001615, SubAppID: 537146866, - AppIDQrcode: 13697054, AppClientVersion: 13172, MainSigmap: 169742560, @@ -44,7 +43,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_LNX_NQ_3.2.10_25765_GW_B", AppID: 1600001615, SubAppID: 537234773, - AppIDQrcode: 13697054, AppClientVersion: 13172, MainSigmap: 169742560, @@ -67,7 +65,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_LNX_NQ_3.2.12_27597_GW_B", AppID: 1600001615, SubAppID: 537243600, - AppIDQrcode: 13697054, AppClientVersion: 13172, MainSigmap: 169742560, @@ -90,7 +87,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_LNX_NQ_3.2.15_30366_GW_B", AppID: 1600001615, SubAppID: 537258424, - AppIDQrcode: 13697054, AppClientVersion: 30366, MainSigmap: 169742560, @@ -110,14 +106,35 @@ var AppList = map[string]map[string]*AppInfo{ PTOSVersion: 19, PackageName: "com.tencent.qq", WTLoginSDK: "nt.wtlogin.0.0.1", + PackageSign: "V1_LNX_NQ_3.2.19-39038_GW_B", AppID: 1600001615, SubAppID: 537313942, - AppIDQrcode: 537313942, AppClientVersion: 39038, MainSigmap: 169742560, SubSigmap: 0, NTLoginType: 1, }, + + "3.2.26-46494": { + OS: "Linux", + Kernel: "Linux", + VendorOS: "linux", + + CurrentVersion: "3.2.26-46494", + BuildVersion: 46494, + MiscBitmap: 32764, + PTVersion: "2.0.0", + PTOSVersion: 19, + PackageName: "com.tencent.qq", + WTLoginSDK: "nt.wtlogin.0.0.1", + PackageSign: "V1_LNX_NQ_3.2.26-46494_GW_B", + AppID: 1600001615, + SubAppID: 537345891, + AppClientVersion: 46494, + MainSigmap: 169742560, + SubSigmap: 0, + NTLoginType: 1, + }, }, "macos": { @@ -136,7 +153,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_MAC_NQ_6.9.20-17153_RDM_B", AppID: 1600001602, SubAppID: 537162356, - AppIDQrcode: 537162356, AppClientVersion: 13172, MainSigmap: 169742560, @@ -160,7 +176,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_WIN_NQ_9.9.12-25493_GW_B", AppID: 1600001604, SubAppID: 537231759, - AppIDQrcode: 537138217, AppClientVersion: 13172, MainSigmap: 169742560, @@ -183,7 +198,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_WIN_NQ_9.9.12-25765_GW_B", AppID: 1600001604, SubAppID: 537234702, - AppIDQrcode: 537138217, AppClientVersion: 13172, MainSigmap: 169742560, @@ -206,7 +220,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_WIN_NQ_9.9.15-27597_GW_B", AppID: 1600001604, SubAppID: 537243441, - AppIDQrcode: 537138217, AppClientVersion: 13172, MainSigmap: 169742560, @@ -229,7 +242,6 @@ var AppList = map[string]map[string]*AppInfo{ PackageSign: "V1_WIN_NQ_9.9.15-28060_GW_B", AppID: 1600001604, SubAppID: 537246092, - AppIDQrcode: 537138217, AppClientVersion: 13172, MainSigmap: 169742560, @@ -254,7 +266,6 @@ type AppInfo struct { PackageSign string `json:"package_sign"` AppID int `json:"app_id"` SubAppID int `json:"sub_app_id"` - AppIDQrcode int `json:"app_id_qrcode"` AppClientVersion int `json:"app_client_version"` MainSigmap int `json:"main_sigmap"` SubSigmap int `json:"sub_sigmap"` diff --git a/client/base.go b/client/base.go index 45ba00e3..85aa10a2 100644 --- a/client/base.go +++ b/client/base.go @@ -129,25 +129,11 @@ type QQClient struct { DisconnectedEvent EventHandle[*DisconnectedEvent] } -// AddSignServer 设置签名服务器url -func (c *QQClient) AddSignServer(signServers ...string) { - if c.signProvider == nil { - c.UseSignProvider(sign.NewSigner(c.debug)) - } - c.signProvider.AddSignServer(signServers...) -} - -// GetSignServer 获得所有签名服务器 -func (c *QQClient) GetSignServer() []string { - return c.signProvider.GetSignServer() -} - // AddSignHeader 设置签名服务器签名时的额外http header func (c *QQClient) AddSignHeader(header map[string]string) { - if c.signProvider == nil { - c.UseSignProvider(sign.NewSigner(c.debug)) + if c.signProvider != nil { + c.signProvider.AddRequestHeader(header) } - c.signProvider.AddRequestHeader(header) } func (c *QQClient) UseVersion(app *auth.AppInfo) { @@ -155,10 +141,9 @@ func (c *QQClient) UseVersion(app *auth.AppInfo) { c.highwaySession.AppID = uint32(app.AppID) c.highwaySession.SubAppID = uint32(app.SubAppID) c.UA = "LagrangeGo qq/" + app.PackageSign - if c.signProvider == nil { - c.signProvider = sign.NewSigner(c.debug) + if c.signProvider != nil { + c.signProvider.SetAppInfo(app) } - c.signProvider.SetAppInfo(app) } func (c *QQClient) UseSignProvider(p sign.Provider) { diff --git a/client/internal/network/transport.go b/client/internal/network/transport.go index 0774ee62..00901670 100644 --- a/client/internal/network/transport.go +++ b/client/internal/network/transport.go @@ -30,9 +30,9 @@ func (t *Transport) PackPacket(req *Request) []byte { if req.Sign != nil { sign := req.Sign.Value head[24] = proto.DynamicMessage{ - 1: []byte(sign.Sign), - 2: []byte(sign.Token), - 3: []byte(sign.Extra), + 1: []byte(sign.SecSign), + 2: []byte(sign.SecToken), + 3: []byte(sign.SecExtra), } } diff --git a/client/packet.go b/client/packet.go index 525beda1..f173463e 100644 --- a/client/packet.go +++ b/client/packet.go @@ -8,7 +8,7 @@ import ( func (c *QQClient) uniPacket(command string, body []byte) (uint32, []byte, error) { seq := c.getAndIncreaseSequence() - sign, err := c.signProvider.Sign(command, seq, body) + sign, err := c.signProvider.Sign(command, seq, body, c.Uin, c.Device().GUID, c.Version().PackageSign) if err != nil { return 0, nil, err } diff --git a/client/sign/http.go b/client/sign/http.go index 376587df..4fbbd5ae 100644 --- a/client/sign/http.go +++ b/client/sign/http.go @@ -71,33 +71,6 @@ func ContainSignPKG(cmd string) bool { return ok } -func httpGet[T any](rawURL string, queryParams map[string]string, timeout time.Duration, header http.Header) (target T, err error) { - u, err := url.Parse(rawURL) - if err != nil { - return - } - - q := u.Query() - for k, v := range queryParams { - q.Set(k, v) - } - u.RawQuery = q.Encode() - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) - if err != nil { - return - } - for k, vs := range header { - for _, v := range vs { - req.Header.Add(k, v) - } - } - return doHTTP[T](ctx, req) -} - func httpPost[T any](rawURL string, body io.Reader, timeout time.Duration, header http.Header) (target T, err error) { u, err := url.Parse(rawURL) if err != nil { diff --git a/client/sign/provider.go b/client/sign/provider.go index 89afa087..c5bdadee 100644 --- a/client/sign/provider.go +++ b/client/sign/provider.go @@ -11,18 +11,21 @@ type ( HexData []byte Request struct { - Cmd string `json:"cmd"` - Seq int `json:"seq"` - Src HexData `json:"src"` + Command string `json:"command"` + Seq int `json:"seq"` + Body HexData `json:"body"` + Uin uint32 `json:"uin"` + Guid string `json:"guid"` + Qua string `json:"qua"` } Response struct { - Platform string `json:"platform"` - Version string `json:"version"` - Value struct { - Sign HexData `json:"sign"` - Extra HexData `json:"extra"` - Token HexData `json:"token"` + Code uint32 `json:"code"` + Message string `json:"message"` + Value struct { + SecSign HexData `json:"sec_sign"` + SecToken HexData `json:"sec_token"` + SecExtra HexData `json:"sec_extra"` } `json:"value"` } ) @@ -45,10 +48,8 @@ func (h *HexData) UnmarshalJSON(data []byte) error { } type Provider interface { - Sign(cmd string, seq uint32, data []byte) (*Response, error) + Sign(cmd string, seq uint32, data []byte, uin uint32, guid, qua string) (*Response, error) AddRequestHeader(header map[string]string) - AddSignServer(signServers ...string) - GetSignServer() []string SetAppInfo(app *auth.AppInfo) Release() } diff --git a/client/sign/sign.go b/client/sign/sign.go index e26e6426..3f89dee3 100644 --- a/client/sign/sign.go +++ b/client/sign/sign.go @@ -5,52 +5,37 @@ import ( "encoding/json" "errors" "fmt" - "math" "net/http" - "sort" - "strconv" "sync" - "sync/atomic" "time" "github.com/LagrangeDev/LagrangeGo/client/auth" - "github.com/LagrangeDev/LagrangeGo/utils/io" ) type ( Client struct { lock sync.RWMutex - signCount atomic.Uint32 - instances []*remote - app *auth.AppInfo + server string httpClient *http.Client extraHeaders http.Header log func(string, ...any) - lastTestTime time.Time } - - remote struct { - server string - latency atomic.Uint32 - } -) - -const ( - serverLatencyDown = math.MaxUint32 ) var ErrVersionMismatch = errors.New("sign version mismatch") -func NewSigner(log func(string, ...any), signServers ...string) *Client { +func NewSigner(log func(string, ...any), token, signServer string) *Client { + if log == nil { + log = func(string, ...any) {} + } client := &Client{ - instances: io.Map(signServers, func(s string) *remote { - return &remote{server: s} - }), - httpClient: &http.Client{}, - extraHeaders: http.Header{}, - log: log, + server: signServer, + httpClient: &http.Client{}, + extraHeaders: http.Header{ + "Authorization": []string{"Bearer " + token}, + }, + log: log, } - go client.test() return client } @@ -64,131 +49,55 @@ func (c *Client) AddRequestHeader(header map[string]string) { } } -func (c *Client) AddSignServer(signServers ...string) { - c.lock.Lock() - defer c.lock.Unlock() - c.instances = append(c.instances, io.Map[string, *remote](signServers, func(s string) *remote { - return &remote{server: s} - })...) -} - -func (c *Client) GetSignServer() []string { - c.lock.RLock() - defer c.lock.RUnlock() - return io.Map(c.instances, func(sign *remote) string { - return sign.server - }) -} - func (c *Client) SetAppInfo(app *auth.AppInfo) { c.lock.Lock() defer c.lock.Unlock() - c.app = app c.extraHeaders.Set("User-Agent", "qq/"+app.CurrentVersion) } -func (c *Client) getAvailableSign() *remote { +func (c *Client) signServer() string { c.lock.RLock() defer c.lock.RUnlock() - for _, i := range c.instances { - if i.latency.Load() < serverLatencyDown { - return i - } - } - return nil -} - -func (c *Client) sortByLatency() { - c.lock.Lock() - defer c.lock.Unlock() - sort.SliceStable(c.instances, func(i, j int) bool { - return c.instances[i].latency.Load() < c.instances[j].latency.Load() - }) + return c.server } -func (c *Client) Sign(cmd string, seq uint32, data []byte) (*Response, error) { +func (c *Client) Sign(cmd string, seq uint32, data []byte, uin uint32, guid, qua string) (*Response, error) { if !ContainSignPKG(cmd) { return nil, nil } - if time.Now().After(c.lastTestTime.Add(30 * time.Minute)) { - go c.test() + server := c.signServer() + if server == "" { + return nil, errors.New("sign server not configured") } startTime := time.Now().UnixMilli() - for { - if sign := c.getAvailableSign(); sign != nil { - resp, err := sign.sign(cmd, seq, data, c.extraHeaders) - if err != nil { - sign.latency.Store(serverLatencyDown) - continue - } else if resp.Version != c.app.CurrentVersion { - return nil, ErrVersionMismatch - } - c.log(fmt.Sprintf("signed for [%s:%d](%dms)", - cmd, seq, time.Now().UnixMilli()-startTime)) - c.signCount.Add(1) - return resp, nil - } - break - } - // 全寄了,重新再测下 - go c.test() - return nil, errors.New("all sign service down") -} - -func (c *Client) test() { - c.lock.Lock() - if time.Now().Before(c.lastTestTime.Add(10 * time.Minute)) { - c.lock.Unlock() - return - } - c.lastTestTime = time.Now() - c.lock.Unlock() - for _, i := range c.instances { - i.test() + resp, err := sign(server, cmd, seq, data, uin, guid, qua, c.extraHeaders) + if err != nil { + return nil, err } - c.sortByLatency() + c.log(fmt.Sprintf("signed for [%s:%d](%dms)", + cmd, seq, time.Now().UnixMilli()-startTime)) + return resp, nil } -func (i *remote) sign(cmd string, seq uint32, buf []byte, header http.Header) (*Response, error) { +func sign(server, cmd string, seq uint32, buf []byte, uin uint32, guid, qua string, header http.Header) (*Response, error) { if !ContainSignPKG(cmd) { return nil, nil } req := Request{ - Cmd: cmd, - Seq: int(seq), - Src: buf, + Command: cmd, + Seq: int(seq), + Body: buf, + Uin: uin, + Guid: guid, + Qua: qua, } data, err := json.Marshal(&req) if err != nil { return nil, err } - resp, err := httpPost[Response](i.server, bytes.NewReader(data), 8*time.Second, header) - if err != nil || len(resp.Value.Sign) == 0 { - resp, err = httpGet[Response](i.server, map[string]string{ - "cmd": cmd, - "seq": strconv.Itoa(int(seq)), - "src": fmt.Sprintf("%x", buf), - }, 8*time.Second, header) - if err != nil { - return nil, err - } + resp, err := httpPost[Response](server, bytes.NewReader(data), 8*time.Second, header) + if err != nil { + return nil, err } return &resp, nil } - -func (i *remote) test() { - startTime := time.Now().UnixMilli() - resp, err := i.sign("wtlogin.login", 1, []byte{11, 45, 14}, nil) - if err != nil || len(resp.Value.Sign) == 0 { - i.latency.Store(serverLatencyDown) - return - } - // 有长连接的情况,取两次平均值 - resp, err = i.sign("wtlogin.login", 1, []byte{11, 45, 14}, nil) - if err != nil || len(resp.Value.Sign) == 0 { - i.latency.Store(serverLatencyDown) - return - } - // 粗略计算,应该足够了 - i.latency.Store(uint32(time.Now().UnixMilli()-startTime) / 2) -} diff --git a/main.go b/main.go index 012dded4..6b677d7d 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/LagrangeDev/LagrangeGo/client" "github.com/LagrangeDev/LagrangeGo/client/auth" + "github.com/LagrangeDev/LagrangeGo/client/sign" "github.com/LagrangeDev/LagrangeGo/message" "github.com/LagrangeDev/LagrangeGo/utils/io" ) @@ -36,7 +37,7 @@ func main() { qqclient := client.NewClient(0, "") qqclient.SetLogger(protocolLogger{}) qqclient.UseVersion(appInfo) - qqclient.AddSignServer("https://sign.lagrangecore.org/api/sign/39038") + qqclient.UseSignProvider(sign.NewSigner(logger.Debugf, "eb505682-95b1-4ae5-86aa-be4db1ee82e0", "https://sign.lagrangecore.org/api/sign/sec-sign")) qqclient.UseDevice(deviceInfo) data, err := os.ReadFile("sig.bin") if err != nil { From 95cf42db681bf235c2eea784d1e80bc254bb8b8f Mon Sep 17 00:00:00 2001 From: Redmomn <109732988+Redmomn@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:02:17 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/cache.go | 9 --------- client/client.go | 2 +- client/packets/tlv/qrcode.go | 4 ++-- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/client/cache.go b/client/cache.go index a608b19c..87796e7b 100644 --- a/client/cache.go +++ b/client/cache.go @@ -1,25 +1,16 @@ package client import ( - "strconv" "time" "github.com/LagrangeDev/LagrangeGo/client/entity" ) -var selfUin string - // GetUID 获取缓存中对应uin的uid func (c *QQClient) GetUID(uin uint32, groupUin ...uint32) string { if uin == 0 { return "" } - if uin == c.Uin { - if selfUin == "" { - selfUin = strconv.FormatUint(uint64(uin), 10) - } - return selfUin - } if len(groupUin) == 0 && c.cache.FriendCacheIsEmpty() { if err := c.RefreshFriendCache(); err != nil { return "" diff --git a/client/client.go b/client/client.go index 7a29b17f..194b0323 100644 --- a/client/client.go +++ b/client/client.go @@ -79,7 +79,7 @@ func (c *QQClient) FetchQRCode(size, margin, ecLevel uint32) ([]byte, string, er WriteU64(0). WriteU8(0). WriteTLV( - tlv.T16(c.Version().AppID, c.Version().AppIDQrcode, + tlv.T16(c.Version().AppID, c.Version().SubAppID, lgrio.MustParseHexStr(c.Device().GUID), c.Version().PTVersion, c.Version().PackageName), tlv.T1b(0, 0, size, margin, 72, ecLevel, 2), tlv.T1d(c.Version().MiscBitmap), diff --git a/client/packets/tlv/qrcode.go b/client/packets/tlv/qrcode.go index 928f4069..ed49059f 100644 --- a/client/packets/tlv/qrcode.go +++ b/client/packets/tlv/qrcode.go @@ -11,11 +11,11 @@ func T11(unusualSign []byte) []byte { Pack(0x11) } -func T16(appid, appidQrcode int, guid []byte, ptVersion, packageName string) []byte { +func T16(appid, subappid int, guid []byte, ptVersion, packageName string) []byte { return binary.NewBuilder(). WriteU32(0). WriteU32(uint32(appid)). - WriteU32(uint32(appidQrcode)). + WriteU32(uint32(subappid)). WriteBytes(guid). WritePacketString(packageName, "u16", false). WritePacketString(ptVersion, "u16", false). From ef15b93a26e90d43dca5c32652dea60b2014e9c7 Mon Sep 17 00:00:00 2001 From: Redmomn <109732988+Redmomn@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:05:17 +0800 Subject: [PATCH 3/3] lint --- client/sign/provider.go | 2 +- client/sign/sign.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/sign/provider.go b/client/sign/provider.go index c5bdadee..b89f1a2b 100644 --- a/client/sign/provider.go +++ b/client/sign/provider.go @@ -15,7 +15,7 @@ type ( Seq int `json:"seq"` Body HexData `json:"body"` Uin uint32 `json:"uin"` - Guid string `json:"guid"` + GUID string `json:"guid"` Qua string `json:"qua"` } diff --git a/client/sign/sign.go b/client/sign/sign.go index 3f89dee3..b7f2ed28 100644 --- a/client/sign/sign.go +++ b/client/sign/sign.go @@ -88,7 +88,7 @@ func sign(server, cmd string, seq uint32, buf []byte, uin uint32, guid, qua stri Seq: int(seq), Body: buf, Uin: uin, - Guid: guid, + GUID: guid, Qua: qua, } data, err := json.Marshal(&req)