From 079bd9d6edd92db76da25541107bbdf6996d685a Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 27 Mar 2021 20:31:54 +0800 Subject: [PATCH 1/5] update self update --- .goreleaser.yml | 3 +- global/fs.go | 100 -------------------------------- global/update/update.go | 96 ++++++++++++++++++++++++++++++ global/update/update_others.go | 50 ++++++++++++++++ global/update/update_windows.go | 35 +++++++++++ main.go | 38 +++--------- 6 files changed, 192 insertions(+), 130 deletions(-) create mode 100644 global/update/update.go create mode 100644 global/update/update_others.go create mode 100644 global/update/update_windows.go diff --git a/.goreleaser.yml b/.goreleaser.yml index af89e8c..60090a3 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -57,4 +57,5 @@ nfpms: file_name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" formats: - deb - - rpm \ No newline at end of file + - rpm + maintainer: Mrs4s \ No newline at end of file diff --git a/global/fs.go b/global/fs.go index ebdf4a1..89cf593 100644 --- a/global/fs.go +++ b/global/fs.go @@ -1,28 +1,20 @@ package global import ( - "bufio" "bytes" - "compress/bzip2" "crypto/md5" "encoding/base64" "encoding/hex" "errors" - "fmt" - "io" "io/ioutil" "net" "net/url" "os" "path" - "path/filepath" "runtime" "strconv" "strings" - "github.com/kardianos/osext" - - "github.com/dustin/go-humanize" log "github.com/sirupsen/logrus" ) @@ -160,95 +152,3 @@ func ReadAddrFile(path string) []*net.TCPAddr { } return ret } - -// WriteCounter 写入量计算实例 -type WriteCounter struct { - Total uint64 -} - -// Write 方法将写入的byte长度追加至写入的总长度Total中 -func (wc *WriteCounter) Write(p []byte) (int, error) { - n := len(p) - wc.Total += uint64(n) - wc.PrintProgress() - return n, nil -} - -// PrintProgress 方法将打印当前的总写入量 -func (wc *WriteCounter) PrintProgress() { - fmt.Printf("\r%s", strings.Repeat(" ", 35)) - fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) -} - -// UpdateFromStream copy form getlantern/go-update -func UpdateFromStream(updateWith io.Reader) (err error, errRecover error) { - updatePath, err := osext.Executable() - if err != nil { - return - } - var newBytes []byte - // no patch to apply, go on through - var fileHeader []byte - bufBytes := bufio.NewReader(updateWith) - fileHeader, err = bufBytes.Peek(2) - if err != nil { - return - } - // The content is always bzip2 compressed except when running test, in - // which case is not prefixed with the magic byte sequence for sure. - if bytes.Equal([]byte{0x42, 0x5a}, fileHeader) { - // Identifying bzip2 files. - updateWith = bzip2.NewReader(bufBytes) - } else { - updateWith = io.Reader(bufBytes) - } - newBytes, err = ioutil.ReadAll(updateWith) - if err != nil { - return - } - // get the directory the executable exists in - updateDir := filepath.Dir(updatePath) - filename := filepath.Base(updatePath) - // Copy the contents of of newbinary to a the new executable file - newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) - fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) - if err != nil { - return - } - // We won't log this error, because it's always going to happen. - defer func() { _ = fp.Close() }() - if _, err = io.Copy(fp, bytes.NewReader(newBytes)); err != nil { - log.Errorf("Unable to copy data: %v\n", err) - } - - // if we don't call fp.Close(), windows won't let us move the new executable - // because the file will still be "in use" - if err := fp.Close(); err != nil { - log.Errorf("Unable to close file: %v\n", err) - } - // this is where we'll move the executable to so that we can swap in the updated replacement - oldPath := filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename)) - - // delete any existing old exec file - this is necessary on Windows for two reasons: - // 1. after a successful update, Windows can't remove the .old file because the process is still running - // 2. windows rename operations fail if the destination file already exists - _ = os.Remove(oldPath) - - // move the existing executable to a new file in the same directory - err = os.Rename(updatePath, oldPath) - if err != nil { - return - } - - // move the new executable in to become the new program - err = os.Rename(newPath, updatePath) - - if err != nil { - // copy unsuccessful - errRecover = os.Rename(oldPath, updatePath) - } else { - // copy successful, remove the old binary - _ = os.Remove(oldPath) - } - return -} diff --git a/global/update/update.go b/global/update/update.go new file mode 100644 index 0000000..8acd24e --- /dev/null +++ b/global/update/update.go @@ -0,0 +1,96 @@ +package update + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/dustin/go-humanize" + "github.com/kardianos/osext" + log "github.com/sirupsen/logrus" +) + +// WriteCounter 写入量计算实例 +type WriteCounter struct { + Total uint64 +} + +// Write 方法将写入的byte长度追加至写入的总长度Total中 +func (wc *WriteCounter) Write(p []byte) (int, error) { + n := len(p) + wc.Total += uint64(n) + wc.PrintProgress() + return n, nil +} + +// PrintProgress 方法将打印当前的总写入量 +func (wc *WriteCounter) PrintProgress() { + fmt.Printf("\r%s", strings.Repeat(" ", 35)) + fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) +} + +// UpdateFromStream copy form getlantern/go-update +func UpdateFromStream(updateWith io.Reader) (err error, errRecover error) { + updatePath, err := osext.Executable() + if err != nil { + return + } + var newBytes []byte + // no patch to apply, go on through + bufBytes := bufio.NewReader(updateWith) + updateWith = io.Reader(bufBytes) + newBytes, err = ioutil.ReadAll(updateWith) + if err != nil { + return + } + // get the directory the executable exists in + updateDir := filepath.Dir(updatePath) + filename := filepath.Base(updatePath) + // Copy the contents of of newbinary to a the new executable file + newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) + fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) + if err != nil { + return + } + // We won't log this error, because it's always going to happen. + defer func() { _ = fp.Close() }() + if _, err = io.Copy(fp, bytes.NewReader(newBytes)); err != nil { + log.Errorf("Unable to copy data: %v\n", err) + } + + // if we don't call fp.Close(), windows won't let us move the new executable + // because the file will still be "in use" + if err := fp.Close(); err != nil { + log.Errorf("Unable to close file: %v\n", err) + } + // this is where we'll move the executable to so that we can swap in the updated replacement + oldPath := filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename)) + + // delete any existing old exec file - this is necessary on Windows for two reasons: + // 1. after a successful update, Windows can't remove the .old file because the process is still running + // 2. windows rename operations fail if the destination file already exists + _ = os.Remove(oldPath) + + // move the existing executable to a new file in the same directory + err = os.Rename(updatePath, oldPath) + if err != nil { + return + } + + // move the new executable in to become the new program + err = os.Rename(newPath, updatePath) + + if err != nil { + // copy unsuccessful + errRecover = os.Rename(oldPath, updatePath) + } else { + // copy successful, remove the old binary + _ = os.Remove(oldPath) + } + return +} diff --git a/global/update/update_others.go b/global/update/update_others.go new file mode 100644 index 0000000..7a93296 --- /dev/null +++ b/global/update/update_others.go @@ -0,0 +1,50 @@ +// +build !windows + +package update + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "io" + "net/http" + + log "github.com/sirupsen/logrus" +) + +func Update(url string) { + resp, err := http.Get(url) + if err != nil { + log.Error("更新失败: ", err) + return + } + defer resp.Body.Close() + wc := WriteCounter{} + data, err := io.ReadAll(io.TeeReader(resp.Body, &wc)) + if err != nil { + log.Error("更新失败: ", err) + return + } + gr, err := gzip.NewReader(bytes.NewReader(data)) + if err != nil { + log.Error("更新失败: ", err) + return + } + tr := tar.NewReader(gr) + for { + header, err := tr.Next() + if err == io.EOF { + return + } + if header.Name == "go-cqhttp" { + err, _ := UpdateFromStream(tr) + fmt.Println() + if err != nil { + log.Error("更新失败!", err) + return + } + log.Info("更新完成!") + } + } +} diff --git a/global/update/update_windows.go b/global/update/update_windows.go new file mode 100644 index 0000000..1414b1a --- /dev/null +++ b/global/update/update_windows.go @@ -0,0 +1,35 @@ +package update + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "net/http" + + log "github.com/sirupsen/logrus" +) + +func Update(url string) { + resp, err := http.Get(url) + if err != nil { + log.Error("更新失败: ", err) + return + } + defer resp.Body.Close() + wc := WriteCounter{} + rsp, _ := io.ReadAll(io.TeeReader(resp.Body, &wc)) + reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength) + file, err := reader.Open("go-cqhttp.exe") + if err != nil { + log.Error("更新失败!", err) + return + } + err, _ = UpdateFromStream(file) + fmt.Println() + if err != nil { + log.Error("更新失败!", err) + return + } + log.Info("更新完成!") +} diff --git a/main.go b/main.go index 009f92b..1d92f6b 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,7 @@ import ( "encoding/hex" "flag" "fmt" - "io" "io/ioutil" - "net/http" "os" "os/signal" "path" @@ -25,6 +23,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/global/terminal" + "github.com/Mrs4s/go-cqhttp/global/update" "github.com/Mrs4s/go-cqhttp/server" "github.com/Mrs4s/MiraiGo/binary" @@ -488,8 +487,9 @@ func selfUpdate(imageURL string) { log.Info("当前最新版本为 ", version) log.Warn("是否更新(y/N): ") r := strings.TrimSpace(readLine()) - - doUpdate := func() { + if r != "y" && r != "Y" { + log.Warn("已取消更新!") + } else { log.Info("正在更新,请稍等...") url := fmt.Sprintf( "%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp-%v-%v-%v", @@ -499,34 +499,14 @@ func selfUpdate(imageURL string) { } return "https://github.com" }(), - version, - version, - runtime.GOOS, - runtime.GOARCH, + version, version, runtime.GOOS, runtime.GOARCH, ) if runtime.GOOS == "windows" { - url += ".exe" + url += ".zip" + } else { + url += ".tar.gz" } - resp, err := http.Get(url) - if err != nil { - log.Error("更新失败: ", err) - return - } - defer func() { _ = resp.Body.Close() }() - wc := global.WriteCounter{} - err, _ = global.UpdateFromStream(io.TeeReader(resp.Body, &wc)) - fmt.Println() - if err != nil { - log.Error("更新失败!") - return - } - log.Info("更新完成!") - } - - if r == "y" || r == "Y" { - doUpdate() - } else { - log.Warn("已取消更新!") + update.Update(url) } } else { log.Info("当前版本已经是最新版本!") From 6f0f66132afbf4f190adcd89c6a87fdce006c4ca Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Sat, 27 Mar 2021 21:30:21 +0800 Subject: [PATCH 2/5] feat: session resume. --- global/config.go | 1 + go.mod | 3 ++- go.sum | 6 ++++-- main.go | 43 ++++++++++++++++++++++++++++++++----------- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/global/config.go b/global/config.go index e6108ff..853c5bd 100644 --- a/global/config.go +++ b/global/config.go @@ -15,6 +15,7 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary var currentPath = getCurrentPath() var DefaultConfFile = path.Join(currentPath, "config.hjson") +var AccountToken []byte // DefaultConfigWithComments 为go-cqhttp的默认配置文件 var DefaultConfigWithComments = ` diff --git a/go.mod b/go.mod index 0db82f6..8bcd3b5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f - github.com/Mrs4s/MiraiGo v0.0.0-20210323143736-d233c90d5083 + github.com/Mrs4s/MiraiGo v0.0.0-20210327114026-05f0087b4f79 github.com/dustin/go-humanize v1.0.0 github.com/gin-gonic/gin v1.6.3 github.com/gorilla/websocket v1.4.2 @@ -26,6 +26,7 @@ require ( github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 github.com/willf/bitset v1.1.11 // indirect golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b + golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba ) diff --git a/go.sum b/go.sum index e75fc3b..0eaec93 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Mrs4s/MiraiGo v0.0.0-20210323143736-d233c90d5083 h1:ELaNvv80OTwHTYhKwoQpgV4dneKPM1qE5Geu3A1kM/8= -github.com/Mrs4s/MiraiGo v0.0.0-20210323143736-d233c90d5083/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw= +github.com/Mrs4s/MiraiGo v0.0.0-20210327114026-05f0087b4f79 h1:eiTXqIOigPaS+Ls8rZotUV2TVC4pQ4t3CHIre18k/NY= +github.com/Mrs4s/MiraiGo v0.0.0-20210327114026-05f0087b4f79/go.mod h1:NjiWhlvGxwv1ftOWIoiFa/OzklnAYI4YqNexFOKSZKw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -136,6 +136,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/main.go b/main.go index 1d92f6b..b22339f 100644 --- a/main.go +++ b/main.go @@ -165,7 +165,7 @@ func main() { log.Warning("将等待10s后启动") time.Sleep(time.Second * 10) } - if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") { + if (conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "")) && !global.PathExists("session.token") { log.Warn("账号密码未配置, 将使用二维码登录.") if !isFastStart { log.Warn("将在 5秒 后继续.") @@ -313,15 +313,33 @@ func main() { // b := server.WebServer.Run(fmt.Sprintf("%s:%d", conf.WebUI.Host, conf.WebUI.WebUIPort), cli) // c := server.Console isQRCodeLogin := (conf.Uin == 0 || len(conf.Password) == 0) && len(conf.PasswordEncrypted) == 0 - if !isQRCodeLogin { - if err := commonLogin(); err != nil { - log.Fatalf("登录时发生致命错误: %v", err) - } - } else { - if err := qrcodeLogin(); err != nil { - log.Fatalf("登录时发生致命错误: %v", err) + isTokenLogin := false + if global.PathExists("session.token") { + token, err := ioutil.ReadFile("session.token") + if err == nil { + if err = cli.TokenLogin(token); err != nil { + log.Warnf("恢复会话失败: %v , 尝试使用正常流程登录.", err) + } else { + isTokenLogin = true + } } } + if !isTokenLogin { + if !isQRCodeLogin { + if err := commonLogin(); err != nil { + log.Fatalf("登录时发生致命错误: %v", err) + } + } else { + if err := qrcodeLogin(); err != nil { + log.Fatalf("登录时发生致命错误: %v", err) + } + } + } + saveToken := func() { + global.AccountToken = cli.GenToken() + _ = ioutil.WriteFile("session.token", global.AccountToken, 0677) + } + saveToken() var times uint = 1 // 重试次数 var reLoginLock sync.Mutex cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { @@ -331,9 +349,6 @@ func main() { if !conf.ReLogin.Enabled { os.Exit(1) } - if isQRCodeLogin { - log.Fatalf("二维码登录暂不支持重连.") - } if times > conf.ReLogin.MaxReloginTimes && conf.ReLogin.MaxReloginTimes != 0 { log.Fatalf("Bot重连次数超过限制, 停止") } @@ -344,6 +359,12 @@ func main() { if cli.Online { return } + if err := cli.TokenLogin(global.AccountToken); err == nil { + saveToken() + } + if isQRCodeLogin { + log.Fatalf("二维码登录暂不支持重连.") + } if err := commonLogin(); err != nil { log.Fatalf("登录时发生致命错误: %v", err) } From 798df7e9dd243b64d0fab8cfb5d99d463086d31c Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Sat, 27 Mar 2021 21:52:10 +0800 Subject: [PATCH 3/5] fix typo. --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index b22339f..8ad8a11 100644 --- a/main.go +++ b/main.go @@ -363,7 +363,7 @@ func main() { saveToken() } if isQRCodeLogin { - log.Fatalf("二维码登录暂不支持重连.") + log.Fatalf("快速重连失败") } if err := commonLogin(); err != nil { log.Fatalf("登录时发生致命错误: %v", err) From 001d9aa0d60436fca0c1b26a0666c422ddf71414 Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Sat, 27 Mar 2021 21:53:36 +0800 Subject: [PATCH 4/5] fix reconnect. --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 8ad8a11..05fb4c9 100644 --- a/main.go +++ b/main.go @@ -361,6 +361,7 @@ func main() { } if err := cli.TokenLogin(global.AccountToken); err == nil { saveToken() + return } if isQRCodeLogin { log.Fatalf("快速重连失败") From 8db68f9427e7adbef3d36a0e23ffb10d7d660168 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 28 Mar 2021 00:11:13 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20cqcode=20fix=20url=20=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E5=86=99=E7=9A=84=E6=97=B6=E5=80=99=E6=86=A8=E6=89=B9?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/cqcode.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 3f8c160..e27b82f 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -443,7 +443,13 @@ S1: // Plain Text if *(*byte)(add(ptr, uintptr(i))) == '[' && i+4 < l && *(*uint32)(add(ptr, uintptr(i))) == magicCQ { // Magic :uint32([]byte("[CQ:")) if i > j { - r = append(r, message.NewText(CQCodeUnescapeText(s[j:i]))) + if SplitURL { + for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) { + r = append(r, message.NewText(str)) + } + } else { + r = append(r, message.NewText(CQCodeUnescapeText(s[j:i]))) + } } CQBegin = i i += 4 @@ -501,7 +507,13 @@ S4: // CQCode param value goto End End: if i > j { - r = append(r, message.NewText(CQCodeUnescapeText(s[j:i]))) + if SplitURL { + for _, str := range global.SplitURL(CQCodeUnescapeText(s[j:i])) { + r = append(r, message.NewText(str)) + } + } else { + r = append(r, message.NewText(CQCodeUnescapeText(s[j:i]))) + } } return }