diff --git a/api/conn.go b/api/conn.go index 671c1607..3e719e02 100644 --- a/api/conn.go +++ b/api/conn.go @@ -165,7 +165,7 @@ func ImConnectionsRWSignServerUrl(c echo.Context) error { } if i.ProtocolType == "onebot" { pa := i.Adapter.(*dice.PlatformAdapterGocq) - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { signServerUrl, signServerVersion := dice.RWLagrangeSignServerUrl(myDice, i, v.SignServerUrl, v.W, v.SignServerVersion) if signServerUrl != "" { return Success(&c, Response{ @@ -207,7 +207,7 @@ func ImConnectionsDel(c echo.Context) error { myDice.ImSession.EndPoints = append(myDice.ImSession.EndPoints[:index], myDice.ImSession.EndPoints[index+1:]...) if i.ProtocolType == "onebot" { pa := i.Adapter.(*dice.PlatformAdapterGocq) - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { dice.BuiltinQQServeProcessKillBase(myDice, i, true) // 经测试,若不延时,可能导致清理对应目录失败(原因:文件被占用) time.Sleep(1 * time.Second) @@ -969,6 +969,7 @@ func ImConnectionsAddBuiltinLagrange(c echo.Context) error { Account string `yaml:"account" json:"account"` SignServerUrl string `yaml:"signServerUrl" json:"signServerUrl"` SignServerVersion string `yaml:"signServerVersion" json:"signServerVersion"` + IsGocq bool `yaml:"isGocq" json:"isGocq"` }{} err := c.Bind(&v) if err == nil { @@ -977,7 +978,7 @@ func ImConnectionsAddBuiltinLagrange(c echo.Context) error { return nil } - conn := dice.NewLagrangeConnectInfoItem(v.Account) + conn := dice.NewLagrangeConnectInfoItem(v.Account, v.IsGocq) conn.UserID = dice.FormatDiceIDQQ(uid) conn.Session = myDice.ImSession pa := conn.Adapter.(*dice.PlatformAdapterGocq) diff --git a/api/utils.go b/api/utils.go index eea0bb36..e1a01456 100644 --- a/api/utils.go +++ b/api/utils.go @@ -148,6 +148,8 @@ func checkUidExists(c echo.Context, uid string) bool { var relWorkDir string if pa.BuiltinMode == "lagrange" { relWorkDir = "extra/lagrange-qq" + uid + } else if pa.BuiltinMode == "lagrange-gocq" { + relWorkDir = "extra/lagrange-gocq-qq" + uid } else { // 默认为gocq relWorkDir = "extra/go-cqhttp-qq" + uid diff --git a/dice/platform_adapter_gocq.go b/dice/platform_adapter_gocq.go index 23b7f189..8cd8cf88 100644 --- a/dice/platform_adapter_gocq.go +++ b/dice/platform_adapter_gocq.go @@ -399,7 +399,7 @@ func (pa *PlatformAdapterGocq) SendSegmentToPerson(ctx *MsgContext, userID strin } func (pa *PlatformAdapterGocq) Serve() int { - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { pa.Implementation = "lagrange" } else { pa.Implementation = "gocq" @@ -1221,7 +1221,7 @@ func (pa *PlatformAdapterGocq) DoRelogin() bool { if pa.InPackGoCqhttpDisconnectedCH != nil { pa.InPackGoCqhttpDisconnectedCH <- -1 } - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { myDice.Logger.Infof("重新启动 lagrange 进程,对应账号: <%s>(%s)", ep.Nickname, ep.UserID) pa.CurLoginIndex++ pa.GoCqhttpState = StateCodeInit @@ -1268,7 +1268,7 @@ func (pa *PlatformAdapterGocq) SetEnable(enable bool) { c.Enable = true if pa.UseInPackClient { - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { BuiltinQQServeProcessKill(d, c) time.Sleep(1 * time.Second) LagrangeServe(d, c, LagrangeLoginInfo{ diff --git a/dice/platform_adapter_lagrange_helper.go b/dice/platform_adapter_lagrange_helper.go index 0778488d..3f00a276 100644 --- a/dice/platform_adapter_lagrange_helper.go +++ b/dice/platform_adapter_lagrange_helper.go @@ -15,6 +15,7 @@ import ( "time" "github.com/google/uuid" + "gopkg.in/yaml.v3" log "sealdice-core/utils/kratos" "sealdice-core/utils/procs" @@ -32,19 +33,22 @@ func lagrangeGetWorkDir(dice *Dice, conn *EndPointInfo) string { return workDir } -func NewLagrangeConnectInfoItem(account string) *EndPointInfo { +func NewLagrangeConnectInfoItem(account string, isGocq bool) *EndPointInfo { conn := new(EndPointInfo) conn.ID = uuid.New().String() conn.Platform = "QQ" conn.ProtocolType = "onebot" conn.Enable = false conn.RelWorkDir = "extra/lagrange-qq" + account - conn.Adapter = &PlatformAdapterGocq{ EndPoint: conn, UseInPackClient: true, BuiltinMode: "lagrange", } + if isGocq { + conn.RelWorkDir = "extra/lagrange-gocq-qq" + account + conn.Adapter.(*PlatformAdapterGocq).BuiltinMode = "lagrange-gocq" + } return conn } @@ -55,11 +59,15 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) loginIndex := pa.CurLoginIndex pa.GoCqhttpState = StateCodeInLogin - if pa.UseInPackClient && pa.BuiltinMode == "lagrange" { //nolint:nestif + if pa.UseInPackClient && (pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq") { //nolint:nestif helper := log.NewCustomHelper(log.LOG_LAGR, false, nil) if dice.ContainerMode { - helper.Warn("onebot: 尝试启动内置客户端,但内置客户端在容器模式下被禁用") + if pa.BuiltinMode == "lagrange" { + helper.Warn("onebot: 尝试启动内置客户端,但内置客户端在容器模式下被禁用") + } else { + helper.Warn("onebot: 尝试启动内置gocq,但内置gocq在容器模式下被禁用") + } conn.State = 3 pa.GoCqhttpState = StateCodeLoginFailed dice.Save(false) @@ -70,22 +78,23 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) _ = os.MkdirAll(workDir, 0o755) wd, _ := os.Getwd() exeFilePath, _ := filepath.Abs(filepath.Join(wd, "lagrange/Lagrange.OneBot")) + qrcodeFilePath := filepath.Join(workDir, fmt.Sprintf("qr-%s.png", conn.UserID[3:])) + configFilePath := filepath.Join(workDir, "appsettings.json") + + if pa.BuiltinMode == "lagrange-gocq" { + exeFilePath, _ = filepath.Abs(filepath.Join(wd, "lagrange/go-cqhttp")) + qrcodeFilePath = filepath.Join(workDir, "qrcode.png") + configFilePath = filepath.Join(workDir, "config.yml") + } + exeFilePath = filepath.ToSlash(exeFilePath) // windows平台需要这个替换 if runtime.GOOS == "windows" { exeFilePath += ".exe" } - qrcodeFilePath := filepath.Join(workDir, fmt.Sprintf("qr-%s.png", conn.UserID[3:])) - configFilePath := filepath.Join(workDir, "appsettings.json") if _, err := os.Stat(qrcodeFilePath); err == nil { // 如果已经存在二维码文件,将其删除 _ = os.Remove(qrcodeFilePath) - } else { - // 如果找不到二维码文件,有一种可能是用户添加账号时写错了账号,这里做个兼容让错误的账号依旧能获取到二维码 - qrcodeFilePath = filepath.Join(workDir, fmt.Sprintf("qr-%s.png", conn.RelWorkDir[17:])) - if _, err := os.Stat(qrcodeFilePath); err == nil { - _ = os.Remove(qrcodeFilePath) - } } helper.Info("onebot: 删除已存在的二维码文件") @@ -93,9 +102,17 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) pa.ConnectURL = "" if file, err := os.ReadFile(configFilePath); err == nil { var result map[string]interface{} - if err := json.Unmarshal(file, &result); err == nil { - if val, ok := result["Implementations"].([]interface{})[0].(map[string]interface{})["Port"].(float64); ok { - pa.ConnectURL = fmt.Sprintf("ws://127.0.0.1:%d", int(val)) + if pa.BuiltinMode == "lagrange" { + if err := json.Unmarshal(file, &result); err == nil { + if val, ok := result["Implementations"].([]interface{})[0].(map[string]interface{})["Port"].(float64); ok { + pa.ConnectURL = fmt.Sprintf("ws://127.0.0.1:%d", int(val)) + } + } + } else { + if err := yaml.Unmarshal(file, &result); err == nil { + if val, ok := result["servers"].([]interface{})[0].(map[string]interface{})["ws"].(map[string]interface{})["address"].(string); ok { + pa.ConnectURL = fmt.Sprintf("ws://%s", val) + } } } } @@ -155,13 +172,21 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) // 登录中 if pa.IsInLogin() { + qrcodeSignal := "QrCode Fetched" + onlineSignal := "Bot Online: " + qrcodeExpiredSignal := "QrCode Expired, Please Fetch QrCode Again" + if pa.BuiltinMode == "lagrange-gocq" { + qrcodeSignal = "qrcode.png" + onlineSignal = "登录成功" + qrcodeExpiredSignal = "QrCode Expired, Please Fetch QrCode Again" + } // 读取二维码 - if strings.Contains(line, "QrCode Fetched") { + if strings.Contains(line, qrcodeSignal) { chQrCode <- 1 } // 登录成功 - if strings.Contains(line, "Success") || strings.Contains(line, "Bot Online: ") { + if strings.Contains(line, "Success") || strings.Contains(line, onlineSignal) { pa.GoCqhttpState = StateCodeLoginSuccessed pa.GoCqhttpLoginSucceeded = true helper.Infof("onebot: 登录成功,账号:<%s>(%s)", conn.Nickname, conn.UserID) @@ -174,7 +199,7 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) go ServeQQ(dice, conn) } - if strings.Contains(line, "QrCode Expired, Please Fetch QrCode Again") { + if strings.Contains(line, qrcodeExpiredSignal) { // 二维码过期,登录失败,杀掉进程 pa.GoCqhttpState = StateCodeLoginFailed helper.Infof("onebot: 二维码过期,登录失败,账号:%s", conn.UserID) @@ -338,30 +363,91 @@ var defaultLagrangeConfig = ` ] } ` +var defaultLagrangeGocqConfig = ` +account: + relogin: + delay: 3 + interval: 3 + max-times: 0 + use-sso-address: true + allow-temp-session: false + sign-servers: + - url: '{NTSignServer地址}' + - url: 'https://sign.lagrangecore.org/api/sign/25765' + + max-check-count: 0 + sign-server-timeout: 60 + +heartbeat: + interval: 5 + +message: + post-format: string + ignore-invalid-cqcode: false + force-fragment: false + fix-url: false + proxy-rewrite: '' + report-self-message: false + remove-reply-at: false + extra-reply-data: false + skip-mime-scan: false + convert-webp-image: false + http-timeout: 15 + +output: + log-level: warn + log-aging: 15 + log-force-new: true + log-colorful: true + debug: false + +default-middlewares: &default + access-token: '' + filter: '' + rate-limit: + enabled: false + frequency: 1 + bucket: 1 + +database: + leveldb: + enable: true + sqlite3: + enable: false + cachettl: 3600000000000 # 1h + +servers: + - ws: + address: 127.0.0.1:{WS端口} + middlewares: + <<: *default +` // 在构建时注入 -var defaultNTSignServer = `https://lwxmagic.sealdice.com/api/sign` -var lagrangeNTSignServer = "https://sign.lagrangecore.org/api/sign" +// var defaultNTSignServer = `https://lwxmagic.sealdice.com/api/sign` +// var lagrangeNTSignServer = "https://sign.lagrangecore.org/api/sign" + +// 此处添加内置sign地址及对应标识字符串 +var signServers = map[string]string{ + "sealdice": `https://lwxmagic.sealdice.com/api/sign`, + "lagrange": "https://sign.lagrangecore.org/api/sign", +} func GenerateLagrangeConfig(port int, signServerUrl string, signServerVersion string, info *EndPointInfo) string { - switch signServerUrl { - case "": - signServerUrl = defaultNTSignServer - if signServerVersion != "" && signServerVersion != "13107" { - signServerUrl += "/" + signServerVersion - } - case "sealdice": - signServerUrl = defaultNTSignServer - if signServerVersion != "" && signServerVersion != "13107" { - signServerUrl += "/" + signServerVersion - } - case "lagrange": - signServerUrl = lagrangeNTSignServer + if signServerUrl == "" { + signServerUrl = "sealdice" + } + if url, exists := signServers[signServerUrl]; exists { + signServerUrl = url if signServerVersion != "" && signServerVersion != "13107" { signServerUrl += "/" + signServerVersion } } + pa := info.Adapter.(*PlatformAdapterGocq) conf := strings.ReplaceAll(defaultLagrangeConfig, "{WS端口}", strconv.Itoa(port)) + if pa.BuiltinMode == "lagrange-gocq" { + conf = strings.ReplaceAll(defaultLagrangeGocqConfig, "{WS端口}", strconv.Itoa(port)) + } conf = strings.ReplaceAll(conf, "{NTSignServer地址}", signServerUrl) conf = strings.ReplaceAll(conf, "{账号UIN}", info.UserID[3:]) return conf @@ -369,8 +455,13 @@ func GenerateLagrangeConfig(port int, signServerUrl string, signServerVersion st func LagrangeServeRemoveSession(dice *Dice, conn *EndPointInfo) { workDir := gocqGetWorkDir(dice, conn) - if _, err := os.Stat(filepath.Join(workDir, "keystore.json")); err == nil { - _ = os.Remove(filepath.Join(workDir, "keystore.json")) + file := filepath.Join(workDir, "keystore.json") + pa := conn.Adapter.(*PlatformAdapterGocq) + if pa.BuiltinMode == "lagrange-gocq" { + file = filepath.Join(workDir, "session.token") + } + if _, err := os.Stat(file); err == nil { + _ = os.Remove(file) } } @@ -386,52 +477,87 @@ func LagrangeServeRemoveConfig(dice *Dice, conn *EndPointInfo) { } func RWLagrangeSignServerUrl(dice *Dice, conn *EndPointInfo, signServerUrl string, w bool, signServerVersion string) (string, string) { - switch signServerUrl { - case "sealdice": - signServerUrl = defaultNTSignServer - if signServerVersion != "" && signServerVersion != "13107" { - signServerUrl += "/" + signServerVersion - } - case "lagrange": - signServerUrl = "https://sign.lagrangecore.org/api/sign" + if signServerUrl == "" { + signServerUrl = "sealdice" + } + if url, exists := signServers[signServerUrl]; exists { + signServerUrl = url if signServerVersion != "" && signServerVersion != "13107" { signServerUrl += "/" + signServerVersion } } workDir := lagrangeGetWorkDir(dice, conn) configFilePath := filepath.Join(workDir, "appsettings.json") + pa := conn.Adapter.(*PlatformAdapterGocq) + + if pa.BuiltinMode == "lagrange-gocq" { + configFilePath = filepath.Join(workDir, "config.yml") + } + + currentSignServerUrl := "" file, err := os.ReadFile(configFilePath) - if err == nil { - var result map[string]interface{} + if err != nil { + dice.Logger.Infof("读取内置客户端配置失败,账号:%s, 原因: %s", conn.UserID, err.Error()) + return "", "" + } + + var result map[string]interface{} + if pa.BuiltinMode == "lagrange" { err = json.Unmarshal(file, &result) - if err == nil { - if val, ok := result["SignServerUrl"].(string); ok { - if w { - result["SignServerUrl"] = signServerUrl - result["SignServerVersion"] = signServerVersion - var c []byte - if c, err = json.MarshalIndent(result, "", " "); err == nil { - _ = os.WriteFile(configFilePath, c, 0o644) - } else { - dice.Logger.Infof("SignServerUrl字段无法正常覆写,账号:%s, 原因: %s", conn.UserID, err.Error()) - } + if err != nil { + dice.Logger.Infof("读取内置客户端配置失败,账号:%s, 原因: %s", conn.UserID, err.Error()) + return "", "" + } + if val, ok := result["SignServerUrl"].(string); ok { + currentSignServerUrl = val + if w { + result["SignServerUrl"] = signServerUrl + var c []byte + c, err = json.MarshalIndent(result, "", " ") + if err != nil { + dice.Logger.Infof("SignServerUrl字段无法正常覆写,账号:%s, 原因: %s", conn.UserID, err.Error()) } - - var version string - if strings.HasPrefix(val, defaultNTSignServer) { - version, _ = strings.CutPrefix(val, defaultNTSignServer) - version, _ = strings.CutPrefix(version, "/") - val = "sealdice" - } else if strings.HasPrefix(val, lagrangeNTSignServer) { - version, _ = strings.CutPrefix(val, lagrangeNTSignServer) - version, _ = strings.CutPrefix(version, "/") - val = "lagrange" + _ = os.WriteFile(configFilePath, c, 0o644) + } + } + } else { + err = yaml.Unmarshal(file, &result) + if err != nil { + dice.Logger.Infof("读取内置客户端配置失败,账号:%s, 原因: %s", conn.UserID, err.Error()) + return "", "" + } + if val, ok := result["account"].(map[string]interface{})["sign-servers"].([]interface{})[0].(map[string]interface{})["url"].(string); ok { + currentSignServerUrl = val + if w { + result["account"].(map[string]interface{})["sign-servers"].([]interface{})[0].(map[string]interface{})["url"] = signServerUrl + var c []byte + c, err = yaml.Marshal(&result) + if err != nil { + dice.Logger.Infof("SignServerUrl字段无法正常覆写,账号:%s, 原因: %s", conn.UserID, err.Error()) } - return val, version + _ = os.WriteFile(configFilePath, c, 0o644) } - err = errors.New("SignServerUrl字段无法正常读取") } } - dice.Logger.Infof("读取内置客户端配置失败,账号:%s, 原因: %s", conn.UserID, err.Error()) - return "", "" + if currentSignServerUrl == "" { + currentSignServerUrl = signServers["sealdice"] + "/25765" + } + var version string + for key, value := range signServers { + if strings.HasPrefix(currentSignServerUrl, value) { + version, _ = strings.CutPrefix(currentSignServerUrl, value) + version, _ = strings.CutPrefix(version, "/") + currentSignServerUrl = key + break + } + } + if _, exists := signServers[currentSignServerUrl]; !exists { + // 此处填写signServer最新版本号,修复前端部分由自定义地址切换至其他选项时无法自动选中sign最新版本 + version = "25765" + } + if version == "" { + // 此处填写sign版本号为空时默认版本号,修复前端部分由于signServerVersion丢失导致13107版本不会处于选中状态 + version = "13107" + } + return currentSignServerUrl, version } diff --git a/main.go b/main.go index 55c71738..f50fa45f 100644 --- a/main.go +++ b/main.go @@ -543,7 +543,7 @@ func diceServe(d *dice.Dice) { } if conn.EndPointInfoBase.ProtocolType == "onebot" { pa := conn.Adapter.(*dice.PlatformAdapterGocq) - if pa.BuiltinMode == "lagrange" { + if pa.BuiltinMode == "lagrange" || pa.BuiltinMode == "lagrange-gocq" { dice.LagrangeServe(d, conn, dice.LagrangeLoginInfo{ IsAsyncRun: true, })