diff --git a/coolq/event.go b/coolq/event.go index 03f35d6..3cbc746 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -9,6 +9,7 @@ import ( "time" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" @@ -16,18 +17,16 @@ import ( log "github.com/sirupsen/logrus" ) -var format = "string" - // SetMessageFormat 设置消息上报格式,默认为string func SetMessageFormat(f string) { - format = f + base.PostFormat = f } // ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 func ToFormattedMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r interface{}) { - if format == "string" { + if base.PostFormat == "string" { r = ToStringMessage(e, groupID, isRaw...) - } else if format == "array" { + } else if base.PostFormat == "array" { r = ToArrayMessage(e, groupID) } return diff --git a/global/update/update.go b/global/update/update.go deleted file mode 100644 index b7028e5..0000000 --- a/global/update/update.go +++ /dev/null @@ -1,93 +0,0 @@ -// Package update 包含go-cqhttp自我更新相关函数 -package update - -import ( - "bufio" - "bytes" - "fmt" - "hash" - "io" - "os" - "path/filepath" - - "github.com/dustin/go-humanize" - "github.com/kardianos/osext" - log "github.com/sirupsen/logrus" -) - -// WriteSumCounter 写入量计算实例 -type WriteSumCounter struct { - Total uint64 - Hash hash.Hash -} - -// Write 方法将写入的byte长度追加至写入的总长度Total中 -func (wc *WriteSumCounter) Write(p []byte) (int, error) { - n := len(p) - wc.Total += uint64(n) - wc.Hash.Write(p) - fmt.Printf("\r ") - fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) - return n, nil -} - -// FromStream copy form getlantern/go-update -func FromStream(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 = io.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, 0o755) - 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/internal/base/flag.go b/internal/base/flag.go index 5d5516a..ad83a92 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -1,7 +1,11 @@ // Package base provides base config for go-cqhttp package base -import "github.com/Mrs4s/go-cqhttp/global/config" +import ( + log "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/global/config" +) // flags var ( @@ -13,9 +17,10 @@ var ( ForceFragmented bool // 是否启用强制分片 SkipMimeScan bool // 是否跳过Mime扫描 - Proxy string // 存储 proxy_rewrite,用于设置代理 - PasswordHash [16]byte // 存储QQ密码哈希供登录使用 - AccountToken []byte // 存储AccountToken供登录使用 + PostFormat = "string" // 上报格式 string or array + Proxy string // 存储 proxy_rewrite,用于设置代理 + PasswordHash [16]byte // 存储QQ密码哈希供登录使用 + AccountToken []byte // 存储AccountToken供登录使用 ) // Parse parses flags from config file @@ -32,5 +37,11 @@ func Parse() { } { // string Proxy = conf.Message.ProxyRewrite + if conf.Message.PostFormat != "string" && conf.Message.PostFormat != "array" { + log.Warnf("post-format 配置错误, 将自动使用 string") + PostFormat = "string" + } else { + PostFormat = conf.Message.PostFormat + } } } diff --git a/internal/selfupdate/update.go b/internal/selfupdate/update.go new file mode 100644 index 0000000..2a05f60 --- /dev/null +++ b/internal/selfupdate/update.go @@ -0,0 +1,206 @@ +// Package selfupdate 版本升级检查和自更新 +package selfupdate + +import ( + "bufio" + "encoding/hex" + "fmt" + "hash" + "io" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/dustin/go-humanize" + "github.com/kardianos/osext" + "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" + + "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +func readLine() (str string) { + console := bufio.NewReader(os.Stdin) + str, _ = console.ReadString('\n') + str = strings.TrimSpace(str) + return +} + +func lastVersion() (string, error) { + r, err := global.GetBytes("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest") + if err != nil { + return "", err + } + return gjson.GetBytes(r, "tag_name").Str, nil +} + +// CheckUpdate 检查更新 +func CheckUpdate() { + logrus.Infof("正在检查更新.") + if base.Version == "(devel)" { + logrus.Warnf("检查更新失败: 使用的 Actions 测试版或自编译版本.") + return + } + latest, err := lastVersion() + if err != nil { + logrus.Warnf("检查更新失败: %v", err) + return + } + if global.VersionNameCompare(base.Version, latest) { + logrus.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/Mrs4s/go-cqhttp/releases 下载.") + logrus.Infof("当前版本: %v 最新版本: %v", base.Version, latest) + return + } + logrus.Infof("检查更新完成. 当前已运行最新版本.") +} + +func binaryName() string { + goarch := runtime.GOARCH + if goarch == "arm" { + goarch += "v7" + } + ext := "tar.gz" + if runtime.GOOS == "windows" { + ext = "zip" + } + return fmt.Sprintf("go-cqhttp_%v_%v.%v", runtime.GOOS, goarch, ext) +} + +func checksum(github, version string) []byte { + sumURL := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_checksums.txt", github, version) + closer, err := global.HTTPGetReadCloser(sumURL) + if err != nil { + return nil + } + + rd := bufio.NewReader(closer) + for { + str, err := rd.ReadString('\n') + if err != nil { + break + } + str = strings.TrimSpace(str) + if strings.HasSuffix(str, binaryName()) { + sum, _ := hex.DecodeString(strings.TrimSuffix(str, " "+binaryName())) + return sum + } + } + return nil +} + +func wait() { + logrus.Info("按 Enter 继续....") + readLine() + os.Exit(0) +} + +// SelfUpdate 自更新 +func SelfUpdate(github string) { + if github == "" { + github = "https://github.com" + } + + logrus.Infof("正在检查更新.") + latest, err := lastVersion() + if err != nil { + logrus.Warnf("获取最新版本失败: %v", err) + wait() + } + url := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/%v", github, latest, binaryName()) + if base.Version == latest { + logrus.Info("当前版本已经是最新版本!") + wait() + } + logrus.Info("当前最新版本为 ", latest) + logrus.Warn("是否更新(y/N): ") + r := strings.TrimSpace(readLine()) + if r != "y" && r != "Y" { + logrus.Warn("已取消更新!") + wait() + } + logrus.Info("正在更新,请稍等...") + sum := checksum(github, latest) + if sum != nil { + err = update(url, sum) + if err != nil { + logrus.Error("更新失败: ", err) + } else { + logrus.Info("更新成功!") + } + } else { + logrus.Error("checksum 失败!") + } + wait() +} + +// writeSumCounter 写入量计算实例 +type writeSumCounter struct { + total uint64 + hash hash.Hash +} + +// Write 方法将写入的byte长度追加至写入的总长度Total中 +func (wc *writeSumCounter) Write(p []byte) (int, error) { + n := len(p) + wc.total += uint64(n) + wc.hash.Write(p) + fmt.Printf("\r ") + fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.total)) + return n, nil +} + +// FromStream copy form getlantern/go-update +func fromStream(updateWith io.Reader) (err error, errRecover error) { + updatePath, err := osext.Executable() + 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, 0o755) + 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, bufio.NewReader(updateWith)); err != nil { + logrus.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 { + logrus.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/internal/selfupdate/update_others.go similarity index 75% rename from global/update/update_others.go rename to internal/selfupdate/update_others.go index 21d39c7..73e370f 100644 --- a/global/update/update_others.go +++ b/internal/selfupdate/update_others.go @@ -1,7 +1,7 @@ //go:build !windows // +build !windows -package update +package selfupdate import ( "archive/tar" @@ -14,21 +14,21 @@ import ( "net/http" ) -// Update go-cqhttp自我更新 -func Update(url string, sum []byte) error { +// update go-cqhttp自我更新 +func update(url string, sum []byte) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() - wc := WriteSumCounter{ - Hash: sha256.New(), + wc := writeSumCounter{ + hash: sha256.New(), } rsp, err := io.ReadAll(io.TeeReader(resp.Body, &wc)) if err != nil { return err } - if !bytes.Equal(wc.Hash.Sum(nil), sum) { + if !bytes.Equal(wc.hash.Sum(nil), sum) { return errors.New("文件已损坏") } gr, err := gzip.NewReader(bytes.NewReader(rsp)) @@ -42,7 +42,7 @@ func Update(url string, sum []byte) error { return err } if header.Name == "go-cqhttp" { - err, _ := FromStream(tr) + err, _ := fromStream(tr) fmt.Println() if err != nil { return err diff --git a/global/update/update_windows.go b/internal/selfupdate/update_windows.go similarity index 71% rename from global/update/update_windows.go rename to internal/selfupdate/update_windows.go index 7fce75b..d80bbc7 100644 --- a/global/update/update_windows.go +++ b/internal/selfupdate/update_windows.go @@ -1,4 +1,4 @@ -package update +package selfupdate import ( "archive/zip" @@ -10,21 +10,21 @@ import ( "net/http" ) -// Update go-cqhttp自我更新 -func Update(url string, sum []byte) error { +// update go-cqhttp自我更新 +func update(url string, sum []byte) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() - wc := WriteSumCounter{ - Hash: sha256.New(), + wc := writeSumCounter{ + hash: sha256.New(), } rsp, err := io.ReadAll(io.TeeReader(resp.Body, &wc)) if err != nil { return err } - if !bytes.Equal(wc.Hash.Sum(nil), sum) { + if !bytes.Equal(wc.hash.Sum(nil), sum) { return errors.New("文件已损坏") } reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength) @@ -32,7 +32,7 @@ func Update(url string, sum []byte) error { if err != nil { return err } - err, _ = FromStream(file) + err, _ = fromStream(file) fmt.Println() if err != nil { return err diff --git a/login.go b/login.go index cdf39cb..292376b 100644 --- a/login.go +++ b/login.go @@ -24,7 +24,7 @@ func readLine() (str string) { return } -var readLineTimeout = func(t time.Duration, de string) (str string) { +func readLineTimeout(t time.Duration, de string) (str string) { r := make(chan string) go func() { select { diff --git a/main.go b/main.go index ed0de4e..6c939d0 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "bufio" "crypto/aes" "crypto/md5" "crypto/sha1" @@ -12,18 +11,15 @@ import ( "os/exec" "path" "path/filepath" - "runtime" "strings" "sync" "time" "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" - "github.com/Mrs4s/MiraiGo/utils" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" log "github.com/sirupsen/logrus" - "github.com/tidwall/gjson" "golang.org/x/crypto/pbkdf2" "golang.org/x/term" @@ -31,8 +27,8 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/global/config" "github.com/Mrs4s/go-cqhttp/global/terminal" - "github.com/Mrs4s/go-cqhttp/global/update" "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/internal/selfupdate" "github.com/Mrs4s/go-cqhttp/server" ) @@ -119,9 +115,9 @@ func main() { switch arg[i] { case "update": if len(arg) > i+1 { - selfUpdate(arg[i+1]) + selfupdate.SelfUpdate(arg[i+1]) } else { - selfUpdate("") + selfupdate.SelfUpdate("") } case "key": p := i + 1 @@ -359,13 +355,6 @@ func main() { } cli.SetOnlineStatus(allowStatus[int(conf.Account.Status)]) bot := coolq.NewQQBot(cli, conf) - _ = bot.Client - if conf.Message.PostFormat != "string" && conf.Message.PostFormat != "array" { - log.Warnf("post-format 配置错误, 将自动使用 string") - coolq.SetMessageFormat("string") - } else { - coolq.SetMessageFormat(conf.Message.PostFormat) - } for _, m := range conf.Servers { if h, ok := m["http"]; ok { hc := new(config.HTTPServer) @@ -411,7 +400,7 @@ func main() { log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") - go checkUpdate() + go selfupdate.CheckUpdate() <-global.SetupMainSignalHandler() } @@ -447,104 +436,6 @@ func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, erro return result, nil } -func checkUpdate() { - log.Infof("正在检查更新.") - if base.Version == "(devel)" { - log.Warnf("检查更新失败: 使用的 Actions 测试版或自编译版本.") - return - } - r, err := global.GetBytes("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest") - if err != nil { - log.Warnf("检查更新失败: %v", err) - return - } - info := gjson.Parse(utils.B2S(r)) - if global.VersionNameCompare(base.Version, info.Get("tag_name").Str) { - log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/Mrs4s/go-cqhttp/releases 下载.") - log.Infof("当前版本: %v 最新版本: %v", base.Version, info.Get("tag_name").Str) - return - } - log.Infof("检查更新完成. 当前已运行最新版本.") -} - -func selfUpdate(imageURL string) { - log.Infof("正在检查更新.") - var r string - res, err := global.GetBytes("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest") - if err != nil { - log.Warnf("检查更新失败: %v", err) - return - } - info := gjson.Parse(utils.B2S(res)) - version := info.Get("tag_name").Str - if base.Version == version { - log.Info("当前版本已经是最新版本!") - goto wait - } - log.Info("当前最新版本为 ", version) - log.Warn("是否更新(y/N): ") - r = strings.TrimSpace(readLine()) - if r != "y" && r != "Y" { - log.Warn("已取消更新!") - } else { - log.Info("正在更新,请稍等...") - sumURL := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_checksums.txt", - func() string { - if imageURL != "" { - return imageURL - } - return "https://github.com" - }(), version) - closer, err := global.HTTPGetReadCloser(sumURL) - if err != nil { - log.Error("更新失败: ", err) - goto wait - } - rd := bufio.NewReader(closer) - binaryName := fmt.Sprintf("go-cqhttp_%v_%v.%v", runtime.GOOS, func() string { - if runtime.GOARCH == "arm" { - return "armv7" - } - return runtime.GOARCH - }(), func() string { - if runtime.GOOS == "windows" { - return "zip" - } - return "tar.gz" - }()) - var sum []byte - for { - str, err := rd.ReadString('\n') - if err != nil { - break - } - str = strings.TrimSpace(str) - if strings.HasSuffix(str, binaryName) { - sum, _ = hex.DecodeString(strings.TrimSuffix(str, " "+binaryName)) - break - } - } - url := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/%v", - func() string { - if imageURL != "" { - return imageURL - } - return "https://github.com" - }(), version, binaryName) - - err = update.Update(url, sum) - if err != nil { - log.Error("更新失败: ", err) - } else { - log.Info("更新成功!") - } - } -wait: - log.Info("按 Enter 继续....") - readLine() - os.Exit(0) -} - // help cli命令行-h的帮助提示 func help() { fmt.Printf(`go-cqhttp service