From b7524a1a3286ab1673f6d3391104c928e0dee33b Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:22:32 +0800 Subject: [PATCH] Test4 (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Compiled main.go and pushed changes * test * 适配了频道私聊,用bolt数据库取代ini * 适配了nonebot2 * add license * add a lot * trss support * add action * add action * add action * fixbug * add wss * bugfix * fix action * fix action again * fa * fix * add a lot * add ws server token * bugifx * fixat * bugfix * bugfix * test * test2 * add url service * add url service * bugfix * fix * fix * fix * bug fix * fix * test * add webui * add image_compress * bug fix * fix cq code * fixbug * bugfix * bugfix * bugfix * bugfix * bugfix * bugfix * bugfix * add new feature * bugfix * bugfix * bugfix * bugfix * bugfix * merge * bugfix * bugfix * bugfix * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * bugfix * bugfix * Remove dist directory from tracking * test * test * bugfix2 --- botgo/token/authtoken.go | 130 +++++++++++++++++++++++++++++------ config/config.go | 39 +++++++++++ handlers/message_parser.go | 15 +++- main.go | 106 +++++++++++++++++----------- template/config_template.go | 3 + template/config_template.yml | 5 +- 6 files changed, 233 insertions(+), 65 deletions(-) diff --git a/botgo/token/authtoken.go b/botgo/token/authtoken.go index 5d254966..9740fff4 100644 --- a/botgo/token/authtoken.go +++ b/botgo/token/authtoken.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strconv" "sync" "time" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/tencent-connect/botgo/log" ) @@ -127,9 +128,10 @@ type queryTokenReq struct { ClientSecret string `json:"clientSecret"` } +// 自定义地址返回需满足这个格式 type queryTokenRsp struct { - AccessToken string `json:"access_token"` - ExpiresIn string `json:"expires_in"` + AccessToken string `json:"access_token"` + ExpiresIn interface{} `json:"expires_in"` // 允许任何类型 } // func queryAccessToken(ctx context.Context, tokenURL, appID, clientSecrent string) (AccessTokenInfo, error) { @@ -191,48 +193,134 @@ type queryTokenRsp struct { // return rdata, err // } -// queryAccessToken retrieves a new AccessToken. func queryAccessToken(ctx context.Context, tokenURL, appID, clientSecret string) (AccessTokenInfo, error) { + // 是否使用自定义ac地址 + configURL := config.GetDevelop_Acdir() if tokenURL == "" { - tokenURL = getAccessTokenURL // Assumes getAccessTokenURL is declared elsewhere + if configURL != "" { + tokenURL = configURL + } else { + tokenURL = getAccessTokenURL // 默认值 + } } - reqBody := queryTokenReq{ - AppID: appID, - ClientSecret: clientSecret, - } - reqData, err := json.Marshal(reqBody) - if err != nil { - return AccessTokenInfo{}, err - } + var req *http.Request + var err error - req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, bytes.NewReader(reqData)) - if err != nil { - return AccessTokenInfo{}, err + //自定义地址使用get,无需参数 + if configURL != "" { + req, err = http.NewRequestWithContext(ctx, http.MethodGet, configURL, nil) + } else { + // 默认ac地址使用post + reqBody := queryTokenReq{ + AppID: appID, + ClientSecret: clientSecret, + } + reqData, err := json.Marshal(reqBody) + if err != nil { + return AccessTokenInfo{}, err + } + + req, err = http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, bytes.NewReader(reqData)) + if err != nil { + return AccessTokenInfo{}, err + } + req.Header.Set("Content-Type", "application/json") } - req.Header.Add("Content-Type", "application/json") + // Perform the HTTP request. resp, err := http.DefaultClient.Do(req) if err != nil { return AccessTokenInfo{}, err } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + // Read the response body. + body, err := io.ReadAll(resp.Body) if err != nil { return AccessTokenInfo{}, err } var respData queryTokenRsp - if err := json.Unmarshal(body, &respData); err != nil { + if err = json.Unmarshal(body, &respData); err != nil { return AccessTokenInfo{}, err } - expiresIn, _ := strconv.ParseInt(respData.ExpiresIn, 10, 64) // Ignoring error can be dangerous, handle it as needed + // 自定义地址返回可能是string或者int + expiresInInt, err := parseExpiresIn(respData.ExpiresIn) + if err != nil { + return AccessTokenInfo{}, fmt.Errorf("expires_in is not a valid int or string: %v", err) + } return AccessTokenInfo{ Token: respData.AccessToken, - ExpiresIn: expiresIn, + ExpiresIn: expiresInInt, UpTime: time.Now(), }, nil } + +func parseExpiresIn(expiresIn interface{}) (int64, error) { + switch v := expiresIn.(type) { + case float64: + // JSON numbers come as floats by default + return int64(v), nil + case int64: + // In case the JSON decoder was set to use integers + return v, nil + case json.Number: + // Convert json.Number to int64 + return v.Int64() + case string: + // If the value is a string, try converting it to an integer + return strconv.ParseInt(v, 10, 64) + default: + // If none of the above types matched, return an error + return 0, fmt.Errorf("expires_in is not a valid type, got %T", expiresIn) + } +} + +//原函数 +// func queryAccessToken(ctx context.Context, tokenURL, appID, clientSecret string) (AccessTokenInfo, error) { +// if tokenURL == "" { +// tokenURL = getAccessTokenURL // Assumes getAccessTokenURL is declared elsewhere +// } + +// reqBody := queryTokenReq{ +// AppID: appID, +// ClientSecret: clientSecret, +// } +// reqData, err := json.Marshal(reqBody) +// if err != nil { +// return AccessTokenInfo{}, err +// } + +// req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, bytes.NewReader(reqData)) +// if err != nil { +// return AccessTokenInfo{}, err +// } +// req.Header.Add("Content-Type", "application/json") + +// resp, err := http.DefaultClient.Do(req) +// if err != nil { +// return AccessTokenInfo{}, err +// } +// defer resp.Body.Close() + +// body, err := ioutil.ReadAll(resp.Body) +// if err != nil { +// return AccessTokenInfo{}, err +// } + +// var respData queryTokenRsp +// if err := json.Unmarshal(body, &respData); err != nil { +// return AccessTokenInfo{}, err +// } + +// expiresIn, _ := strconv.ParseInt(respData.ExpiresIn, 10, 64) // Ignoring error can be dangerous, handle it as needed + +// return AccessTokenInfo{ +// Token: respData.AccessToken, +// ExpiresIn: expiresIn, +// UpTime: time.Now(), +// }, nil +// } diff --git a/config/config.go b/config/config.go index 752d262d..396dabf6 100644 --- a/config/config.go +++ b/config/config.go @@ -51,6 +51,9 @@ type Settings struct { ImageLimit int `yaml:"image_sizelimit"` RemovePrefix bool `yaml:"remove_prefix"` BackupPort string `yaml:"backup_port"` + DevlopAcDir string `yaml:"develop_access_token_dir"` + RemoveAt bool `yaml:"remove_at"` + DevBotid string `yaml:"develop_bot_id"` } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -348,6 +351,30 @@ func GetServer_dir() string { return instance.Settings.Server_dir } +// 获取DevBotid +func GetDevBotid() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get DevBotid.") + return "1234" + } + return instance.Settings.DevBotid +} + +// 获取Develop_Acdir服务的地址 +func GetDevelop_Acdir() string { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get DevlopAcDir.") + return "" + } + return instance.Settings.DevlopAcDir +} + // 获取lotus的值 func GetLotusValue() bool { mu.Lock() @@ -360,6 +387,18 @@ func GetLotusValue() bool { return instance.Settings.Lotus } +// 获取RemoveAt的值 +func GetRemoveAt() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to get RemoveAt value.") + return false + } + return instance.Settings.RemoveAt +} + // 获取port的值 func GetPortValue() string { mu.Lock() diff --git a/handlers/message_parser.go b/handlers/message_parser.go index ab5d8dd5..7aa36694 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -220,6 +220,7 @@ func RevertTransformedText(data interface{}) string { default: return "" } + //处理前 先去前后空 messageText := strings.TrimSpace(msg.Content) // 将messageText里的BotID替换成AppID @@ -232,9 +233,13 @@ func RevertTransformedText(data interface{}) string { submatches := re.FindStringSubmatch(m) if len(submatches) > 1 { userID := submatches[1] - // 检查是否是 BotID,如果是则直接返回,不进行映射 + // 检查是否是 BotID,如果是则直接返回,不进行映射,或根据用户需求移除 if userID == AppID { - return "[CQ:at,qq=" + AppID + "]" + if config.GetRemoveAt() { + return "" + } else { + return "[CQ:at,qq=" + AppID + "]" + } } // 不是 BotID,进行正常映射 @@ -271,6 +276,12 @@ func RevertTransformedText(data interface{}) string { } } + //如果移除了前部at,信息就会以空格开头,因为只移去了最前面的at,但at后紧跟随一个空格 + if config.GetRemoveAt() { + //再次去前后空 + messageText = strings.TrimSpace(messageText) + } + return messageText } diff --git a/main.go b/main.go index ec8ce30c..7bf5329e 100644 --- a/main.go +++ b/main.go @@ -120,19 +120,26 @@ func main() { log.Fatalln(err) } apiV2 = botgo.NewOpenAPI(token).WithTimeout(3 * time.Second) - - // 执行API请求 显示机器人信息 - me, err := api.Me(ctx) // Adjusted to pass only the context - if err != nil { - log.Printf("Error fetching bot details: %v\n", err) - //return - nologin = true + configURL := config.GetDevelop_Acdir() + var me *dto.User + if configURL == "" { // 执行API请求 显示机器人信息 + me, err = api.Me(ctx) // Adjusted to pass only the context + if err != nil { + log.Printf("Error fetching bot details: %v\n", err) + //return + nologin = true + } + log.Printf("Bot details: %+v\n", me) + } else { + log.Printf("自定义ac地址模式...请从日志手动获取bot的真实id并设置,不然at会不正常") } - log.Printf("Bot details: %+v\n", me) if !nologin { - //初始化handlers - handlers.BotID = me.ID - //handlers.BotID = "1234" + if configURL == "" { //初始化handlers + handlers.BotID = me.ID + } else { //初始化handlers + handlers.BotID = config.GetDevBotid() + } + handlers.AppID = fmt.Sprintf("%d", conf.Settings.AppID) // 获取 websocket 信息 这里用哪一个api获取就是用哪一个api去连接ws @@ -170,39 +177,46 @@ func main() { } }() - // 启动多个WebSocket客户端 - wsClientChan := make(chan *wsclient.WebSocketClient, len(conf.Settings.WsAddress)) - errorChan := make(chan error, len(conf.Settings.WsAddress)) - - for _, wsAddr := range conf.Settings.WsAddress { - go func(address string) { - wsClient, err := wsclient.NewWebSocketClient(address, conf.Settings.AppID, api, apiV2) - if err != nil { - log.Printf("Error creating WebSocketClient for address %s: %v\n", address, err) - errorChan <- err - return + // 启动多个WebSocket客户端的逻辑 + if !allEmpty(conf.Settings.WsAddress) { + wsClientChan := make(chan *wsclient.WebSocketClient, len(conf.Settings.WsAddress)) + errorChan := make(chan error, len(conf.Settings.WsAddress)) + // 定义计数器跟踪尝试建立的连接数 + attemptedConnections := 0 + for _, wsAddr := range conf.Settings.WsAddress { + if wsAddr == "" { + continue // Skip empty addresses + } + attemptedConnections++ // 增加尝试连接的计数 + go func(address string) { + wsClient, err := wsclient.NewWebSocketClient(address, conf.Settings.AppID, api, apiV2) + if err != nil { + log.Printf("Error creating WebSocketClient for address %s: %v\n", address, err) + errorChan <- err + return + } + wsClientChan <- wsClient + }(wsAddr) + } + // 获取连接成功后的wsClient + for i := 0; i < attemptedConnections; i++ { + select { + case wsClient := <-wsClientChan: + wsClients = append(wsClients, wsClient) + case err := <-errorChan: + log.Printf("Error encountered while initializing WebSocketClient: %v\n", err) } - wsClientChan <- wsClient - }(wsAddr) - } - - // 获取连接成功后的wsClient - for i := 0; i < len(conf.Settings.WsAddress); i++ { - select { - case wsClient := <-wsClientChan: - wsClients = append(wsClients, wsClient) - case err := <-errorChan: - log.Printf("Error encountered while initializing WebSocketClient: %v\n", err) } - } - // 确保所有wsClients都已初始化 - if len(wsClients) != len(conf.Settings.WsAddress) { - log.Println("Error: Not all wsClients are initialized!") - //log.Fatalln("Failed to initialize all WebSocketClients.") - } else { - log.Println("All wsClients are successfully initialized.") - p = Processor.NewProcessor(api, apiV2, &conf.Settings, wsClients) + // 确保所有尝试建立的连接都有对应的wsClient + if len(wsClients) != attemptedConnections { + log.Println("Error: Not all wsClients are initialized!") + // 处理初始化失败的情况 + } else { + log.Println("All wsClients are successfully initialized.") + // 所有客户端都成功初始化 + p = Processor.NewProcessor(api, apiV2, &conf.Settings, wsClients) + } } } else { // 设置颜色为红色 @@ -463,3 +477,13 @@ func getHandlerByName(handlerName string) (interface{}, bool) { return nil, false } } + +// allEmpty checks if all the strings in the slice are empty. +func allEmpty(addresses []string) bool { + for _, addr := range addresses { + if addr != "" { + return false + } + } + return true +} diff --git a/template/config_template.go b/template/config_template.go index 957afb34..d2897c8f 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -49,7 +49,10 @@ settings: server_user_password : "admin" #默认网页面板密码 image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 remove_prefix : false #是否忽略公域机器人指令前第一个/ + remove_at : false #是否忽略公域机器人指令前第一个[CQ:aq,qq=机器人] 场景(公域机器人,但插件未适配at开头) backup_port : "5200" #当totus为ture时,port值不再是本地webui的端口,使用lotus_Port来访问webui + develop_access_token_dir : "" #开发者测试环境access_token自定义获取地址 默认留空 请留空忽略 + develop_bot_id : "1234" #开发者环境需自行获取botid 填入 用户请不要设置这两行...开发者调试用 ` const Logo = ` ' diff --git a/template/config_template.yml b/template/config_template.yml index a6b54d41..9768bf1a 100644 --- a/template/config_template.yml +++ b/template/config_template.yml @@ -41,4 +41,7 @@ settings: server_user_password : "admin" #默认网页面板密码 image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 remove_prefix : false #是否忽略公域机器人指令前第一个/ - backup_port : "5200" #当totus为ture时,port值不再是本地webui的端口,使用lotus_Port来访问webui \ No newline at end of file + remove_at : false #是否忽略公域机器人指令前第一个[CQ:aq,qq=机器人] 场景(公域机器人,但插件未适配at开头) + backup_port : "5200" #当totus为ture时,port值不再是本地webui的端口,使用lotus_Port来访问webui + develop_access_token_dir : "" #开发者测试环境access_token自定义获取地址 默认留空 请留空忽略 + develop_bot_id : "1234" #开发者环境需自行获取botid 填入 用户请不要设置这两行...开发者调试用 \ No newline at end of file