From abfc600918213ffe1d17d6fb6ab4a627a828f113 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:48:31 +0800 Subject: [PATCH] Test4 (#21) 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 --- Processor/ProcessC2CMessage.go | 40 +++++++--- Processor/ProcessChannelDirectMessage.go | 58 +++++++++++---- Processor/ProcessGroupMessage.go | 21 ++++-- Processor/ProcessGuildATMessage.go | 43 ++++++++--- Processor/ProcessGuildNormalMessage.go | 43 ++++++++--- Processor/Processor.go | 22 ++++-- callapi/callapi.go | 4 +- config/config.go | 44 ++++++++--- config_template.go | 19 +++-- config_template.yml | 5 +- handlers/get_status.go | 78 ++++++++++++++++++++ handlers/message_parser.go | 78 +++++++++++++++++++- handlers/send_group_msg.go | 14 +++- handlers/send_guild_channel_msg.go | 18 +++-- handlers/send_msg.go | 23 ++++-- handlers/send_private_msg.go | 15 +++- main.go | 94 ++++++++++++------------ server/uploadpic.go | 2 +- server/wsserver.go | 89 ++++++++++++++++++++++ wsclient/ws.go | 58 ++++++++++----- 20 files changed, 604 insertions(+), 164 deletions(-) create mode 100644 handlers/get_status.go create mode 100644 server/wsserver.go diff --git a/Processor/ProcessC2CMessage.go b/Processor/ProcessC2CMessage.go index d5fcb301..50f097e3 100644 --- a/Processor/ProcessC2CMessage.go +++ b/Processor/ProcessC2CMessage.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/config" @@ -16,7 +17,7 @@ import ( ) // ProcessC2CMessage 处理C2C消息 群私聊 -func (p *Processor) ProcessC2CMessage(data *dto.WSC2CMessageData) error { +func (p *Processors) ProcessC2CMessage(data *dto.WSC2CMessageData) error { // 打印data结构体 PrintStructWithFieldNames(data) @@ -89,15 +90,26 @@ func (p *Processor) ProcessC2CMessage(data *dto.WSC2CMessageData) error { // Convert OnebotGroupMessage to map and send privateMsgMap := structToMap(privateMsg) - err = p.Wsclient.SendMessage(privateMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(privateMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending private message via wsclient: %v", err)) + } + } + + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) } } else { //将私聊信息转化为群信息(特殊需求情况下) //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -165,11 +177,21 @@ func (p *Processor) ProcessC2CMessage(data *dto.WSC2CMessageData) error { // Convert OnebotGroupMessage to map and send groupMsgMap := structToMap(groupMsg) - err = p.Wsclient.SendMessage(groupMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(groupMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending group message via wsclient: %v", err)) + } } - } + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + log.Println("Encountered errors while sending to wsclients:", strings.Join(errors, "; ")) + } + } return nil } diff --git a/Processor/ProcessChannelDirectMessage.go b/Processor/ProcessChannelDirectMessage.go index ebf08932..52b31940 100644 --- a/Processor/ProcessChannelDirectMessage.go +++ b/Processor/ProcessChannelDirectMessage.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/config" @@ -17,7 +18,7 @@ import ( ) // ProcessChannelDirectMessage 处理频道私信消息 这里我们是被动收到 -func (p *Processor) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) error { +func (p *Processors) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) error { // 打印data结构体 //PrintStructWithFieldNames(data) @@ -93,9 +94,20 @@ func (p *Processor) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) e // Convert OnebotGroupMessage to map and send privateMsgMap := structToMap(privateMsg) - err = p.Wsclient.SendMessage(privateMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(privateMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending private message via wsclient: %v", err)) + } + } + + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) } } else { if !p.Settings.GlobalChannelToGroup { @@ -109,7 +121,7 @@ func (p *Processor) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) e //获取s s := client.GetGlobalS() //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -175,18 +187,27 @@ func (p *Processor) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) e // 将 onebotMsg 结构体转换为 map[string]interface{} msgMap := structToMap(onebotMsg) + var errors []string - // 使用 wsclient 发送消息 - err = p.Wsclient.SendMessage(msgMap) - if err != nil { - return fmt.Errorf("error sending message via wsclient: %v", err) + for _, client := range p.Wsclient { + err = client.SendMessage(msgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending message via wsclient: %v", err)) + } + } + + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) } } else { //将频道信息转化为群信息(特殊需求情况下) //将channelid写入ini,可取出guild_id idmap.WriteConfigv2(data.ChannelID, "guild_id", data.GuildID) //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -266,9 +287,20 @@ func (p *Processor) ProcessChannelDirectMessage(data *dto.WSDirectMessageData) e // Convert OnebotGroupMessage to map and send groupMsgMap := structToMap(groupMsg) - err = p.Wsclient.SendMessage(groupMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(groupMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending group message via wsclient: %v", err)) + } + } + + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) } } diff --git a/Processor/ProcessGroupMessage.go b/Processor/ProcessGroupMessage.go index c414ef35..bb057694 100644 --- a/Processor/ProcessGroupMessage.go +++ b/Processor/ProcessGroupMessage.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/config" @@ -17,14 +18,14 @@ import ( ) // ProcessGroupMessage 处理群组消息 -func (p *Processor) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { +func (p *Processors) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { // 获取s s := client.GetGlobalS() idmap.WriteConfigv2(data.ChannelID, "guild_id", data.GuildID) // 转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) // 转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) @@ -107,10 +108,20 @@ func (p *Processor) ProcessGroupMessage(data *dto.WSGroupATMessageData) error { // Convert OnebotGroupMessage to map and send groupMsgMap := structToMap(groupMsg) - err = p.Wsclient.SendMessage(groupMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(groupMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending group message via wsclient: %v", err)) + } } + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) + } return nil } diff --git a/Processor/ProcessGuildATMessage.go b/Processor/ProcessGuildATMessage.go index 80e40c35..ea4e5998 100644 --- a/Processor/ProcessGuildATMessage.go +++ b/Processor/ProcessGuildATMessage.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/config" @@ -17,7 +18,7 @@ import ( ) // ProcessGuildATMessage 处理消息,执行逻辑并可能使用 api 发送响应 -func (p *Processor) ProcessGuildATMessage(data *dto.WSATMessageData) error { +func (p *Processors) ProcessGuildATMessage(data *dto.WSATMessageData) error { if !p.Settings.GlobalChannelToGroup { // 将时间字符串转换为时间戳 t, err := time.Parse(time.RFC3339, string(data.Timestamp)) @@ -27,7 +28,7 @@ func (p *Processor) ProcessGuildATMessage(data *dto.WSATMessageData) error { //获取s s := client.GetGlobalS() //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -98,12 +99,21 @@ func (p *Processor) ProcessGuildATMessage(data *dto.WSATMessageData) error { // 将 onebotMsg 结构体转换为 map[string]interface{} msgMap := structToMap(onebotMsg) - // 使用 wsclient 发送消息 - err = p.Wsclient.SendMessage(msgMap) - if err != nil { - return fmt.Errorf("error sending message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(msgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending message via wsclient: %v", err)) + } } + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) + } } else { // GlobalChannelToGroup为true时的处理逻辑 //将频道转化为一个群 @@ -111,8 +121,8 @@ func (p *Processor) ProcessGuildATMessage(data *dto.WSATMessageData) error { s := client.GetGlobalS() //将channelid写入ini,可取出guild_id todo 比ini更好的储存方式 idmap.WriteConfigv2(data.ChannelID, "guild_id", data.GuildID) - //转换at - messageText := handlers.RevertTransformedText(data.Content) + //转换at和图片 + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -192,9 +202,20 @@ func (p *Processor) ProcessGuildATMessage(data *dto.WSATMessageData) error { // Convert OnebotGroupMessage to map and send groupMsgMap := structToMap(groupMsg) - err = p.Wsclient.SendMessage(groupMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + var errors []string + + for _, client := range p.Wsclient { + err = client.SendMessage(groupMsgMap) + if err != nil { + // 记录错误信息,但不立即返回 + errors = append(errors, fmt.Sprintf("error sending group message via wsclient: %v", err)) + } + } + + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) } } diff --git a/Processor/ProcessGuildNormalMessage.go b/Processor/ProcessGuildNormalMessage.go index 011d96e9..5eeb84a2 100644 --- a/Processor/ProcessGuildNormalMessage.go +++ b/Processor/ProcessGuildNormalMessage.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "time" "github.com/hoshinonyaruko/gensokyo/config" @@ -16,7 +17,7 @@ import ( ) // ProcessGuildNormalMessage 处理频道常规消息 -func (p *Processor) ProcessGuildNormalMessage(data *dto.WSMessageData) error { +func (p *Processors) ProcessGuildNormalMessage(data *dto.WSMessageData) error { if !p.Settings.GlobalChannelToGroup { // 将时间字符串转换为时间戳 t, err := time.Parse(time.RFC3339, string(data.Timestamp)) @@ -26,7 +27,7 @@ func (p *Processor) ProcessGuildNormalMessage(data *dto.WSMessageData) error { //获取s s := client.GetGlobalS() //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -97,12 +98,22 @@ func (p *Processor) ProcessGuildNormalMessage(data *dto.WSMessageData) error { // 将 onebotMsg 结构体转换为 map[string]interface{} msgMap := structToMap(onebotMsg) - // 使用 wsclient 发送消息 - err = p.Wsclient.SendMessage(msgMap) - if err != nil { - return fmt.Errorf("error sending message via wsclient: %v", err) + var errors []string + + // 遍历每一个 wsclient 并发送消息 + for _, client := range p.Wsclient { + err = client.SendMessage(msgMap) + if err != nil { + // 记录错误但不立即返回 + errors = append(errors, fmt.Sprintf("error sending message via wsclient: %v", err)) + } } + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join来合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) + } } else { // GlobalChannelToGroup为true时的处理逻辑 //将频道转化为一个群 @@ -111,7 +122,7 @@ func (p *Processor) ProcessGuildNormalMessage(data *dto.WSMessageData) error { //将channelid写入ini,可取出guild_id todo 比ini更好的储存方式 idmap.WriteConfigv2(data.ChannelID, "guild_id", data.GuildID) //转换at - messageText := handlers.RevertTransformedText(data.Content) + messageText := handlers.RevertTransformedText(data) //转换appid AppIDString := strconv.FormatUint(p.Settings.AppID, 10) //构造echo @@ -191,11 +202,23 @@ func (p *Processor) ProcessGuildNormalMessage(data *dto.WSMessageData) error { // Convert OnebotGroupMessage to map and send groupMsgMap := structToMap(groupMsg) - err = p.Wsclient.SendMessage(groupMsgMap) - if err != nil { - return fmt.Errorf("error sending group message via wsclient: %v", err) + + var errors []string + + // 遍历每一个 wsclient 并发送消息 + for _, client := range p.Wsclient { + err = client.SendMessage(groupMsgMap) + if err != nil { + // 记录错误但不立即返回 + errors = append(errors, fmt.Sprintf("error sending group message via wsclient: %v", err)) + } } + // 在循环结束后处理记录的错误 + if len(errors) > 0 { + // 使用strings.Join来合并所有的错误信息 + return fmt.Errorf(strings.Join(errors, "; ")) + } } return nil diff --git a/Processor/Processor.go b/Processor/Processor.go index ceada563..b0b91915 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -15,11 +15,11 @@ import ( ) // Processor 结构体用于处理消息 -type Processor struct { - Api openapi.OpenAPI // API 类型 - Apiv2 openapi.OpenAPI //群的API - Settings *config.Settings // 使用指针 - Wsclient *wsclient.WebSocketClient // 使用指针 +type Processors struct { + Api openapi.OpenAPI // API 类型 + Apiv2 openapi.OpenAPI //群的API + Settings *config.Settings // 使用指针 + Wsclient []*wsclient.WebSocketClient // 指针的切片 } type Sender struct { @@ -95,7 +95,7 @@ func FoxTimestamp() int64 { } // ProcessInlineSearch 处理内联查询 -func (p *Processor) ProcessInlineSearch(data *dto.WSInteractionData) error { +func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { //ctx := context.Background() // 或从更高级别传递一个上下文 // 在这里处理内联查询 @@ -173,3 +173,13 @@ func structToMap(obj interface{}) map[string]interface{} { json.Unmarshal(j, &out) return out } + +// 修改函数的返回类型为 *Processor +func NewProcessor(api openapi.OpenAPI, apiv2 openapi.OpenAPI, settings *config.Settings, wsclient []*wsclient.WebSocketClient) *Processors { + return &Processors{ + Api: api, + Apiv2: apiv2, + Settings: settings, + Wsclient: wsclient, + } +} diff --git a/callapi/callapi.go b/callapi/callapi.go index 51116fd4..ec9221f1 100644 --- a/callapi/callapi.go +++ b/callapi/callapi.go @@ -111,11 +111,9 @@ type Message struct { Echo interface{} `json:"echo,omitempty"` } -// 这是一个接口,在wsclient传入client但不需要引用wsclient包,避免循环引用 +// 这是一个接口,在wsclient传入client但不需要引用wsclient包,避免循环引用,复用wsserver和client逻辑 type Client interface { SendMessage(message map[string]interface{}) error - GetAppID() uint64 - GetAppIDStr() string } // 根据action订阅handler处理api diff --git a/config/config.go b/config/config.go index edbc57da..2e27f98c 100644 --- a/config/config.go +++ b/config/config.go @@ -3,6 +3,7 @@ package config import ( + "fmt" "log" "os" "sync" @@ -21,7 +22,7 @@ type Config struct { } type Settings struct { - WsAddress string `yaml:"ws_address"` + WsAddress []string `yaml:"ws_address"` AppID uint64 `yaml:"app_id"` Token string `yaml:"token"` ClientSecret string `yaml:"client_secret"` @@ -32,12 +33,9 @@ type Settings struct { Server_dir string `yaml:"server_dir"` Lotus bool `yaml:"lotus"` Port string `yaml:"port"` - - // 连接wss时使用,不是wss可留空 - WsToken string `yaml:"ws_token,omitempty"` - - // 如果需要在群权限判断是管理员是,将user_id填入这里,master_id是一个文本数组 - MasterID []string `yaml:"master_id,omitempty"` + WsToken []string `yaml:"ws_token,omitempty"` // 连接wss时使用,不是wss可留空 一一对应 + MasterID []string `yaml:"master_id,omitempty"` // 如果需要在群权限判断是管理员是,将user_id填入这里,master_id是一个文本数组 + EnableWsServer bool `yaml:"enable_ws_server,omitempty"` //正向ws开关 } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -62,14 +60,14 @@ func LoadConfig(path string) (*Config, error) { return conf, nil } -// 获取ws地址 -func GetWsAddress() string { +// 获取ws地址数组 +func GetWsAddress() []string { mu.Lock() defer mu.Unlock() if instance != nil { return instance.Settings.WsAddress } - return "" + return nil // 返回nil,如果instance为nil } // 获取gensokyo服务的地址 @@ -130,14 +128,24 @@ func GetAppID() uint64 { return 0 // or whatever default value you'd like to return if instance is nil } +// 获取AppID String +func GetAppIDStr() string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return fmt.Sprintf("%d", instance.Settings.AppID) + } + return "0" +} + // 获取WsToken -func GetWsToken() string { +func GetWsToken() []string { mu.Lock() defer mu.Unlock() if instance != nil { return instance.Settings.WsToken } - return "" // 返回空字符串,如果instance为nil + return nil // 返回nil,如果instance为nil } // 获取MasterID数组 @@ -149,3 +157,15 @@ func GetMasterID() []string { } return nil // 返回nil,如果instance为nil } + +// 获取port的值 +func GetEnableWsServer() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + log.Println("Warning: instance is nil when trying to get port value.") + return false + } + return instance.Settings.EnableWsServer +} diff --git a/config_template.go b/config_template.go index 96238c8a..6200655e 100644 --- a/config_template.go +++ b/config_template.go @@ -3,7 +3,7 @@ package main const configTemplate = ` version: 1 settings: - ws_address: "ws://:" # WebSocket服务的地址 + ws_address: ["ws://:"] # WebSocket服务的地址 支持多个["","",""] app_id: # 你的应用ID token: "" # 你的应用令牌 client_secret: "" # 你的客户端密钥 @@ -16,21 +16,28 @@ settings: # - "GuildEventHandler" # 频道事件 # - "MemberEventHandler" # 频道成员新增 # - "ChannelEventHandler" # 频道事件 - # - "CreateMessageHandler" # 频道不at信息 + # - "CreateMessageHandler" # 频道不at信息 私域机器人需要开启 公域机器人开启会连接失败 # - "InteractionHandler" # 添加频道互动回应 # - "GroupATMessageEventHandler" # 群at信息 仅频道机器人时候需要注释 # - "C2CMessageEventHandler" # 群私聊 仅频道机器人时候需要注释 # - "ThreadEventHandler" # 发帖事件 (当前版本已禁用) - global_channel_to_group: false # 是否将频道转换成群 - global_private_to_channel: false # 是否将私聊转换成频道 + + global_channel_to_group: true # 是否将频道转换成群 默认true + global_private_to_channel: false # 是否将私聊转换成频道 如果是群场景 会将私聊转为群(方便提审\测试) array: false + server_dir: "" # 提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 - port: "" # idmaps和图床对外开放的端口号 + port: "" # idmaps和图床对外开放的端口号 + + lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。 # 使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。 # 如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port - ws_token: "" #连接wss地址时服务器所需的token,如果是ws,可留空 + + + ws_token: ["","",""] #连接wss地址时服务器所需的token,如果是ws,可留空,按顺序一一对应 master_id : ["1","2"] #群场景尚未开放获取管理员和列表能力,手动从日志中获取需要设置为管理,的user_id并填入(适用插件有权限判断场景) + enable_ws_server: true #是否启用正向ws服务器 监听server_dir:port/ws ` diff --git a/config_template.yml b/config_template.yml index d06b677d..6ea4cb5c 100644 --- a/config_template.yml +++ b/config_template.yml @@ -28,5 +28,6 @@ settings: lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。 # 使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。 # 如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port - ws_token: "" #连接wss地址时服务器所需的token,如果是ws,可留空 - master_id : ["1","2"] #群场景尚未开放获取管理员和列表能力,手动从日志中获取需要设置为管理,的user_id并填入(适用插件有权限判断场景) \ No newline at end of file + ws_token: ["",""] #连接wss地址时服务器所需的token,如果是ws,可留空,按顺序一一对应 + master_id : ["1","2"] #群场景尚未开放获取管理员和列表能力,手动从日志中获取需要设置为管理,的user_id并填入(适用插件有权限判断场景) + enable_ws_server: true #是否启用正向ws服务器 监听server_dir:port/ws \ No newline at end of file diff --git a/handlers/get_status.go b/handlers/get_status.go new file mode 100644 index 00000000..fc74ace7 --- /dev/null +++ b/handlers/get_status.go @@ -0,0 +1,78 @@ +package handlers + +import ( + "log" + + "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/tencent-connect/botgo/openapi" +) + +type GetStatusResponse struct { + Data StatusData `json:"data"` + Message string `json:"message"` + RetCode int `json:"retcode"` + Status string `json:"status"` + Echo interface{} `json:"echo"` +} + +type StatusData struct { + AppInitialized bool `json:"app_initialized"` + AppEnabled bool `json:"app_enabled"` + PluginsGood bool `json:"plugins_good"` + AppGood bool `json:"app_good"` + Online bool `json:"online"` + Good bool `json:"good"` + Stat Statistics `json:"stat"` +} + +type Statistics struct { + PacketReceived uint64 `json:"packet_received"` + PacketSent uint64 `json:"packet_sent"` + PacketLost uint32 `json:"packet_lost"` + MessageReceived uint64 `json:"message_received"` + MessageSent uint64 `json:"message_sent"` + DisconnectTimes uint32 `json:"disconnect_times"` + LostTimes uint32 `json:"lost_times"` + LastMessageTime int64 `json:"last_message_time"` +} + +func init() { + callapi.RegisterHandler("get_status", getStatus) +} + +func getStatus(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.OpenAPI, message callapi.ActionMessage) { + + var response GetStatusResponse + + response.Data = StatusData{ + AppInitialized: true, + AppEnabled: true, + PluginsGood: true, + AppGood: true, + Online: true, //测试数据 + Good: true, //测试数据 + Stat: Statistics{ + PacketReceived: 1000, //测试数据 + PacketSent: 950, //测试数据 + PacketLost: 50, //测试数据 + MessageReceived: 500, //测试数据 + MessageSent: 490, //测试数据 + DisconnectTimes: 5, //测试数据 + LostTimes: 2, //测试数据 + LastMessageTime: 1677721600, //测试数据 + }, + } + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + response.Echo = string(message.Echo) // Directly assign the string value + + outputMap := structToMap(response) + + log.Printf("get_status: %+v\n", outputMap) + + err := client.SendMessage(outputMap) + if err != nil { + log.Printf("Error sending message via client: %v", err) + } +} diff --git a/handlers/message_parser.go b/handlers/message_parser.go index 3c98a62d..c2368bf3 100644 --- a/handlers/message_parser.go +++ b/handlers/message_parser.go @@ -3,6 +3,7 @@ package handlers import ( "fmt" "log" + "path/filepath" "regexp" "strings" @@ -13,6 +14,48 @@ import ( var BotID string var AppID string +// 定义响应结构体 +type ServerResponse struct { + Data struct { + MessageID int `json:"message_id"` + } `json:"data"` + Message string `json:"message"` + RetCode int `json:"retcode"` + Status string `json:"status"` + Echo string `json:"echo"` +} + +// 发送成功回执 todo 返回可互转的messageid +func SendResponse(client callapi.Client, err error, message *callapi.ActionMessage) error { + // 设置响应值 + response := ServerResponse{} + response.Data.MessageID = 0 // todo 实现messageid转换 + response.Echo = string(message.Echo) + if err != nil { + response.Message = err.Error() // 可选:在响应中添加错误消息 + //response.RetCode = -1 // 可以是任何非零值,表示出错 + //response.Status = "failed" + response.RetCode = 0 //官方api审核异步的 审核中默认返回失败,但其实信息发送成功了 + response.Status = "ok" + } else { + response.Message = "" + response.RetCode = 0 + response.Status = "ok" + } + + // 转化为map并发送 + outputMap := structToMap(response) + + sendErr := client.SendMessage(outputMap) + if sendErr != nil { + log.Printf("Error sending message via client: %v", sendErr) + return sendErr + } + + log.Printf("发送成功回执: %+v", outputMap) + return nil +} + func parseMessageContent(paramsMessage callapi.ParamsContent) (string, map[string][]string) { messageText := "" @@ -118,9 +161,23 @@ func transformMessageText(messageText string) string { } // 处理at和其他定形文到onebotv11格式(cq码) -func RevertTransformedText(messageText string) string { - // Trim leading and trailing spaces - messageText = strings.TrimSpace(messageText) +func RevertTransformedText(data interface{}) string { + var msg *dto.Message + switch v := data.(type) { + case *dto.WSGroupATMessageData: + msg = (*dto.Message)(v) + case *dto.WSATMessageData: + msg = (*dto.Message)(v) + case *dto.WSMessageData: + msg = (*dto.Message)(v) + case *dto.WSDirectMessageData: + msg = (*dto.Message)(v) + case *dto.WSC2CMessageData: + msg = (*dto.Message)(v) + default: + return "" + } + messageText := strings.TrimSpace(msg.Content) // 将messageText里的BotID替换成AppID messageText = strings.ReplaceAll(messageText, BotID, AppID) @@ -128,13 +185,26 @@ func RevertTransformedText(messageText string) string { // 使用正则表达式来查找所有<@!数字>的模式 re := regexp.MustCompile(`<@!(\d+)>`) // 使用正则表达式来替换找到的模式为[CQ:at,qq=数字] - return re.ReplaceAllStringFunc(messageText, func(m string) string { + messageText = re.ReplaceAllStringFunc(messageText, func(m string) string { submatches := re.FindStringSubmatch(m) if len(submatches) > 1 { return "[CQ:at,qq=" + submatches[1] + "]" } return m }) + + // 处理图片附件 + for _, attachment := range msg.Attachments { + if strings.HasPrefix(attachment.ContentType, "image/") { + // 获取文件的后缀名 + ext := filepath.Ext(attachment.FileName) + md5name := strings.TrimSuffix(attachment.FileName, ext) + imageCQ := "[CQ:image,file=" + md5name + ".image,subType=0,url=" + attachment.URL + "]" + messageText += imageCQ + } + } + + return messageText } // 将收到的data.content转换为message segment todo,群场景不支持受图片,频道场景的图片可以拼一下 diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 76091c21..a548c89b 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/server" @@ -27,12 +28,12 @@ func handleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap //如果获取不到 就用user_id获取信息类型 if msgType == "" { - msgType = GetMessageTypeByUserid(client.GetAppIDStr(), message.Params.UserID) + msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } //如果获取不到 就用group_id获取信息类型 if msgType == "" { - msgType = GetMessageTypeByGroupid(client.GetAppIDStr(), message.Params.GroupID) + msgType = GetMessageTypeByGroupid(config.GetAppIDStr(), message.Params.GroupID) } switch msgType { @@ -50,7 +51,7 @@ func handleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap log.Println("foundItems:", foundItems) // 如果messageID为空,通过函数获取 if messageID == "" { - messageID = GetMessageIDByUseridOrGroupid(client.GetAppIDStr(), message.Params.GroupID) + messageID = GetMessageIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.GroupID) log.Println("通过GetMessageIDByUserid函数获取的message_id:", messageID) } @@ -74,10 +75,13 @@ func handleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - _, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + //重新为err赋值 + _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { log.Printf("发送文本群组信息失败: %v", err) } + //发送成功回执 + SendResponse(client, err, &message) } // 遍历foundItems并发送每种信息 @@ -98,6 +102,8 @@ func handleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap if err != nil { log.Printf("发送 %s 信息失败_send_group_msg: %v", key, err) } + //发送成功回执 + SendResponse(client, err, &message) } case "guild": //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go index 71e47fe4..892aed6c 100644 --- a/handlers/send_guild_channel_msg.go +++ b/handlers/send_guild_channel_msg.go @@ -8,6 +8,7 @@ import ( "os" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" @@ -25,12 +26,12 @@ func handleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 //如果获取不到 就用user_id获取信息类型 if msgType == "" { - msgType = GetMessageTypeByUserid(client.GetAppIDStr(), message.Params.UserID) + msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } //如果获取不到 就用group_id获取信息类型 if msgType == "" { - appID := client.GetAppIDStr() + appID := config.GetAppIDStr() groupID := message.Params.GroupID fmt.Printf("appID: %s, GroupID: %v\n", appID, groupID) @@ -53,12 +54,15 @@ func handleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 log.Println("频道发信息对应的message_id:", messageID) log.Println("频道发信息messageText:", messageText) log.Println("foundItems:", foundItems) + var err error // 优先发送文本信息 if messageText != "" { textMsg, _ := generateReplyMessage(messageID, nil, messageText) - if _, err := api.PostMessage(context.TODO(), channelID, textMsg); err != nil { + if _, err = api.PostMessage(context.TODO(), channelID, textMsg); err != nil { log.Printf("发送文本信息失败: %v", err) } + //发送成功回执 + SendResponse(client, err, &message) } // 遍历foundItems并发送每种信息 @@ -80,13 +84,17 @@ func handleSendGuildChannelMsg(client callapi.Client, api openapi.OpenAPI, apiv2 reply.Content = "" // 使用Multipart方法发送 - if _, err := api.PostMessageMultipart(context.TODO(), channelID, reply, fileImageData); err != nil { + if _, err = api.PostMessageMultipart(context.TODO(), channelID, reply, fileImageData); err != nil { log.Printf("使用multipart发送 %s 信息失败: %v message_id %v", key, err, messageID) } + //发送成功回执 + SendResponse(client, err, &message) } else { - if _, err := api.PostMessage(context.TODO(), channelID, reply); err != nil { + if _, err = api.PostMessage(context.TODO(), channelID, reply); err != nil { log.Printf("发送 %s 信息失败: %v", key, err) } + //发送成功回执 + SendResponse(client, err, &message) } } diff --git a/handlers/send_msg.go b/handlers/send_msg.go index e20753d4..844ac1d0 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/hoshinonyaruko/gensokyo/server" @@ -27,7 +28,7 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope //如果获取不到 就用group_id获取信息类型 if msgType == "" { - appID := client.GetAppIDStr() + appID := config.GetAppIDStr() groupID := message.Params.GroupID fmt.Printf("appID: %s, GroupID: %v\n", appID, groupID) @@ -37,7 +38,7 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope //如果获取不到 就用user_id获取信息类型 if msgType == "" { - msgType = GetMessageTypeByUserid(client.GetAppIDStr(), message.Params.UserID) + msgType = GetMessageTypeByUserid(config.GetAppIDStr(), message.Params.UserID) } switch msgType { @@ -55,7 +56,7 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope log.Println("foundItems:", foundItems) // 如果messageID为空,通过函数获取 if messageID == "" { - messageID = GetMessageIDByUseridOrGroupid(client.GetAppIDStr(), message.Params.GroupID) + messageID = GetMessageIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.GroupID) log.Println("通过GetMessageIDByUserid函数获取的message_id:", messageID) } @@ -79,10 +80,12 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - _, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) + _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { log.Printf("发送文本群组信息失败: %v", err) } + //发送成功回执 + SendResponse(client, err, &message) } // 遍历foundItems并发送每种信息 @@ -100,10 +103,12 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope } fmt.Printf("richMediaMessage: %+v\n", richMediaMessage) - _, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), richMediaMessage) + _, err = apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), richMediaMessage) if err != nil { log.Printf("发送 %s 信息失败_send_msg: %v", key, err) } + //发送成功回执 + SendResponse(client, err, &message) } case "guild": //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 @@ -175,10 +180,12 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope } groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - _, err := apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) + _, err = apiv2.PostC2CMessage(context.TODO(), UserID, groupMessage) if err != nil { log.Printf("发送文本私聊信息失败: %v", err) } + //发送成功回执 + SendResponse(client, err, &message) } // 遍历 foundItems 并发送每种信息 @@ -194,10 +201,12 @@ func handleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope log.Printf("Error: Expected RichMediaMessage type for key %s.", key) continue } - _, err := apiv2.PostC2CMessage(context.TODO(), UserID, richMediaMessage) + _, err = apiv2.PostC2CMessage(context.TODO(), UserID, richMediaMessage) if err != nil { log.Printf("发送 %s 私聊信息失败: %v", key, err) } + //发送成功回执 + SendResponse(client, err, &message) } default: log.Printf("1Unknown message type: %s", msgType) diff --git a/handlers/send_private_msg.go b/handlers/send_private_msg.go index 68423f61..6798eb92 100644 --- a/handlers/send_private_msg.go +++ b/handlers/send_private_msg.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/config" "github.com/hoshinonyaruko/gensokyo/echo" "github.com/hoshinonyaruko/gensokyo/idmap" "github.com/tencent-connect/botgo/dto" @@ -149,7 +150,7 @@ func handleSendGuildChannelPrivateMsg(client callapi.Client, api openapi.OpenAPI log.Println("foundItems:", foundItems) // 如果messageID为空,通过函数获取 if messageID == "" { - messageID = GetMessageIDByUseridOrGroupid(client.GetAppIDStr(), message.Params.UserID) + messageID = GetMessageIDByUseridOrGroupid(config.GetAppIDStr(), message.Params.UserID) log.Println("通过GetMessageIDByUserid函数获取的message_id:", messageID) } @@ -166,9 +167,11 @@ func handleSendGuildChannelPrivateMsg(client callapi.Client, api openapi.OpenAPI // 优先发送文本信息 if messageText != "" { textMsg, _ := generateReplyMessage(messageID, nil, messageText) - if _, err := apiv2.PostDirectMessage(context.TODO(), dm, textMsg); err != nil { + if _, err = apiv2.PostDirectMessage(context.TODO(), dm, textMsg); err != nil { log.Printf("发送文本信息失败: %v", err) } + //发送成功回执 + SendResponse(client, err, &message) } // 遍历foundItems并发送每种信息 @@ -190,13 +193,17 @@ func handleSendGuildChannelPrivateMsg(client callapi.Client, api openapi.OpenAPI reply.Content = "" // 使用Multipart方法发送 - if _, err := api.PostDirectMessageMultipart(context.TODO(), dm, reply, fileImageData); err != nil { + if _, err = api.PostDirectMessageMultipart(context.TODO(), dm, reply, fileImageData); err != nil { log.Printf("使用multipart发送 %s 信息失败: %v message_id %v", key, err, messageID) } + //发送成功回执 + SendResponse(client, err, &message) } else { - if _, err := api.PostDirectMessage(context.TODO(), dm, reply); err != nil { + if _, err = api.PostDirectMessage(context.TODO(), dm, reply); err != nil { log.Printf("发送 %s 信息失败: %v", key, err) } + //发送成功回执 + SendResponse(client, err, &message) } } diff --git a/main.go b/main.go index b95be7a7..66bd3eb3 100644 --- a/main.go +++ b/main.go @@ -28,17 +28,7 @@ import ( ) // 消息处理器,持有 openapi 对象 -var processor *Processor.Processor - -// 修改函数的返回类型为 *Processor -func NewProcessor(api openapi.OpenAPI, apiv2 openapi.OpenAPI, settings *config.Settings, wsclient *wsclient.WebSocketClient) *Processor.Processor { - return &Processor.Processor{ - Api: api, - Apiv2: apiv2, - Settings: settings, - Wsclient: wsclient, - } -} +var p *Processor.Processors func main() { if _, err := os.Stat("config.yml"); os.IsNotExist(err) { @@ -134,30 +124,40 @@ func main() { } }() - // 创建一个通道来传递 WebSocketClient - wsClientChan := make(chan *wsclient.WebSocketClient) + // 启动多个WebSocket客户端 + wsClients := []*wsclient.WebSocketClient{} + 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 { + fmt.Printf("Error creating WebSocketClient for address %s: %v\n", address, err) + errorChan <- err + return + } + wsClientChan <- wsClient + }(wsAddr) + } - // 在新的 go 函数中初始化 wsClient - go func() { - wsClient, err := wsclient.NewWebSocketClient(conf.Settings.WsAddress, conf.Settings.AppID, api, apiV2) - if err != nil { - fmt.Printf("Error creating WebSocketClient: %v\n", err) - close(wsClientChan) // 关闭通道表示不再发送值 - return + // Collect results + for i := 0; i < len(conf.Settings.WsAddress); i++ { + select { + case wsClient := <-wsClientChan: + wsClients = append(wsClients, wsClient) + case err := <-errorChan: + fmt.Printf("Error encountered while initializing WebSocketClient: %v\n", err) } - wsClientChan <- wsClient // 将 wsClient 发送到通道 - }() - - // 从通道中接收 wsClient 的值 - wsClient := <-wsClientChan + } - // 确保 wsClient 不为 nil,然后创建 Processor - if wsClient != nil { - fmt.Println("wsClient is successfully initialized.") - processor = NewProcessor(api, apiV2, &conf.Settings, wsClient) + // 确保所有wsClients都已初始化 + if len(wsClients) != len(conf.Settings.WsAddress) { + fmt.Println("Error: Not all wsClients are initialized!") + log.Fatalln("Failed to initialize all WebSocketClients.") } else { - fmt.Println("Error: wsClient is nil!") - log.Fatalln("Failed to initialize WebSocketClient.") + fmt.Println("All wsClients are successfully initialized.") + p = Processor.NewProcessor(api, apiV2, &conf.Settings, wsClients) } //创建idmap服务器 @@ -166,14 +166,16 @@ func main() { //图片上传 调用次数限制 rateLimiter := server.NewRateLimiter() - + //是否启动服务器 + shouldStartServer := !conf.Settings.Lotus || conf.Settings.EnableWsServer //如果连接到其他gensokyo,则不需要启动服务器 - if !conf.Settings.Lotus { + if shouldStartServer { r := gin.Default() r.GET("/getid", server.GetIDHandler) r.POST("/uploadpic", server.UploadBase64ImageHandler(rateLimiter)) r.Static("/channel_temp", "./channel_temp") - r.Run("0.0.0.0:" + conf.Settings.Port) // 注意,这里我更改了端口为你提供的Port,并监听0.0.0.0地址 + r.GET("/ws", server.WsHandlerWithDependencies(api, apiV2)) + r.Run("0.0.0.0:" + conf.Settings.Port) // 监听0.0.0.0地址的Port端口 } // 使用通道来等待信号 @@ -184,9 +186,12 @@ func main() { <-sigCh // 关闭 WebSocket 连接 - err = wsClient.Close() - if err != nil { - fmt.Printf("Error closing WebSocket connection: %v\n", err) + // wsClients 是一个 *wsclient.WebSocketClient 的切片 + for _, client := range wsClients { + err := client.Close() + if err != nil { + fmt.Printf("Error closing WebSocket connection: %v\n", err) + } } } @@ -207,7 +212,7 @@ func ErrorNotifyHandler() event.ErrorNotifyHandler { // ATMessageEventHandler 实现处理 频道at 消息的回调 func ATMessageEventHandler() event.ATMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSATMessageData) error { - return processor.ProcessGuildATMessage(data) + return p.ProcessGuildATMessage(data) } } @@ -238,7 +243,7 @@ func MemberEventHandler() event.GuildMemberEventHandler { // DirectMessageHandler 处理私信事件 func DirectMessageHandler() event.DirectMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSDirectMessageData) error { - return processor.ProcessChannelDirectMessage(data) + return p.ProcessChannelDirectMessage(data) } } @@ -246,7 +251,7 @@ func DirectMessageHandler() event.DirectMessageEventHandler { func CreateMessageHandler() event.MessageEventHandler { return func(event *dto.WSPayload, data *dto.WSMessageData) error { fmt.Println("收到私域信息", data) - return processor.ProcessGuildNormalMessage(data) + return p.ProcessGuildNormalMessage(data) } } @@ -254,22 +259,21 @@ func CreateMessageHandler() event.MessageEventHandler { func InteractionHandler() event.InteractionEventHandler { return func(event *dto.WSPayload, data *dto.WSInteractionData) error { fmt.Println(data) - return processor.ProcessInlineSearch(data) + return p.ProcessInlineSearch(data) } } // GroupATMessageEventHandler 实现处理 群at 消息的回调 func GroupATMessageEventHandler() event.GroupATMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSGroupATMessageData) error { - return processor.ProcessGroupMessage(data) + return p.ProcessGroupMessage(data) } } // C2CMessageEventHandler 实现处理 群私聊 消息的回调 func C2CMessageEventHandler() event.C2CMessageEventHandler { return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error { - log.Print("1111") - return processor.ProcessC2CMessage(data) + return p.ProcessC2CMessage(data) } } @@ -293,7 +297,7 @@ func getHandlerByName(handlerName string) (interface{}, bool) { return CreateMessageHandler(), true case "InteractionHandler": //添加频道互动回应 return InteractionHandler(), true - case "ThreadEventHandler": //发帖事件 + case "ThreadEventHandler": //发帖事件 暂不支持 return nil, false //return ThreadEventHandler(), true case "GroupATMessageEventHandler": //群at信息 diff --git a/server/uploadpic.go b/server/uploadpic.go index 78c419d0..606a76a0 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -43,7 +43,7 @@ func NewRateLimiter() *RateLimiter { } } -// 网页后端,图床逻辑,基于gin和www静态文件的简易图床 +// 闭包,网页后端,图床逻辑,基于gin和www静态文件的简易图床 func UploadBase64ImageHandler(rateLimiter *RateLimiter) gin.HandlerFunc { return func(c *gin.Context) { ipAddress := c.ClientIP() diff --git a/server/wsserver.go b/server/wsserver.go new file mode 100644 index 00000000..30d9d2d2 --- /dev/null +++ b/server/wsserver.go @@ -0,0 +1,89 @@ +package server + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/hoshinonyaruko/gensokyo/callapi" + "github.com/hoshinonyaruko/gensokyo/wsclient" + "github.com/tencent-connect/botgo/openapi" +) + +type WebSocketServerClient struct { + Conn *websocket.Conn + API openapi.OpenAPI + APIv2 openapi.OpenAPI +} + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +// 使用闭包结构 因为gin需要c *gin.Context固定签名 +func WsHandlerWithDependencies(api openapi.OpenAPI, apiV2 openapi.OpenAPI) gin.HandlerFunc { + return func(c *gin.Context) { + wsHandler(api, apiV2, c) + } +} + +func wsHandler(api openapi.OpenAPI, apiV2 openapi.OpenAPI, c *gin.Context) { + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Printf("Failed to set websocket upgrade: %+v", err) + return + } + + clientIP := c.ClientIP() + headers := c.Request.Header + log.Printf("WebSocket client connected. IP: %s, Headers: %v", clientIP, headers) + + // 创建WebSocketServerClient实例 + client := &WebSocketServerClient{ + Conn: conn, + API: api, + APIv2: apiV2, + } + + defer conn.Close() + + for { + messageType, p, err := conn.ReadMessage() + if err != nil { + log.Printf("Error reading message: %v", err) + return + } + + if messageType == websocket.TextMessage { + processWSMessage(client, p) // 使用WebSocketServerClient而不是直接使用连接 + } + } +} + +func processWSMessage(client *WebSocketServerClient, msg []byte) { + var message callapi.ActionMessage + err := json.Unmarshal(msg, &message) + if err != nil { + log.Printf("Error unmarshalling message: %v, Original message: %s", err, string(msg)) + return + } + + fmt.Println("Received from WebSocket onebotv11 client:", wsclient.TruncateMessage(message, 500)) + // 调用callapi + callapi.CallAPIFromDict(client, client.API, client.APIv2, message) +} + +// 发信息给client +func (c *WebSocketServerClient) SendMessage(message map[string]interface{}) error { + msgBytes, err := json.Marshal(message) + if err != nil { + log.Println("Error marshalling message:", err) + return err + } + return c.Conn.WriteMessage(websocket.TextMessage, msgBytes) +} diff --git a/wsclient/ws.go b/wsclient/ws.go index 4255b1a3..5db10e70 100644 --- a/wsclient/ws.go +++ b/wsclient/ws.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "net/http" + "net/url" "time" "github.com/gorilla/websocket" @@ -18,17 +19,6 @@ type WebSocketClient struct { conn *websocket.Conn api openapi.OpenAPI apiv2 openapi.OpenAPI - appid uint64 -} - -// 获取appid -func (c *WebSocketClient) GetAppID() uint64 { - return c.appid -} - -// 获取appid的字符串形式 -func (c *WebSocketClient) GetAppIDStr() string { - return fmt.Sprintf("%d", c.appid) } // 发送json信息给onebot应用端 @@ -70,13 +60,13 @@ func (c *WebSocketClient) recvMessage(msg []byte) { return } - fmt.Println("Received from onebotv11:", truncateMessage(message, 500)) + fmt.Println("Received from onebotv11 server:", TruncateMessage(message, 500)) // 调用callapi callapi.CallAPIFromDict(c, c.api, c.apiv2, message) } // 截断信息 -func truncateMessage(message callapi.ActionMessage, maxLength int) string { +func TruncateMessage(message callapi.ActionMessage, maxLength int) string { paramsStr, err := json.Marshal(message.Params) if err != nil { return "Error marshalling Params for truncation." @@ -112,7 +102,22 @@ func (c *WebSocketClient) sendHeartbeat(ctx context.Context, botID uint64) { // NewWebSocketClient 创建 WebSocketClient 实例,接受 WebSocket URL、botID 和 openapi.OpenAPI 实例 func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2 openapi.OpenAPI) (*WebSocketClient, error) { - token := config.GetWsToken() // 从配置中获取 token + addresses := config.GetWsAddress() + tokens := config.GetWsToken() + + var token string + for index, address := range addresses { + if address == urlStr && index < len(tokens) { + token = tokens[index] + break + } + } + + // 检查URL中是否有access_token参数 + mp := getParamsFromURI(urlStr) + if val, ok := mp["access_token"]; ok { + token = val + } headers := http.Header{ "User-Agent": []string{"CQHttp/4.15.0"}, @@ -120,11 +125,10 @@ func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2 "X-Self-ID": []string{fmt.Sprintf("%d", botID)}, } - // 如果 token 不为空,将其添加到 headers 中 if token != "" { headers["Authorization"] = []string{"Token " + token} } - + fmt.Printf("准备使用token[%s]连接到[%s]\n", token, urlStr) dialer := websocket.Dialer{ Proxy: http.ProxyFromEnvironment, HandshakeTimeout: 45 * time.Second, @@ -146,7 +150,7 @@ func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2 } } - client := &WebSocketClient{conn: conn, api: api, apiv2: apiv2, appid: botID} + client := &WebSocketClient{conn: conn, api: api, apiv2: apiv2} // Sending initial message similar to your setupB function message := map[string]interface{}{ @@ -177,3 +181,23 @@ func NewWebSocketClient(urlStr string, botID uint64, api openapi.OpenAPI, apiv2 func (ws *WebSocketClient) Close() error { return ws.conn.Close() } + +// getParamsFromURI 解析给定URI中的查询参数,并返回一个映射(map) +func getParamsFromURI(uriStr string) map[string]string { + params := make(map[string]string) + + u, err := url.Parse(uriStr) + if err != nil { + fmt.Printf("Error parsing the URL: %v\n", err) + return params + } + + // 遍历查询参数并将其添加到返回的映射中 + for key, values := range u.Query() { + if len(values) > 0 { + params[key] = values[0] // 如果一个参数有多个值,这里只选择第一个。可以根据需求进行调整。 + } + } + + return params +}