From 6cf8030d3c03b83ba9bc15feac76cfb558ee84d3 Mon Sep 17 00:00:00 2001 From: KomeiDiSanXian <2211213431@qq.com> Date: Mon, 10 Jul 2023 07:15:43 +0800 Subject: [PATCH 01/23] =?UTF-8?q?update:=20=E5=85=BC=E5=AE=B9=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=88=B0v1.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/login.go | 36 ++++++++++++++++++++++++++++--- cmd/gocq/main.go | 1 + internal/base/flag.go | 4 ++++ modules/config/config.go | 2 ++ modules/config/default_config.yml | 5 +++++ 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 5909af0..b000d9e 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -270,10 +270,14 @@ func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, erro if !strings.HasSuffix(signServer, "/") { signServer += "/" } - response, err := download.Request{ + req := download.Request{ Method: http.MethodGet, - URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)), - }.Bytes() + URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v", id, hex.EncodeToString(salt), uin), + } + if base.IsBelow110 { + req.URL = signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)) + } + response, err := req.Bytes() if err != nil { log.Warnf("获取T544 sign时出现错误: %v server: %v", err, signServer) return nil, err @@ -310,3 +314,29 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b token, _ = hex.DecodeString(gjson.GetBytes(response, "data.token").String()) return sign, extra, token, nil } + +func register(uin int64, androidID, guid []byte, qimei36, key string) { + if base.IsBelow110 { + log.Warn("签名服务器版本低于1.1.0, 跳过实例注册") + return + } + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" + } + resp, err := download.Request{ + Method: http.MethodGet, + URL: signServer + "register" + fmt.Sprintf("?uin=%v&android_id=%v&guid=%v&qimei36=%v&key=%s", + uin, hex.EncodeToString(androidID), hex.EncodeToString(guid), qimei36, key), + }.Bytes() + if err != nil { + log.Warnf("注册QQ实例时出现错误: %v server: %v", err, signServer) + return + } + msg := gjson.GetBytes(resp, "msg") + if gjson.GetBytes(resp, "code").Int() != 0 { + log.Warnf("注册QQ实例时出现错误: %v server: %v", msg, signServer) + return + } + log.Infof("注册QQ实例 %v 成功: %v", uin, msg) +} diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 10ab254..5061865 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -166,6 +166,7 @@ func LoginInteract() { if base.SignServer != "-" && base.SignServer != "" { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) + register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) wrapper.DandelionEnergy = energy wrapper.FekitGetSign = sign } else { diff --git a/internal/base/flag.go b/internal/base/flag.go index e622eca..33ec62d 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -39,6 +39,8 @@ var ( AllowTempSession bool // 是否允许发送临时会话信息 UpdateProtocol bool // 是否更新协议 SignServer string // 使用特定的服务器进行签名 + Key string // 签名服务器密钥 + IsBelow110 bool // 签名服务器版本是否低于1.1.0及以下 HTTPTimeout int PostFormat string // 上报格式 string or array @@ -89,6 +91,8 @@ func Init() { UseSSOAddress = conf.Account.UseSSOAddress AllowTempSession = conf.Account.AllowTempSession SignServer = conf.Account.SignServer + Key = conf.Account.Key + IsBelow110 = conf.Account.IsBelow110 HTTPTimeout = conf.Message.HTTPTimeout } { // others diff --git a/modules/config/config.go b/modules/config/config.go index b438359..cf8143f 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -36,6 +36,8 @@ type Account struct { UseSSOAddress bool `yaml:"use-sso-address"` AllowTempSession bool `yaml:"allow-temp-session"` SignServer string `yaml:"sign-server"` + Key string `yaml:"key"` + IsBelow110 bool `yaml:"is-below-110"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 6873052..0d62c53 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -24,6 +24,11 @@ account: # 账号相关 # sign-server: 'https://signserver.example.com' # 线上签名服务器 # 服务器可使用docker在本地搭建或者使用他人开放的服务 sign-server: '-' + # 如果签名服务器的版本在1.1.0及以下, 请将下面的参数改成true + is-below-110: false + # 签名服务器所需要的apikey, 如果签名服务器的版本在1.1.0及以下则此项无效 + # 本地部署的默认为114514 + key: '114514' heartbeat: # 心跳频率, 单位秒 From 16a2ff050e68d041bb6ec56dc112571adb77c4a9 Mon Sep 17 00:00:00 2001 From: KomeiDiSanXian <2211213431@qq.com> Date: Mon, 10 Jul 2023 07:22:50 +0800 Subject: [PATCH 02/23] fix lint warning: unused-parameter --- cmd/gocq/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index b000d9e..8b19768 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -265,7 +265,7 @@ func fetchCaptcha(id string) string { return "" } -func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, error) { +func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" From 526391e61389af2a0113b83b6928b76170bb9e22 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 06:44:28 +0000 Subject: [PATCH 03/23] ci(chore): Fix stylings --- cmd/gocq/login.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 8b19768..9aa16c0 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -14,13 +14,14 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/download" ) From a4c131e04a1184406c2bbd938332a05a5e297499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=80=A1=E7=84=B6?= <63996691+zhaodice@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:47:59 +0800 Subject: [PATCH 04/23] fix: cancel hex encoding of android_id field (#2318) --- cmd/gocq/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 9aa16c0..3176432 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -328,7 +328,7 @@ func register(uin int64, androidID, guid []byte, qimei36, key string) { resp, err := download.Request{ Method: http.MethodGet, URL: signServer + "register" + fmt.Sprintf("?uin=%v&android_id=%v&guid=%v&qimei36=%v&key=%s", - uin, hex.EncodeToString(androidID), hex.EncodeToString(guid), qimei36, key), + uin, androidID, hex.EncodeToString(guid), qimei36, key), }.Bytes() if err != nil { log.Warnf("注册QQ实例时出现错误: %v server: %v", err, signServer) From aa3a5d28da415041523a4d20df77497116508b9f Mon Sep 17 00:00:00 2001 From: achieve <44516835+chieve-bale@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:51:58 +0800 Subject: [PATCH 05/23] optimize: increase http client timeout (#2315) --- internal/download/download.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/download/download.go b/internal/download/download.go index a82af5a..feaec23 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -33,7 +33,7 @@ var client = &http.Client{ TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{}, MaxIdleConnsPerHost: 999, }, - Timeout: time.Second * 5, + Timeout: time.Second * 15, } var clienth2 = &http.Client{ @@ -47,7 +47,7 @@ var clienth2 = &http.Client{ ForceAttemptHTTP2: true, MaxIdleConnsPerHost: 999, }, - Timeout: time.Second * 5, + Timeout: time.Second * 15, } // ErrOverSize 响应主体过大时返回此错误 From a3ad233cd99143916fe94459296da0dadd61f1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:55:59 +0800 Subject: [PATCH 06/23] fix: group not found report (#2312) (#2337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决以下问题: 当群组踢人时,该人不在群内,返回“群聊不存在”的BUG https://github.com/Mrs4s/go-cqhttp/issues/1774#issue-1459854639 Co-authored-by: PSoul --- coolq/api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index a1e6fc0..39815e2 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1149,7 +1149,9 @@ func (bot *CQBot) CQDelGroupMemo(groupID int64, fid string) global.MSG { // @rename(msg->message, block->reject_add_request) func (bot *CQBot) CQSetGroupKick(groupID int64, userID int64, msg string, block bool) global.MSG { if g := bot.Client.FindGroup(groupID); g != nil { - if m := g.FindMember(userID); m != nil { + if m := g.FindMember(userID); m == nil { + return Failed(100, "MEMBER_IS_NOT_IN_GROUP", "人员不存在") + } else { err := m.Kick(msg, block) if err != nil { return Failed(100, "NOT_MANAGEABLE", "机器人权限不足") From 19dd37a9386651d1fcdba677dc07f44d14e26ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 09:36:21 +0800 Subject: [PATCH 07/23] fix(lint): if-block --- coolq/api.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 39815e2..5e5fda3 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1151,13 +1151,12 @@ func (bot *CQBot) CQSetGroupKick(groupID int64, userID int64, msg string, block if g := bot.Client.FindGroup(groupID); g != nil { if m := g.FindMember(userID); m == nil { return Failed(100, "MEMBER_IS_NOT_IN_GROUP", "人员不存在") - } else { - err := m.Kick(msg, block) - if err != nil { - return Failed(100, "NOT_MANAGEABLE", "机器人权限不足") - } - return OK(nil) } + err := m.Kick(msg, block) + if err != nil { + return Failed(100, "NOT_MANAGEABLE", "机器人权限不足") + } + return OK(nil) } return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") } From 13215f23c5caf02384c76e749c757db6ad7bc755 Mon Sep 17 00:00:00 2001 From: Komei_DiSanXian <2211213431@qq.com> Date: Tue, 1 Aug 2023 09:36:57 +0800 Subject: [PATCH 08/23] feat: add waitSignServer (#2311) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: 等待签名服务器启动以后再进行注册 * add: 支持签名服务器自动注册实例 * fix * fix: 修复获取sign 时报错 * 调整代码结构 * Update login.go * Update main.go * Update login.go --------- Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- cmd/gocq/login.go | 37 ++++++++++++++++++++++++++++++++----- cmd/gocq/main.go | 4 ++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 3176432..51d9626 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -8,6 +8,7 @@ import ( "image" "image/png" "net/http" + "net/url" "os" "strings" "time" @@ -20,9 +21,8 @@ import ( "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/download" ) @@ -273,7 +273,8 @@ func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { } req := download.Request{ Method: http.MethodGet, - URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v", id, hex.EncodeToString(salt), uin), + URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v&android_id=%v&guid=%v", + id, hex.EncodeToString(salt), uin, utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)), } if base.IsBelow110 { req.URL = signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)) @@ -304,7 +305,8 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b Method: http.MethodPost, URL: signServer + "sign", Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, - Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&qua=%s&cmd=%s&seq=%v&buffer=%v", uin, qua, cmd, seq, hex.EncodeToString(buff)))), + Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&qua=%s&cmd=%s&seq=%v&buffer=%v&android_id=%v&guid=%v", + uin, qua, cmd, seq, hex.EncodeToString(buff), utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)))), }.Bytes() if err != nil { log.Warnf("获取sso sign时出现错误: %v server: %v", err, signServer) @@ -328,7 +330,7 @@ func register(uin int64, androidID, guid []byte, qimei36, key string) { resp, err := download.Request{ Method: http.MethodGet, URL: signServer + "register" + fmt.Sprintf("?uin=%v&android_id=%v&guid=%v&qimei36=%v&key=%s", - uin, androidID, hex.EncodeToString(guid), qimei36, key), + uin, utils.B2S(androidID), hex.EncodeToString(guid), qimei36, key), }.Bytes() if err != nil { log.Warnf("注册QQ实例时出现错误: %v server: %v", err, signServer) @@ -341,3 +343,28 @@ func register(uin int64, androidID, guid []byte, qimei36, key string) { } log.Infof("注册QQ实例 %v 成功: %v", uin, msg) } + +func waitSignServer() bool { + t := time.NewTicker(time.Second*5) + defer t.Stop() + i := 0 + for range t.C { + if i > 3 { + return false + } + i++ + u, err := url.Parse(base.SignServer) + if err != nil { + log.Warnf("连接到签名服务器出现错误: %v", err) + continue + } + r := utils.RunTCPPingLoop(u.Host, 4) + if r.PacketsLoss > 0 { + log.Warnf("连接到签名服务器出现错误: 丢包%d/%d 时延%dms", r.PacketsLoss, r.PacketsSent, r.AvgTimeMill) + continue + } + break + } + log.Infof("连接至签名服务器: %s", base.SignServer) + return true +} diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 5061865..606fae8 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -166,6 +166,10 @@ func LoginInteract() { if base.SignServer != "-" && base.SignServer != "" { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) + // 等待签名服务器直到连接成功 + if !waitSignServer() { + log.Fatalf("连接签名服务器失败") + } register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) wrapper.DandelionEnergy = energy wrapper.FekitGetSign = sign From f466ca7a72bde7b20b96b60477a8602990fffbd3 Mon Sep 17 00:00:00 2001 From: Lumine <66518048+1umine@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:33:32 +0800 Subject: [PATCH 09/23] =?UTF-8?q?feat:=20=E6=8F=90=E4=BE=9B=201.1.6=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BB=A5=E4=B8=8A=20qsign=20=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=94=AF=E6=8C=81=20(#2307)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加签名服务超时设置 * 获取签名和err为空时尝试重新注册实例 * 可配置自动刷新token以及自动注册 * fix lint * wrap callback * add config: refresh-interval * support qsign's `auto-register` * fix: add registerLock to avoid repeat registraion. * update: Enable disabling auto token refresh * fix: use string android_id (not hexadecimal * update default_config.yml * fix: compatible with older qsign (bellow 1.1.0 * fix: refresh token * update dependency * update go.sum * fix: fix warnings on old version sign server --------- Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- cmd/gocq/login.go | 172 +++++++++++++++++++++++++++++- cmd/gocq/main.go | 15 ++- go.mod | 2 +- go.sum | 4 +- modules/config/config.go | 3 + modules/config/default_config.yml | 13 ++- 6 files changed, 202 insertions(+), 7 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 51d9626..5828b03 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -10,7 +10,9 @@ import ( "net/http" "net/url" "os" + "strconv" "strings" + "sync" "time" "github.com/Mrs4s/MiraiGo/client" @@ -296,7 +298,40 @@ func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { return data, nil } -func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { +// t: 提交的操作类型 +func submit(uin string, cmd string, callbackID int64, buffer []byte, t string) { + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" + } + buffStr := hex.EncodeToString(buffer) + log.Infof("submit %v: uin=%v, cmd=%v, callbackID=%v, buffer-end=%v", t, uin, cmd, callbackID, + buffStr[len(buffStr)-10:]) + _, err := download.Request{ + Method: http.MethodGet, + URL: signServer + "submit" + fmt.Sprintf("?uin=%v&cmd=%v&callback_id=%v&buffer=%v", + uin, cmd, callbackID, buffStr), + }.Bytes() + if err != nil { + log.Warnf("提交 callback 时出现错误: %v server: %v", err, signServer) + } +} + +// request token和签名的回调 +func callback(uin string, results []gjson.Result, t string) { + for _, result := range results { + cmd := result.Get("cmd").String() + callbackID := result.Get("callbackId").Int() + body, _ := hex.DecodeString(result.Get("body").String()) + ret, err := cli.SendSsoPacket(cmd, body) + if err != nil { + log.Warnf("callback error: %v", err) + } + submit(uin, cmd, callbackID, ret, t) + } +} + +func _sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" @@ -309,15 +344,19 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b uin, qua, cmd, seq, hex.EncodeToString(buff), utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)))), }.Bytes() if err != nil { - log.Warnf("获取sso sign时出现错误: %v server: %v", err, signServer) return nil, nil, nil, err } sign, _ = hex.DecodeString(gjson.GetBytes(response, "data.sign").String()) extra, _ = hex.DecodeString(gjson.GetBytes(response, "data.extra").String()) token, _ = hex.DecodeString(gjson.GetBytes(response, "data.token").String()) + if !base.IsBelow110 { + go callback(uin, gjson.GetBytes(response, "data.requestCallback").Array(), "sign") + } return sign, extra, token, nil } +var registerLock sync.Mutex + func register(uin int64, androidID, guid []byte, qimei36, key string) { if base.IsBelow110 { log.Warn("签名服务器版本低于1.1.0, 跳过实例注册") @@ -344,6 +383,135 @@ func register(uin int64, androidID, guid []byte, qimei36, key string) { log.Infof("注册QQ实例 %v 成功: %v", uin, msg) } +func refreshToken(uin string) error { + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" + } + log.Info("正在刷新 token") + resp, err := download.Request{ + Method: http.MethodGet, + URL: signServer + "request_token" + fmt.Sprintf("?uin=%v", uin), + }.Bytes() + if err != nil { + return err + } + msg := gjson.GetBytes(resp, "msg") + if gjson.GetBytes(resp, "code").Int() != 0 { + return errors.New(msg) + } + go callback(uin, gjson.GetBytes(resp, "data").Array(), "request token") + return true +} + +var missTokenCount = uint64(0) + +func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { + i := 0 + for { + sign, extra, token, err = _sign(seq, uin, cmd, qua, buff) + if err != nil { + log.Warnf("获取sso sign时出现错误: %v server: %v", err, base.SignServer) + } + if i > 0 { + break + } + i++ + if (!base.IsBelow110) && base.Account.AutoRegister && err == nil && len(sign) == 0 { + if registerLock.TryLock() { // 避免并发时多处同时销毁并重新注册 + log.Warn("获取签名为空,实例可能丢失,正在尝试重新注册") + defer registerLock.Unlock() + err := destroySignServer(uin) + if err != nil { + log.Warnf(err) + return nil, nil, nil, err + } + register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) + } + continue + } + if (!base.IsBelow110) && base.Account.AutoRefreshToken && len(token) == 0 { + log.Warnf("token 已过期, 总丢失 token 次数为 %v", atomic.AddUint64(&missTokenCount, 1)) + if registerLock.TryLock() { + defer registerLock.Unlock() + if err := refreshToken(uin); err != nil { + log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) + } else { + log.Info("刷新 token 成功") + } + } + continue + } + break + } + return sign, extra, token, err +} + +func destroySignServer(uin string) error { + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" + } + signVersion, err := getSignServerVersion() + if err != nil { + return errors.Wrapf(err, "获取签名服务版本出现错误, server: %v", signServer) + } + if global.VersionNameCompare("v"+signVersion, "v1.1.6") { + return errors.Errorf("当前签名服务器版本 %v 低于 1.1.6,无法使用 destroy 接口", signVersion) + } + resp, err := download.Request{ + Method: http.MethodGet, + URL: signServer + "destroy" + fmt.Sprintf("?uin=%v&key=%v", uin, base.Key), + }.Bytes() + if err != nil { + return errors.Wrapf(err, "destroy 实例出现错误, server: %v", signServer) + } + msg := gjson.GetBytes(resp, "msg") + if gjson.GetBytes(resp, "code").Int() != 0 { + return errors.Wrapf(err, "destroy 实例出现错误, server: %v", signServer) + } + return nil +} + +func getSignServerVersion() (version string, err error) { + signServer := base.SignServer + resp, err := download.Request{ + Method: http.MethodGet, + URL: signServer, + }.Bytes() + if err != nil { + return "", err + } + if gjson.GetBytes(resp, "code").Int() == 0 { + return gjson.GetBytes(resp, "data.version").String(), nil + } + return "", errors.New("empty version") +} + +// 定时刷新 token, interval 为间隔时间(分钟) +func startRefreshTokenTask(interval int64) { + if interval <= 0 { + log.Warn("定时刷新 token 已关闭") + return + } + log.Infof("每 %v 分钟将刷新一次签名 token", interval) + if interval < 10 { + log.Warnf("间隔时间 %v 分钟较短,推荐 30~40 分钟", interval) + } + if interval > 60 { + log.Warn("间隔时间不能超过 60 分钟,已自动设置为 60 分钟") + interval = 60 + } + t := time.Newticker(time.Duration(interval) * time.Minute) + defer t.Stop() + for range t.C { + err := refreshToken(strconv.FormatInt(base.Account.Uin, 10)) + if err != nil { + log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) + } + } +} + func waitSignServer() bool { t := time.NewTicker(time.Second*5) defer t.Stop() diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 606fae8..281ae25 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -166,13 +166,25 @@ func LoginInteract() { if base.SignServer != "-" && base.SignServer != "" { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) + download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 设置签名超时时间 // 等待签名服务器直到连接成功 if !waitSignServer() { log.Fatalf("连接签名服务器失败") } register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) + go startRefreshTokenTask(base.Account.RefreshInterval) // 定时刷新 token wrapper.DandelionEnergy = energy wrapper.FekitGetSign = sign + if !base.IsBelow110 { + if !base.Account.AutoRegister { + log.Warn("自动注册实例已关闭,若未配置 sign-server 端自动注册实例则实例丢失时需要重启 go-cqhttp 以正常签名") + } + if !base.Account.AutoRefreshToken { + log.Warn("自动刷新 token 已关闭,token 过期后获取签名时将不会立即尝试刷新获取新 token") + } + } else { + log.Warn("签名服务器版本 <= 1.1.0 ,无法使用刷新 token 等操作,建议使用 1.1.6 版本及以上签名服务器") + } } else { log.Warnf("警告: 未配置签名服务器, 这可能会导致登录 45 错误码或发送消息被风控") } @@ -294,6 +306,7 @@ func LoginInteract() { } if !base.FastStart { log.Infof("正在检查协议更新...") + download.SetTimeout(time.Second * 5) // 防止协议更新堵塞过久 currentVersionName := device.Protocol.Version().SortVersionName remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) if err == nil { @@ -378,7 +391,7 @@ func LoginInteract() { }) saveToken() cli.AllowSlider = true - download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 在登录完成后设置, 防止在堵塞协议更新 + download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 登陆完成进行最终超时设置 log.Infof("登录成功 欢迎使用: %v", cli.Nickname) log.Info("开始加载好友列表...") global.Check(cli.ReloadFriendList(), true) diff --git a/go.mod b/go.mod index ffe42a1..8ea6e36 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e + github.com/Mrs4s/MiraiGo v0.0.0-20230730133947-d344e0f318ab github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index d251a11..61e86ae 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e h1:99itMjI//+KaFF0+0QCBg/uHhGMJ99jG2lP6z/UnOsU= -github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230730133947-d344e0f318ab h1:SLciJTlC5YiG3qqvGJf4sHJDHDXUdH+v4rjqVhE5SIQ= +github.com/Mrs4s/MiraiGo v0.0.0-20230730133947-d344e0f318ab/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= diff --git a/modules/config/config.go b/modules/config/config.go index cf8143f..f424054 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -38,6 +38,9 @@ type Account struct { SignServer string `yaml:"sign-server"` Key string `yaml:"key"` IsBelow110 bool `yaml:"is-below-110"` + AutoRegister bool `yaml:"auto-register"` + AutoRefreshToken bool `yaml:"auto-refresh-token"` + RefreshInterval int64 `yaml:"refresh-interval"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 0d62c53..df2244b 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -29,6 +29,17 @@ account: # 账号相关 # 签名服务器所需要的apikey, 如果签名服务器的版本在1.1.0及以下则此项无效 # 本地部署的默认为114514 key: '114514' + # 在实例可能丢失(获取到的签名为空)时是否尝试重新注册 + # 为 true 时,在签名服务不可用时可能每次发消息都会尝试重新注册并签名。 + # 为 false 时,将不会自动注册实例,在签名服务器重启或实例被销毁后需要重启 go-cqhttp 以获取实例 + # 否则后续消息将不会正常签名。关闭此项后可以考虑开启签名服务器端 auto_register 避免需要重启 + auto-register: false + # 是否在 token 过期后立即自动刷新签名 token(在需要签名时才会检测到,主要防止 token 意外丢失) + # 独立于定时刷新 + auto-refresh-token: false + # 定时刷新 token 间隔时间,单位为分钟, 建议 30~40 分钟, 不可超过 60 分钟 + # 目前丢失token也不会有太大影响,可设置为 0 以关闭,推荐开启 + refresh-interval: 40 heartbeat: # 心跳频率, 单位秒 @@ -59,7 +70,7 @@ message: skip-mime-scan: false # 是否自动转换 WebP 图片 convert-webp-image: false - # http超时时间 + # http超时时间,同时作为签名服务超时时间 http-timeout: 0 output: From dae03784ccfd1d98d2d5de0ef664ca451fcf1b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:38:53 +0800 Subject: [PATCH 10/23] make lint happy --- cmd/gocq/login.go | 109 ++++++++++++++++++++++------------------------ coolq/api.go | 3 +- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 5828b03..1fb918f 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/Mrs4s/MiraiGo/client" @@ -398,52 +399,52 @@ func refreshToken(uin string) error { } msg := gjson.GetBytes(resp, "msg") if gjson.GetBytes(resp, "code").Int() != 0 { - return errors.New(msg) + return errors.New(msg.String()) } go callback(uin, gjson.GetBytes(resp, "data").Array(), "request token") - return true + return nil } var missTokenCount = uint64(0) func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { - i := 0 - for { - sign, extra, token, err = _sign(seq, uin, cmd, qua, buff) - if err != nil { - log.Warnf("获取sso sign时出现错误: %v server: %v", err, base.SignServer) - } - if i > 0 { - break - } - i++ - if (!base.IsBelow110) && base.Account.AutoRegister && err == nil && len(sign) == 0 { - if registerLock.TryLock() { // 避免并发时多处同时销毁并重新注册 - log.Warn("获取签名为空,实例可能丢失,正在尝试重新注册") - defer registerLock.Unlock() - err := destroySignServer(uin) - if err != nil { - log.Warnf(err) - return nil, nil, nil, err - } - register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) - } - continue - } - if (!base.IsBelow110) && base.Account.AutoRefreshToken && len(token) == 0 { - log.Warnf("token 已过期, 总丢失 token 次数为 %v", atomic.AddUint64(&missTokenCount, 1)) - if registerLock.TryLock() { - defer registerLock.Unlock() - if err := refreshToken(uin); err != nil { - log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) - } else { - log.Info("刷新 token 成功") - } - } - continue - } - break - } + i := 0 + for { + sign, extra, token, err = _sign(seq, uin, cmd, qua, buff) + if err != nil { + log.Warnf("获取sso sign时出现错误: %v server: %v", err, base.SignServer) + } + if i > 0 { + break + } + i++ + if (!base.IsBelow110) && base.Account.AutoRegister && err == nil && len(sign) == 0 { + if registerLock.TryLock() { // 避免并发时多处同时销毁并重新注册 + log.Warn("获取签名为空,实例可能丢失,正在尝试重新注册") + defer registerLock.Unlock() + err := destroySignServer(uin) + if err != nil { + log.Warnln(err) + return nil, nil, nil, err + } + register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) + } + continue + } + if (!base.IsBelow110) && base.Account.AutoRefreshToken && len(token) == 0 { + log.Warnf("token 已过期, 总丢失 token 次数为 %v", atomic.AddUint64(&missTokenCount, 1)) + if registerLock.TryLock() { + defer registerLock.Unlock() + if err := refreshToken(uin); err != nil { + log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) + } else { + log.Info("刷新 token 成功") + } + } + continue + } + break + } return sign, extra, token, err } @@ -453,9 +454,9 @@ func destroySignServer(uin string) error { signServer += "/" } signVersion, err := getSignServerVersion() - if err != nil { - return errors.Wrapf(err, "获取签名服务版本出现错误, server: %v", signServer) - } + if err != nil { + return errors.Wrapf(err, "获取签名服务版本出现错误, server: %v", signServer) + } if global.VersionNameCompare("v"+signVersion, "v1.1.6") { return errors.Errorf("当前签名服务器版本 %v 低于 1.1.6,无法使用 destroy 接口", signVersion) } @@ -463,14 +464,10 @@ func destroySignServer(uin string) error { Method: http.MethodGet, URL: signServer + "destroy" + fmt.Sprintf("?uin=%v&key=%v", uin, base.Key), }.Bytes() - if err != nil { + if err != nil || gjson.GetBytes(resp, "code").Int() != 0 { return errors.Wrapf(err, "destroy 实例出现错误, server: %v", signServer) } - msg := gjson.GetBytes(resp, "msg") - if gjson.GetBytes(resp, "code").Int() != 0 { - return errors.Wrapf(err, "destroy 实例出现错误, server: %v", signServer) - } - return nil + return nil } func getSignServerVersion() (version string, err error) { @@ -485,7 +482,7 @@ func getSignServerVersion() (version string, err error) { if gjson.GetBytes(resp, "code").Int() == 0 { return gjson.GetBytes(resp, "data.version").String(), nil } - return "", errors.New("empty version") + return "", errors.New("empty version") } // 定时刷新 token, interval 为间隔时间(分钟) @@ -502,18 +499,18 @@ func startRefreshTokenTask(interval int64) { log.Warn("间隔时间不能超过 60 分钟,已自动设置为 60 分钟") interval = 60 } - t := time.Newticker(time.Duration(interval) * time.Minute) - defer t.Stop() + t := time.NewTicker(time.Duration(interval) * time.Minute) + defer t.Stop() for range t.C { - err := refreshToken(strconv.FormatInt(base.Account.Uin, 10)) - if err != nil { - log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) - } + err := refreshToken(strconv.FormatInt(base.Account.Uin, 10)) + if err != nil { + log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) + } } } func waitSignServer() bool { - t := time.NewTicker(time.Second*5) + t := time.NewTicker(time.Second * 5) defer t.Stop() i := 0 for range t.C { diff --git a/coolq/api.go b/coolq/api.go index 5e5fda3..72b34b9 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1149,7 +1149,8 @@ func (bot *CQBot) CQDelGroupMemo(groupID int64, fid string) global.MSG { // @rename(msg->message, block->reject_add_request) func (bot *CQBot) CQSetGroupKick(groupID int64, userID int64, msg string, block bool) global.MSG { if g := bot.Client.FindGroup(groupID); g != nil { - if m := g.FindMember(userID); m == nil { + m := g.FindMember(userID) + if m == nil { return Failed(100, "MEMBER_IS_NOT_IN_GROUP", "人员不存在") } err := m.Kick(msg, block) From 04cbf7b5d71f37d8768d05c53609ab125c1e93dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:44:55 +0800 Subject: [PATCH 11/23] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/login.go | 40 ++++++++++++++++++++-------------------- cmd/gocq/main.go | 6 +++--- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 1fb918f..a6f6e88 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -299,8 +299,8 @@ func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { return data, nil } -// t: 提交的操作类型 -func submit(uin string, cmd string, callbackID int64, buffer []byte, t string) { +// signSubmit 提交的操作类型 +func signSubmit(uin string, cmd string, callbackID int64, buffer []byte, t string) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" @@ -318,8 +318,8 @@ func submit(uin string, cmd string, callbackID int64, buffer []byte, t string) { } } -// request token和签名的回调 -func callback(uin string, results []gjson.Result, t string) { +// signCallback request token 和签名的回调 +func signCallback(uin string, results []gjson.Result, t string) { for _, result := range results { cmd := result.Get("cmd").String() callbackID := result.Get("callbackId").Int() @@ -328,11 +328,11 @@ func callback(uin string, results []gjson.Result, t string) { if err != nil { log.Warnf("callback error: %v", err) } - submit(uin, cmd, callbackID, ret, t) + signSubmit(uin, cmd, callbackID, ret, t) } } -func _sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { +func signRequset(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" @@ -351,14 +351,14 @@ func _sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign [] extra, _ = hex.DecodeString(gjson.GetBytes(response, "data.extra").String()) token, _ = hex.DecodeString(gjson.GetBytes(response, "data.token").String()) if !base.IsBelow110 { - go callback(uin, gjson.GetBytes(response, "data.requestCallback").Array(), "sign") + go signCallback(uin, gjson.GetBytes(response, "data.requestCallback").Array(), "sign") } return sign, extra, token, nil } var registerLock sync.Mutex -func register(uin int64, androidID, guid []byte, qimei36, key string) { +func signRegister(uin int64, androidID, guid []byte, qimei36, key string) { if base.IsBelow110 { log.Warn("签名服务器版本低于1.1.0, 跳过实例注册") return @@ -384,7 +384,7 @@ func register(uin int64, androidID, guid []byte, qimei36, key string) { log.Infof("注册QQ实例 %v 成功: %v", uin, msg) } -func refreshToken(uin string) error { +func signRefreshToken(uin string) error { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" @@ -401,7 +401,7 @@ func refreshToken(uin string) error { if gjson.GetBytes(resp, "code").Int() != 0 { return errors.New(msg.String()) } - go callback(uin, gjson.GetBytes(resp, "data").Array(), "request token") + go signCallback(uin, gjson.GetBytes(resp, "data").Array(), "request token") return nil } @@ -410,7 +410,7 @@ var missTokenCount = uint64(0) func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { i := 0 for { - sign, extra, token, err = _sign(seq, uin, cmd, qua, buff) + sign, extra, token, err = signRequset(seq, uin, cmd, qua, buff) if err != nil { log.Warnf("获取sso sign时出现错误: %v server: %v", err, base.SignServer) } @@ -422,12 +422,12 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b if registerLock.TryLock() { // 避免并发时多处同时销毁并重新注册 log.Warn("获取签名为空,实例可能丢失,正在尝试重新注册") defer registerLock.Unlock() - err := destroySignServer(uin) + err := signServerDestroy(uin) if err != nil { log.Warnln(err) return nil, nil, nil, err } - register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) + signRegister(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) } continue } @@ -435,7 +435,7 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b log.Warnf("token 已过期, 总丢失 token 次数为 %v", atomic.AddUint64(&missTokenCount, 1)) if registerLock.TryLock() { defer registerLock.Unlock() - if err := refreshToken(uin); err != nil { + if err := signRefreshToken(uin); err != nil { log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) } else { log.Info("刷新 token 成功") @@ -448,12 +448,12 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b return sign, extra, token, err } -func destroySignServer(uin string) error { +func signServerDestroy(uin string) error { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" } - signVersion, err := getSignServerVersion() + signVersion, err := signVersion() if err != nil { return errors.Wrapf(err, "获取签名服务版本出现错误, server: %v", signServer) } @@ -470,7 +470,7 @@ func destroySignServer(uin string) error { return nil } -func getSignServerVersion() (version string, err error) { +func signVersion() (version string, err error) { signServer := base.SignServer resp, err := download.Request{ Method: http.MethodGet, @@ -486,7 +486,7 @@ func getSignServerVersion() (version string, err error) { } // 定时刷新 token, interval 为间隔时间(分钟) -func startRefreshTokenTask(interval int64) { +func signStartRefreshToken(interval int64) { if interval <= 0 { log.Warn("定时刷新 token 已关闭") return @@ -502,14 +502,14 @@ func startRefreshTokenTask(interval int64) { t := time.NewTicker(time.Duration(interval) * time.Minute) defer t.Stop() for range t.C { - err := refreshToken(strconv.FormatInt(base.Account.Uin, 10)) + err := signRefreshToken(strconv.FormatInt(base.Account.Uin, 10)) if err != nil { log.Warnf("刷新 token 出现错误: %v server: %v", err, base.SignServer) } } } -func waitSignServer() bool { +func signWaitServer() bool { t := time.NewTicker(time.Second * 5) defer t.Stop() i := 0 diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 281ae25..9204e15 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -168,11 +168,11 @@ func LoginInteract() { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 设置签名超时时间 // 等待签名服务器直到连接成功 - if !waitSignServer() { + if !signWaitServer() { log.Fatalf("连接签名服务器失败") } - register(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) - go startRefreshTokenTask(base.Account.RefreshInterval) // 定时刷新 token + signRegister(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, base.Key) + go signStartRefreshToken(base.Account.RefreshInterval) // 定时刷新 token wrapper.DandelionEnergy = energy wrapper.FekitGetSign = sign if !base.IsBelow110 { From 7adbbd6f81334d6190ccd7d1c8afa5ae4832ad63 Mon Sep 17 00:00:00 2001 From: SuperKenVery <39673849+SuperKenVery@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:48:14 +0800 Subject: [PATCH 12/23] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E6=97=B6=E9=97=B4=20(#2302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Increase timeout in download * 直接改一分钟好了 --------- Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- internal/download/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/download/download.go b/internal/download/download.go index feaec23..ac5ae03 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -33,7 +33,7 @@ var client = &http.Client{ TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{}, MaxIdleConnsPerHost: 999, }, - Timeout: time.Second * 15, + Timeout: time.Minute, } var clienth2 = &http.Client{ From 7c813f85796eed1373ddf8864b5dc9bb6d36e634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:09:04 +0800 Subject: [PATCH 13/23] feat: add config sign-server-timeout --- cmd/gocq/login.go | 14 ++++----- cmd/gocq/main.go | 4 +-- internal/base/flag.go | 4 ++- internal/download/download.go | 52 ++++++++++++++++++++----------- modules/config/config.go | 1 + modules/config/default_config.yml | 6 ++-- 6 files changed, 49 insertions(+), 32 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index a6f6e88..2823ae4 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -278,7 +278,7 @@ func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { Method: http.MethodGet, URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v&android_id=%v&guid=%v", id, hex.EncodeToString(salt), uin, utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)), - } + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second) if base.IsBelow110 { req.URL = signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)) } @@ -312,7 +312,7 @@ func signSubmit(uin string, cmd string, callbackID int64, buffer []byte, t strin Method: http.MethodGet, URL: signServer + "submit" + fmt.Sprintf("?uin=%v&cmd=%v&callback_id=%v&buffer=%v", uin, cmd, callbackID, buffStr), - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil { log.Warnf("提交 callback 时出现错误: %v server: %v", err, signServer) } @@ -343,7 +343,7 @@ func signRequset(seq uint64, uin string, cmd string, qua string, buff []byte) (s Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&qua=%s&cmd=%s&seq=%v&buffer=%v&android_id=%v&guid=%v", uin, qua, cmd, seq, hex.EncodeToString(buff), utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)))), - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil { return nil, nil, nil, err } @@ -371,7 +371,7 @@ func signRegister(uin int64, androidID, guid []byte, qimei36, key string) { Method: http.MethodGet, URL: signServer + "register" + fmt.Sprintf("?uin=%v&android_id=%v&guid=%v&qimei36=%v&key=%s", uin, utils.B2S(androidID), hex.EncodeToString(guid), qimei36, key), - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil { log.Warnf("注册QQ实例时出现错误: %v server: %v", err, signServer) return @@ -393,7 +393,7 @@ func signRefreshToken(uin string) error { resp, err := download.Request{ Method: http.MethodGet, URL: signServer + "request_token" + fmt.Sprintf("?uin=%v", uin), - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil { return err } @@ -463,7 +463,7 @@ func signServerDestroy(uin string) error { resp, err := download.Request{ Method: http.MethodGet, URL: signServer + "destroy" + fmt.Sprintf("?uin=%v&key=%v", uin, base.Key), - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil || gjson.GetBytes(resp, "code").Int() != 0 { return errors.Wrapf(err, "destroy 实例出现错误, server: %v", signServer) } @@ -475,7 +475,7 @@ func signVersion() (version string, err error) { resp, err := download.Request{ Method: http.MethodGet, URL: signServer, - }.Bytes() + }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() if err != nil { return "", err } diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 9204e15..7dbaff1 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -166,7 +166,6 @@ func LoginInteract() { if base.SignServer != "-" && base.SignServer != "" { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) - download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 设置签名超时时间 // 等待签名服务器直到连接成功 if !signWaitServer() { log.Fatalf("连接签名服务器失败") @@ -304,9 +303,9 @@ func LoginInteract() { cli.Uin = base.Account.Uin cli.PasswordMd5 = base.PasswordHash } + download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) if !base.FastStart { log.Infof("正在检查协议更新...") - download.SetTimeout(time.Second * 5) // 防止协议更新堵塞过久 currentVersionName := device.Protocol.Version().SortVersionName remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) if err == nil { @@ -391,7 +390,6 @@ func LoginInteract() { }) saveToken() cli.AllowSlider = true - download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 登陆完成进行最终超时设置 log.Infof("登录成功 欢迎使用: %v", cli.Nickname) log.Info("开始加载好友列表...") global.Check(cli.ReloadFriendList(), true) diff --git a/internal/base/flag.go b/internal/base/flag.go index 33ec62d..97e5128 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -41,7 +41,8 @@ var ( SignServer string // 使用特定的服务器进行签名 Key string // 签名服务器密钥 IsBelow110 bool // 签名服务器版本是否低于1.1.0及以下 - HTTPTimeout int + HTTPTimeout int // download 超时时间 + SignServerTimeout int // 签名服务器超时时间 PostFormat string // 上报格式 string or array Proxy string // 存储 proxy_rewrite,用于设置代理 @@ -94,6 +95,7 @@ func Init() { Key = conf.Account.Key IsBelow110 = conf.Account.IsBelow110 HTTPTimeout = conf.Message.HTTPTimeout + SignServerTimeout = conf.Message.SignServerTimeout } { // others Proxy = conf.Message.ProxyRewrite diff --git a/internal/download/download.go b/internal/download/download.go index ac5ae03..786658f 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -21,20 +21,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" ) -var client = &http.Client{ - Transport: &http.Transport{ - Proxy: func(request *http.Request) (*url.URL, error) { - if base.Proxy == "" { - return http.ProxyFromEnvironment(request) - } - return url.Parse(base.Proxy) - }, - // Disable http2 - TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{}, - MaxIdleConnsPerHost: 999, - }, - Timeout: time.Minute, -} +var client = newcli(time.Second * 15) var clienth2 = &http.Client{ Transport: &http.Transport{ @@ -50,12 +37,35 @@ var clienth2 = &http.Client{ Timeout: time.Second * 15, } +func newcli(t time.Duration) *http.Client { + return &http.Client{ + Transport: &http.Transport{ + Proxy: func(request *http.Request) (*url.URL, error) { + if base.Proxy == "" { + return http.ProxyFromEnvironment(request) + } + return url.Parse(base.Proxy) + }, + // Disable http2 + TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{}, + MaxIdleConnsPerHost: 999, + }, + Timeout: t, + } +} + // ErrOverSize 响应主体过大时返回此错误 var ErrOverSize = errors.New("oversize") // UserAgent HTTP请求时使用的UA const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" +// WithTimeout get a download instance with timeout t +func (r Request) WithTimeout(t time.Duration) *Request { + r.custcli = newcli(t) + return &r +} + // SetTimeout set internal/download client timeout func SetTimeout(t time.Duration) { if t == 0 { @@ -67,14 +77,18 @@ func SetTimeout(t time.Duration) { // Request is a file download request type Request struct { - Method string - URL string - Header map[string]string - Limit int64 - Body io.Reader + Method string + URL string + Header map[string]string + Limit int64 + Body io.Reader + custcli *http.Client } func (r Request) client() *http.Client { + if r.custcli != nil { + return r.custcli + } if strings.Contains(r.URL, "go-cqhttp.org") { return clienth2 } diff --git a/modules/config/config.go b/modules/config/config.go index f424054..5a02a7d 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -63,6 +63,7 @@ type Config struct { SkipMimeScan bool `yaml:"skip-mime-scan"` ConvertWebpImage bool `yaml:"convert-webp-image"` HTTPTimeout int `yaml:"http-timeout"` + SignServerTimeout int `yaml:"sign-server-timeout"` } `yaml:"message"` Output struct { diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index df2244b..b8f9b6d 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -70,8 +70,10 @@ message: skip-mime-scan: false # 是否自动转换 WebP 图片 convert-webp-image: false - # http超时时间,同时作为签名服务超时时间 - http-timeout: 0 + # download 超时时间(s) + http-timeout: 15 + # 签名服务超时时间(s) + sign-server-timeout: 60 output: # 日志等级 trace,debug,info,warn,error From 88f5db89a85ad43ed327b70491ae4d1b75a9ff43 Mon Sep 17 00:00:00 2001 From: Bluefissure Date: Mon, 31 Jul 2023 20:15:31 -0700 Subject: [PATCH 14/23] feat: add Bearer authentication to sign server requests (#2247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add sign-server-bearer * fix: golint * fix: golint * fix: remove trimprefix --------- Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- cmd/gocq/login.go | 17 +++++++++++++++-- cmd/gocq/main.go | 3 +++ internal/base/flag.go | 2 ++ modules/config/config.go | 1 + modules/config/default_config.yml | 3 +++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 2823ae4..12aa2da 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -24,6 +24,8 @@ import ( "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/download" @@ -269,13 +271,19 @@ func fetchCaptcha(id string) string { return "" } -func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { +func energy(_ uint64, id string, _ string, salt []byte) ([]byte, error) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" } + headers := make(map[string]string) + signServerBearer := base.SignServerBearer + if signServerBearer != "-" && signServerBearer != "" { + headers["Authorization"] = "Bearer " + signServerBearer + } req := download.Request{ Method: http.MethodGet, + Header: headers, URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v&android_id=%v&guid=%v", id, hex.EncodeToString(salt), uin, utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)), }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second) @@ -337,10 +345,15 @@ func signRequset(seq uint64, uin string, cmd string, qua string, buff []byte) (s if !strings.HasSuffix(signServer, "/") { signServer += "/" } + headers := map[string]string{"Content-Type": "application/x-www-form-urlencoded"} + signServerBearer := base.SignServerBearer + if signServerBearer != "-" && signServerBearer != "" { + headers["Authorization"] = "Bearer " + signServerBearer + } response, err := download.Request{ Method: http.MethodPost, URL: signServer + "sign", - Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + Header: headers, Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&qua=%s&cmd=%s&seq=%v&buffer=%v&android_id=%v&guid=%v", uin, qua, cmd, seq, hex.EncodeToString(buff), utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)))), }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second).Bytes() diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 7dbaff1..4e71431 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -166,6 +166,9 @@ func LoginInteract() { if base.SignServer != "-" && base.SignServer != "" { log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) + if base.SignServerBearer != "-" && base.SignServerBearer != "" { + log.Infof("使用 Bearer %s 认证签名服务器 %s ", base.SignServerBearer, base.SignServer) + } // 等待签名服务器直到连接成功 if !signWaitServer() { log.Fatalf("连接签名服务器失败") diff --git a/internal/base/flag.go b/internal/base/flag.go index 97e5128..8e0c37a 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -39,6 +39,7 @@ var ( AllowTempSession bool // 是否允许发送临时会话信息 UpdateProtocol bool // 是否更新协议 SignServer string // 使用特定的服务器进行签名 + SignServerBearer string // 认证签名服务器的 Bearer Token Key string // 签名服务器密钥 IsBelow110 bool // 签名服务器版本是否低于1.1.0及以下 HTTPTimeout int // download 超时时间 @@ -92,6 +93,7 @@ func Init() { UseSSOAddress = conf.Account.UseSSOAddress AllowTempSession = conf.Account.AllowTempSession SignServer = conf.Account.SignServer + SignServerBearer = conf.Account.SignServerBearer Key = conf.Account.Key IsBelow110 = conf.Account.IsBelow110 HTTPTimeout = conf.Message.HTTPTimeout diff --git a/modules/config/config.go b/modules/config/config.go index 5a02a7d..4bfbc18 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -36,6 +36,7 @@ type Account struct { UseSSOAddress bool `yaml:"use-sso-address"` AllowTempSession bool `yaml:"allow-temp-session"` SignServer string `yaml:"sign-server"` + SignServerBearer string `yaml:"sign-server-bearer"` Key string `yaml:"key"` IsBelow110 bool `yaml:"is-below-110"` AutoRegister bool `yaml:"auto-register"` diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index b8f9b6d..5acb845 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -24,6 +24,9 @@ account: # 账号相关 # sign-server: 'https://signserver.example.com' # 线上签名服务器 # 服务器可使用docker在本地搭建或者使用他人开放的服务 sign-server: '-' + # 签名服务器认证 Bearer Token + # 使用开放的服务可能需要提供此 Token 进行认证 + sign-server-bearer: '-' # 如果签名服务器的版本在1.1.0及以下, 请将下面的参数改成true is-below-110: false # 签名服务器所需要的apikey, 如果签名服务器的版本在1.1.0及以下则此项无效 From 5cb8548487637cfae868bc6fbc10aa3b084bfcdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:16:58 +0800 Subject: [PATCH 15/23] resolve conflicts --- cmd/gocq/login.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 12aa2da..0bc2d7a 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -24,8 +24,6 @@ import ( "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/download" @@ -271,7 +269,7 @@ func fetchCaptcha(id string) string { return "" } -func energy(_ uint64, id string, _ string, salt []byte) ([]byte, error) { +func energy(uin uint64, id string, _ string, salt []byte) ([]byte, error) { signServer := base.SignServer if !strings.HasSuffix(signServer, "/") { signServer += "/" @@ -283,7 +281,7 @@ func energy(_ uint64, id string, _ string, salt []byte) ([]byte, error) { } req := download.Request{ Method: http.MethodGet, - Header: headers, + Header: headers, URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v&uin=%v&android_id=%v&guid=%v", id, hex.EncodeToString(salt), uin, utils.B2S(device.AndroidId), hex.EncodeToString(device.Guid)), }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second) From 2b1d9c21cb9af3d2cd5922f26c0cc54e8bb0959f Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Tue, 1 Aug 2023 11:20:02 +0800 Subject: [PATCH 16/23] =?UTF-8?q?Fix=20#2226=20-=20=E4=BF=AE=E6=94=B9=20co?= =?UTF-8?q?olq/cqcode.go#L54=20=E5=AE=89=E5=8D=93=E7=AB=AF=E7=A7=81?= =?UTF-8?q?=E8=81=8A=E5=9B=9E=E5=A4=8D=E6=97=B6r.GroupID=E4=BC=BC=E4=B9=8E?= =?UTF-8?q?=E4=B8=BA0=20(#2230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/cqcode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index a08a275..6397280 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -51,7 +51,7 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { } // 私聊时,部分(不确定)的账号会在 ReplyElement 中带有 GroupID 字段。 // 这里需要判断是由于 “直接回复” 功能,GroupID 为触发直接回复的来源那个群。 - if source.SourceType == message.SourcePrivate && (r.Sender == source.PrimaryID || r.GroupID == source.PrimaryID) { + if source.SourceType == message.SourcePrivate && (r.Sender == source.PrimaryID || r.GroupID == source.PrimaryID || r.GroupID == 0) { // 私聊似乎腾讯服务器有bug? seq = int32(uint16(seq)) id = r.Sender From cffdfd8181983c57927683c3cba6050136664723 Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Tue, 1 Aug 2023 11:24:59 +0800 Subject: [PATCH 17/23] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BD=BF=20get=5Fmsg?= =?UTF-8?q?=20=E8=8E=B7=E5=8F=96=E6=B6=88=E6=81=AF=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D=E4=BF=A1=E6=81=AF=20(#2207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/api.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/coolq/api.go b/coolq/api.go index 72b34b9..7749832 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1686,8 +1686,26 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { switch o := msg.(type) { case *db.StoredGroupMessage: m["group_id"] = o.GroupCode + if o.QuotedInfo != nil { + elem := global.MSG{ + "type": "reply", + "data": global.MSG{ + "id": strconv.FormatInt(int64(o.QuotedInfo.PrevGlobalID), 10), + }, + } + o.Content = append(o.Content, elem) + } m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}) case *db.StoredPrivateMessage: + if o.QuotedInfo != nil { + elem := global.MSG{ + "type": "reply", + "data": global.MSG{ + "id": strconv.FormatInt(int64(o.QuotedInfo.PrevGlobalID), 10), + }, + } + o.Content = append(o.Content, elem) + } m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}) } return OK(m) From 75ad7aa45c1080fc21af4e8eff61898767a48d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=9E=E7=81=AA?= <995645888@qq.com> Date: Tue, 1 Aug 2023 11:25:44 +0800 Subject: [PATCH 18/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=92=8C?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=B8=8D=E4=B8=80=E8=87=B4=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=20(#2189)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复获取群荣誉列表群聊之火键名错误问题 * fix: 修复获取版本信息,错误的go-cqhttp字段 --- coolq/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 7749832..72d29b4 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1390,7 +1390,7 @@ func (bot *CQBot) CQGetGroupHonorInfo(groupID int64, t string) global.MSG { if t == "performer" || t == "all" { if honor, err := bot.Client.GetGroupHonorInfo(groupID, client.Performer); err == nil { - msg["performer_lis"] = convertMem(honor.ActorList) + msg["performer_list"] = convertMem(honor.ActorList) } } @@ -2032,7 +2032,7 @@ func (bot *CQBot) CQGetVersionInfo() global.MSG { "protocol_version": "v11", "coolq_directory": wd, "coolq_edition": "pro", - "go-cqhttp": true, + "go_cqhttp": true, "plugin_version": "4.15.0", "plugin_build_number": 99, "plugin_build_configuration": "release", From 14539adcb8c69eaa785e46e3be2c1e1a2e548c23 Mon Sep 17 00:00:00 2001 From: Lu7fer <59082785+Lu7fer@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:32:32 +0800 Subject: [PATCH 19/23] fix: #2112 add CQ code reply to db and solve recursive reply resolve (#2115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- coolq/api.go | 21 ++++++++++++++------- coolq/bot.go | 26 +++++++++++++++----------- coolq/cqcode.go | 23 +++++++++++++++-------- coolq/event.go | 4 ++-- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 72d29b4..db8673b 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -896,7 +896,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType SenderId: m.GetAttribute().SenderUin, SenderName: m.GetAttribute().SenderName, Time: int32(msgTime), - Message: resolveElement(bot.ConvertContentMessage(m.GetContent(), mSource)), + Message: resolveElement(bot.ConvertContentMessage(m.GetContent(), mSource, false)), } } log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) @@ -971,7 +971,10 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa if m.Type != gjson.JSON { return Failed(100) } - + source := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: 0, + } fe := bot.uploadForwardElement(m, groupID, message.SourceGroup) if fe == nil { return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息") @@ -981,7 +984,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.") return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } - mid := bot.InsertGroupMessage(ret) + mid := bot.InsertGroupMessage(ret, source) log.Infof("发送群 %v(%v) 的合并转发消息: %v (%v)", groupID, groupID, limitedString(m.String()), mid) return OK(global.MSG{ "message_id": mid, @@ -1695,7 +1698,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { } o.Content = append(o.Content, elem) } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup, false), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}) case *db.StoredPrivateMessage: if o.QuotedInfo != nil { elem := global.MSG{ @@ -1706,7 +1709,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { } o.Content = append(o.Content, elem) } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate, false), message.Source{SourceType: message.SourcePrivate}) } return OK(m) } @@ -1766,7 +1769,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), "nickname": channelMsgByDB.Attribute.SenderName, } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel, false), source) } case message.SourceGuildDirect: // todo(mrs4s): 支持 direct 消息 @@ -1809,10 +1812,14 @@ func (bot *CQBot) CQGetGroupMessageHistory(groupID int64, seq int64) global.MSG log.Warnf("获取群历史消息失败: %v", err) return Failed(100, "MESSAGES_API_ERROR", err.Error()) } + source := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: 0, + } ms := make([]*event, 0, len(msg)) for _, m := range msg { bot.checkMedia(m.Elements, groupID) - id := bot.InsertGroupMessage(m) + id := bot.InsertGroupMessage(m, source) t := bot.formatGroupMessage(m) t.Others["message_id"] = id ms = append(ms, t) diff --git a/coolq/bot.go b/coolq/bot.go index 512a1e6..e2e3231 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -288,7 +288,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupID, err) return -1, errors.Wrap(err, "send group music share error") } - return bot.InsertGroupMessage(ret), nil + return bot.InsertGroupMessage(ret, source), nil case *message.AtElement: if i.Target == 0 && group.SelfPermission() == client.Member { e = message.NewText("@全体成员") @@ -307,7 +307,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in log.Warnf("群消息发送失败: 账号可能被风控.") return -1, errors.New("send group message failed: blocked by server") } - return bot.InsertGroupMessage(ret), nil + return bot.InsertGroupMessage(ret, source), nil } // SendPrivateMessage 发送私聊消息 @@ -357,7 +357,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen case bot.Client.FindFriend(target) != nil: // 双向好友 msg := bot.Client.SendPrivateMessage(target, m) if msg != nil { - id = bot.InsertPrivateMessage(msg) + id = bot.InsertPrivateMessage(msg, source) } case ok || groupID != 0: // 临时会话 if !base.AllowTempSession { @@ -395,7 +395,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen case unidirectionalFriendExists(): // 单向好友 msg := bot.Client.SendPrivateMessage(target, m) if msg != nil { - id = bot.InsertPrivateMessage(msg) + id = bot.InsertPrivateMessage(msg, source) } default: nickname := "Unknown" @@ -444,7 +444,7 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. } // InsertGroupMessage 群聊消息入数据库 -func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 { +func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage, source message.Source) int32 { t := &message.SendingMessage{Elements: m.Elements} replyElem := t.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -468,7 +468,7 @@ func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 { } return "" }(), - Content: ToMessageContent(m.Elements), + Content: ToMessageContent(m.Elements, source), } if replyElem != nil { reply := replyElem.(*message.ReplyElement) @@ -476,7 +476,7 @@ func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 { msg.QuotedInfo = &db.QuotedInfo{ PrevID: encodeMessageID(m.GroupCode, reply.ReplySeq), PrevGlobalID: db.ToGlobalID(m.GroupCode, reply.ReplySeq), - QuotedContent: ToMessageContent(reply.Elements), + QuotedContent: ToMessageContent(reply.Elements, source), } } if err := db.InsertGroupMessage(msg); err != nil { @@ -487,7 +487,7 @@ func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 { } // InsertPrivateMessage 私聊消息入数据库 -func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 { +func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage, source message.Source) int32 { t := &message.SendingMessage{Elements: m.Elements} replyElem := t.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -511,7 +511,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 { return m.Sender.Uin }(), TargetUin: m.Target, - Content: ToMessageContent(m.Elements), + Content: ToMessageContent(m.Elements, source), } if replyElem != nil { reply := replyElem.(*message.ReplyElement) @@ -519,7 +519,7 @@ func (bot *CQBot) InsertPrivateMessage(m *message.PrivateMessage) int32 { msg.QuotedInfo = &db.QuotedInfo{ PrevID: encodeMessageID(reply.Sender, reply.ReplySeq), PrevGlobalID: db.ToGlobalID(reply.Sender, reply.ReplySeq), - QuotedContent: ToMessageContent(reply.Elements), + QuotedContent: ToMessageContent(reply.Elements, source), } } if err := db.InsertPrivateMessage(msg); err != nil { @@ -562,6 +562,10 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 // InsertGuildChannelMessage 频道消息入数据库 func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) string { id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id, message.SourceGuildChannel) + source := message.Source{ + SourceType: message.SourceGuildChannel, + PrimaryID: int64(m.Sender.TinyId), + } msg := &db.StoredGuildChannelMessage{ ID: id, Attribute: &db.StoredGuildMessageAttribute{ @@ -573,7 +577,7 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri }, GuildID: m.GuildId, ChannelID: m.ChannelId, - Content: ToMessageContent(m.Elements), + Content: ToMessageContent(m.Elements, source), } if err := db.InsertGuildChannelMessage(msg); err != nil { log.Warnf("记录聊天数据时出现错误: %v", err) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 6397280..8c37f17 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -263,13 +263,17 @@ func toElements(e []message.IMessageElement, source message.Source) (r []msg.Ele return } -// ToMessageContent 将消息转换成 Content. 忽略 Reply // 不同于 onebot 的 Array Message, 此函数转换出来的 Content 的 data 段为实际类型 // 方便数据库查询 -func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { +func ToMessageContent(e []message.IMessageElement, source message.Source) (r []global.MSG) { for _, elem := range e { var m global.MSG switch o := elem.(type) { + case *message.ReplyElement: + m = global.MSG{ + "type": "reply", + "data": global.MSG{"id": replyID(o, source)}, + } case *message.TextElement: m = global.MSG{ "type": "text", @@ -384,7 +388,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { // ConvertStringMessage 将消息字符串转为消息元素数组 func (bot *CQBot) ConvertStringMessage(spec *onebot.Spec, raw string, sourceType message.SourceType) (r []message.IMessageElement) { elems := msg.ParseString(raw) - return bot.ConvertElements(spec, elems, sourceType) + return bot.ConvertElements(spec, elems, sourceType, true) } // ConvertObjectMessage 将消息JSON对象转为消息元素数组 @@ -393,11 +397,11 @@ func (bot *CQBot) ConvertObjectMessage(spec *onebot.Spec, m gjson.Result, source return bot.ConvertStringMessage(spec, m.Str, sourceType) } elems := msg.ParseObject(m) - return bot.ConvertElements(spec, elems, sourceType) + return bot.ConvertElements(spec, elems, sourceType, false) } // ConvertContentMessage 将数据库用的 content 转换为消息元素数组 -func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType, noReply bool) (r []message.IMessageElement) { elems := make([]msg.Element, len(content)) for i, v := range content { elem := msg.Element{Type: v["type"].(string)} @@ -407,13 +411,16 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message } elems[i] = elem } - return bot.ConvertElements(onebot.V11, elems, sourceType) + return bot.ConvertElements(onebot.V11, elems, sourceType, noReply) } // ConvertElements 将解码后的消息数组转换为MiraiGo表示 -func (bot *CQBot) ConvertElements(spec *onebot.Spec, elems []msg.Element, sourceType message.SourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertElements(spec *onebot.Spec, elems []msg.Element, sourceType message.SourceType, noReply bool) (r []message.IMessageElement) { var replyCount int for _, elem := range elems { + if noReply && elem.Type == "reply" { + continue + } me, err := bot.ConvertElement(spec, elem, sourceType) if err != nil { // TODO: don't use cqcode format @@ -497,7 +504,7 @@ func (bot *CQBot) reply(spec *onebot.Spec, elem msg.Element, sourceType message. ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), + Elements: bot.ConvertContentMessage(org.GetContent(), sourceType, true), } default: diff --git a/coolq/event.go b/coolq/event.go index 605ea3e..61e763f 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -79,7 +79,7 @@ func (bot *CQBot) privateMessageEvent(_ *client.QQClient, m *message.PrivateMess PrimaryID: m.Sender.Uin, } cqm := toStringMessage(m.Elements, source) - id := bot.InsertPrivateMessage(m) + id := bot.InsertPrivateMessage(m, source) log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id) typ := "message/private/friend" if m.Sender.Uin == bot.Client.Uin { @@ -126,7 +126,7 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) PrimaryID: m.GroupCode, } cqm := toStringMessage(m.Elements, source) - id := bot.InsertGroupMessage(m) + id := bot.InsertGroupMessage(m, source) log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id) gm := bot.formatGroupMessage(m) if gm == nil { From b8527721c2c3a3881e985f2c8d8331d204da4d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:34:34 +0800 Subject: [PATCH 20/23] make lint happy --- coolq/cqcode.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 8c37f17..1ce3dd2 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -263,6 +263,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []msg.Ele return } +// ToMessageContent 将消息转换成 Content. 忽略 Reply // 不同于 onebot 的 Array Message, 此函数转换出来的 Content 的 data 段为实际类型 // 方便数据库查询 func ToMessageContent(e []message.IMessageElement, source message.Source) (r []global.MSG) { From 1bd0bb9ae20b523f3511c9c029a333d7747a437f Mon Sep 17 00:00:00 2001 From: A lucky guy <79245287+tom-snow@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:35:22 +0800 Subject: [PATCH 21/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Drelease=20?= =?UTF-8?q?=E7=9A=84=20action=20=E5=8F=AA=E6=8B=89=E4=B8=BB=E4=BB=93?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E4=BF=AE=E5=A4=8D=20checkout=20=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E7=9A=84=E9=97=AE=E9=A2=98=20(#1999)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35f4ace..39a7985 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,8 @@ jobs: - name: Checkout run: | git version - git clone https://github.com/Mrs4s/go-cqhttp.git /home/runner/work/go-cqhttp/go-cqhttp + git clone "${{ github.event.repository.html_url }}" /home/runner/work/go-cqhttp/go-cqhttp + git checkout "${{ github.ref }}" - name: Set up Go uses: actions/setup-go@v3 From 7cae9829a8eb11dcaaef35a4e106bd304e480242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=84=86=E9=A5=BC=E5=B9=B2?= Date: Tue, 1 Aug 2023 11:45:05 +0800 Subject: [PATCH 22/23] api: _send_group_notice return noticeId (#1834) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- coolq/api.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index db8673b..63f1a73 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1113,17 +1113,18 @@ func (bot *CQBot) CQSetGroupMemo(groupID int64, msg, img string) global.MSG { if err != nil { return Failed(100, "IMAGE_NOT_FOUND", "图片未找到") } - _, err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data) + noticeID, err := bot.Client.AddGroupNoticeWithPic(groupID, msg, data) if err != nil { return Failed(100, "SEND_NOTICE_ERROR", err.Error()) } + return OK(global.MSG{"notice_id": noticeID}) } else { - _, err := bot.Client.AddGroupNoticeSimple(groupID, msg) + noticeID, err := bot.Client.AddGroupNoticeSimple(groupID, msg) if err != nil { return Failed(100, "SEND_NOTICE_ERROR", err.Error()) } + return OK(global.MSG{"notice_id": noticeID}) } - return OK(nil) } return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") } From fe92bb54df63c63c510bb615c516f0b89f173ed0 Mon Sep 17 00:00:00 2001 From: PSoul Date: Tue, 1 Aug 2023 11:47:11 +0800 Subject: [PATCH 23/23] api: rename kick message type (#1775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 踢人时进行判断,当该人不在群内时返回人员不存在的错误 Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com> --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 63f1a73..4dd2fd2 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1155,7 +1155,7 @@ func (bot *CQBot) CQSetGroupKick(groupID int64, userID int64, msg string, block if g := bot.Client.FindGroup(groupID); g != nil { m := g.FindMember(userID) if m == nil { - return Failed(100, "MEMBER_IS_NOT_IN_GROUP", "人员不存在") + return Failed(100, "MEMBER_NOT_FOUND", "人员不存在") } err := m.Kick(msg, block) if err != nil {