Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 内置客户端支持切换及自定义签名服务器地址 #864

Merged
merged 10 commits into from
Jul 10, 2024
1 change: 1 addition & 0 deletions api/api_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ func Bind(e *echo.Echo, _myDice *dice.DiceManager) {
e.POST(prefix+"/im_connections/del", ImConnectionsDel)
e.POST(prefix+"/im_connections/set_enable", ImConnectionsSetEnable)
e.POST(prefix+"/im_connections/set_data", ImConnectionsSetData)
e.POST(prefix+"/im_connections/set_sign_server", ImConnectionsRWSignServerUrl)
e.POST(prefix+"/im_connections/gocqhttpRelogin", ImConnectionsGocqhttpRelogin)
e.POST(prefix+"/im_connections/walleQRelogin", ImConnectionsWalleQRelogin)
e.GET(prefix+"/im_connections/gocq_config_download.zip", ImConnectionsGocqConfigDownload)
Expand Down
52 changes: 45 additions & 7 deletions api/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,44 @@ func ImConnectionsSetData(c echo.Context) error {
return c.JSON(http.StatusNotFound, nil)
}

func ImConnectionsRWSignServerUrl(c echo.Context) error {
if !doAuth(c) {
return c.JSON(http.StatusForbidden, nil)
}
if dm.JustForTest {
return c.JSON(http.StatusOK, map[string]interface{}{
"testMode": true,
})
}

v := struct {
ID string `form:"id" json:"id"`
SignServerUrl string `form:"signServerUrl" json:"signServerUrl"`
W bool `form:"w" json:"w"`
}{}

err := c.Bind(&v)
if err != nil {
myDice.Save(false)
return c.JSON(http.StatusNotFound, nil)
}
for _, i := range myDice.ImSession.EndPoints {
if i.ID != v.ID {
continue
}
if i.ProtocolType == "onebot" {
pa := i.Adapter.(*dice.PlatformAdapterGocq)
if pa.BuiltinMode == "lagrange" {
signServerUrl := dice.RWLagrangeSignServerUrl(myDice, i, v.SignServerUrl, v.W)
if signServerUrl != "" {
return Success(&c, Response{"signServerUrl": signServerUrl})
}
}
}
}
return Error(&c, "读取signServerUrl字段失败", Response{})
}

func ImConnectionsDel(c echo.Context) error {
if !doAuth(c) {
return c.JSON(http.StatusForbidden, nil)
Expand Down Expand Up @@ -924,8 +962,8 @@ func ImConnectionsAddBuiltinLagrange(c echo.Context) error {
}

v := struct {
Account string `yaml:"account" json:"account"`
Protocol int `yaml:"protocol" json:"protocol"`
Account string `yaml:"account" json:"account"`
SignServerUrl string `yaml:"signServerUrl" json:"signServerUrl"`
}{}
err := c.Bind(&v)
if err == nil {
Expand All @@ -938,7 +976,7 @@ func ImConnectionsAddBuiltinLagrange(c echo.Context) error {
conn.UserID = dice.FormatDiceIDQQ(uid)
conn.Session = myDice.ImSession
pa := conn.Adapter.(*dice.PlatformAdapterGocq)
pa.InPackGoCqhttpProtocol = v.Protocol
// pa.InPackGoCqhttpProtocol = v.Protocol
pa.Session = myDice.ImSession

myDice.ImSession.EndPoints = append(myDice.ImSession.EndPoints, conn)
Expand All @@ -947,10 +985,10 @@ func ImConnectionsAddBuiltinLagrange(c echo.Context) error {
if err != nil {
return err
}
dice.LagrangeServe(myDice, conn, dice.GoCqhttpLoginInfo{
Protocol: v.Protocol,
UIN: uin,
IsAsyncRun: true,
dice.LagrangeServe(myDice, conn, dice.LagrangeLoginInfo{
UIN: uin,
SignServerUrl: v.SignServerUrl,
IsAsyncRun: true,
})
return c.JSON(http.StatusOK, v)
}
Expand Down
6 changes: 4 additions & 2 deletions dice/platform_adapter_gocq.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,13 +1183,15 @@ func (pa *PlatformAdapterGocq) DoRelogin() bool {
myDice.Logger.Infof("重新启动 lagrange 进程,对应账号: <%s>(%s)", ep.Nickname, ep.UserID)
pa.CurLoginIndex++
pa.GoCqhttpState = StateCodeInit
ep.Enable = false // 拉格朗进程杀死前应先禁用账号,否则拉格朗会自动重启(该行为在LagrangeServe中)
go BuiltinQQServeProcessKill(myDice, ep)
time.Sleep(10 * time.Second) // 上面那个清理有概率卡住,具体不懂,改成等5s -> 10s 超过一次重试间隔
LagrangeServeRemoveSession(myDice, ep) // 删除 keystore
pa.GoCqhttpLastRestrictedTime = 0 // 重置风控时间
ep.Enable = true
myDice.LastUpdatedTime = time.Now().Unix()
myDice.Save(false)
GoCqhttpServe(myDice, ep, GoCqhttpLoginInfo{
LagrangeServe(myDice, ep, LagrangeLoginInfo{
IsAsyncRun: true,
})
return true
Expand Down Expand Up @@ -1227,7 +1229,7 @@ func (pa *PlatformAdapterGocq) SetEnable(enable bool) {
if pa.BuiltinMode == "lagrange" {
BuiltinQQServeProcessKill(d, c)
time.Sleep(1 * time.Second)
LagrangeServe(d, c, GoCqhttpLoginInfo{
LagrangeServe(d, c, LagrangeLoginInfo{
IsAsyncRun: true,
})
} else {
Expand Down
5 changes: 1 addition & 4 deletions dice/platform_adapter_gocq_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ func BuiltinQQServeProcessKillBase(dice *Dice, conn *EndPointInfo, isSync bool)
pa.GoCqhttpQrcodeData = nil

if pa.BuiltinMode == "lagrange" {
workDir := LagrangeGetWorkDir(dice, conn)
workDir := lagrangeGetWorkDir(dice, conn)
qrcodeFile := filepath.Join(workDir, fmt.Sprintf("qr-%s.png", conn.UserID[3:]))
if _, err := os.Stat(qrcodeFile); err == nil {
// 如果已经存在二维码文件,将其删除
Expand Down Expand Up @@ -626,9 +626,6 @@ func GoCqhttpServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo)
pa.GoCqhttpState = StateCodeInLogin
builtinGoCqhttpServe(dice, conn, loginInfo)
}
if pa.BuiltinMode == "lagrange" {
LagrangeServe(dice, conn, loginInfo)
}
} else {
pa.GoCqhttpState = StateCodeLoginSuccessed
pa.GoCqhttpLoginSucceeded = true
Expand Down
80 changes: 71 additions & 9 deletions dice/platform_adapter_lagrange_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ import (
"sealdice-core/utils/procs"
)

func LagrangeGetWorkDir(dice *Dice, conn *EndPointInfo) string {
type LagrangeLoginInfo struct {
UIN int64
SignServerUrl string
IsAsyncRun bool
}

func lagrangeGetWorkDir(dice *Dice, conn *EndPointInfo) string {
workDir := filepath.Join(dice.BaseConfig.DataDir, conn.RelWorkDir)
return workDir
}
Expand All @@ -39,7 +45,7 @@ func NewLagrangeConnectInfoItem(account string) *EndPointInfo {
return conn
}

func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo) {
func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo LagrangeLoginInfo) {
pa := conn.Adapter.(*PlatformAdapterGocq)

pa.CurLoginIndex++
Expand All @@ -57,7 +63,7 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo)
return
}

workDir := LagrangeGetWorkDir(dice, conn)
workDir := lagrangeGetWorkDir(dice, conn)
_ = os.MkdirAll(workDir, 0o755)
wd, _ := os.Getwd()
exeFilePath, _ := filepath.Abs(filepath.Join(wd, "lagrange/Lagrange.OneBot"))
Expand All @@ -71,8 +77,14 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo)
if _, err := os.Stat(qrcodeFilePath); err == nil {
// 如果已经存在二维码文件,将其删除
_ = os.Remove(qrcodeFilePath)
log.Info("onebot: 删除已存在的二维码文件")
} else {
// 如果找不到二维码文件,有一种可能是用户添加账号时写错了账号,这里做个兼容让错误的账号依旧能获取到二维码
qrcodeFilePath = filepath.Join(workDir, fmt.Sprintf("qr-%s.png", conn.RelWorkDir[17:]))
if _, err := os.Stat(qrcodeFilePath); err == nil {
_ = os.Remove(qrcodeFilePath)
}
}
log.Info("onebot: 删除已存在的二维码文件")

// 创建配置文件
pa.ConnectURL = ""
Expand All @@ -87,7 +99,7 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo)
if pa.ConnectURL == "" {
p, _ := GetRandomFreePort()
pa.ConnectURL = fmt.Sprintf("ws://127.0.0.1:%d", p)
c := GenerateLagrangeConfig(p, conn)
c := GenerateLagrangeConfig(p, loginInfo.SignServerUrl, conn)
_ = os.WriteFile(configFilePath, []byte(c), 0o644)
}

Expand Down Expand Up @@ -154,6 +166,8 @@ func LagrangeServe(dice *Dice, conn *EndPointInfo, loginInfo GoCqhttpLoginInfo)
dice.Save(false)
isPrintLog = false

// 经测试,若不延时,登录成功的同一时刻进行ws正向连接有几率导致第一次连接失败
time.Sleep(1 * time.Second)
go ServeQQ(dice, conn)
}

Expand Down Expand Up @@ -324,10 +338,19 @@ var defaultLagrangeConfig = `

// 在构建时注入
var defaultNTSignServer = `https://lwxmagic.sealdice.com/api/sign`

func GenerateLagrangeConfig(port int, info *EndPointInfo) string {
var lagrangeNTSignServer = "https://sign.lagrangecore.org/api/sign"

func GenerateLagrangeConfig(port int, signServerUrl string, info *EndPointInfo) string {
switch signServerUrl {
case "":
signServerUrl = defaultNTSignServer
case "sealdice":
signServerUrl = defaultNTSignServer
case "lagrange":
signServerUrl = lagrangeNTSignServer
}
conf := strings.ReplaceAll(defaultLagrangeConfig, "{WS端口}", fmt.Sprintf("%d", port))
conf = strings.ReplaceAll(conf, "{NTSignServer地址}", defaultNTSignServer)
conf = strings.ReplaceAll(conf, "{NTSignServer地址}", signServerUrl)
conf = strings.ReplaceAll(conf, "{账号UIN}", info.UserID[3:])
return conf
}
Expand All @@ -341,11 +364,50 @@ func LagrangeServeRemoveSession(dice *Dice, conn *EndPointInfo) {

// 清理内置客户端配置文件目录
func LagrangeServeRemoveConfig(dice *Dice, conn *EndPointInfo) {
workDir := LagrangeGetWorkDir(dice, conn)
workDir := lagrangeGetWorkDir(dice, conn)
err := os.RemoveAll(workDir)
if err != nil {
dice.Logger.Errorf("清理内置客户端文件失败, 原因: %s, 请手动删除目录: %s", err.Error(), workDir)
} else {
dice.Logger.Infof("已自动清理内置客户端目录: %s", workDir)
}
}

func RWLagrangeSignServerUrl(dice *Dice, conn *EndPointInfo, signServerUrl string, w bool) string {
switch signServerUrl {
case "sealdice":
signServerUrl = defaultNTSignServer
case "lagrange":
signServerUrl = "https://sign.lagrangecore.org/api/sign"
}
workDir := lagrangeGetWorkDir(dice, conn)
configFilePath := filepath.Join(workDir, "appsettings.json")
file, err := os.ReadFile(configFilePath)
if err == nil {
var result map[string]interface{}
err = json.Unmarshal(file, &result)
if err == nil {
if val, ok := result["SignServerUrl"].(string); ok {
if w {
result["SignServerUrl"] = signServerUrl
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())
}
}
switch val {
case defaultNTSignServer:
val = "sealdice"
case lagrangeNTSignServer:
val = "lagrange"
}
return val
}
err = errors.New("SignServerUrl字段无法正常读取")
}
}
dice.Logger.Infof("读取内置客户端配置失败,账号:%s, 原因: %s", conn.UserID, err.Error())
return ""
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ func diceServe(d *dice.Dice) {
if conn.EndPointInfoBase.ProtocolType == "onebot" {
pa := conn.Adapter.(*dice.PlatformAdapterGocq)
if pa.BuiltinMode == "lagrange" {
dice.LagrangeServe(d, conn, dice.GoCqhttpLoginInfo{
dice.LagrangeServe(d, conn, dice.LagrangeLoginInfo{
IsAsyncRun: true,
})
return
Expand Down
Loading