diff --git a/botgo/dto/message_create.go b/botgo/dto/message_create.go index 0e1be64b..0107c976 100644 --- a/botgo/dto/message_create.go +++ b/botgo/dto/message_create.go @@ -21,6 +21,7 @@ type RichMediaMessage struct { EventID string `json:"event_id,omitempty"` // 要回复的事件id, 逻辑同MsgID FileType uint64 `json:"file_type,omitempty"` // 业务类型,图片,文件,语音,视频 文件类型,取值:1图片,2视频,3语音(目前语音只支持silk格式) URL string `json:"url,omitempty"` + FileData string `json:"file_data,omitempty"` //没有base64头的base64 SrvSendMsg bool `json:"srv_send_msg,omitempty"` Content string `json:"content,omitempty"` } diff --git a/config/config.go b/config/config.go index d97e6d4d..a5a6bf91 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" "sync" + "time" "github.com/hoshinonyaruko/gensokyo/mylog" "github.com/hoshinonyaruko/gensokyo/sys" @@ -31,136 +32,170 @@ type VisualPrefixConfig struct { NoWhiteResponse string `yaml:"No_White_Response"` } type Settings struct { - WsAddress []string `yaml:"ws_address"` - AppID uint64 `yaml:"app_id"` - Token string `yaml:"token"` - ClientSecret string `yaml:"client_secret"` - TextIntent []string `yaml:"text_intent"` - GlobalChannelToGroup bool `yaml:"global_channel_to_group"` - GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` - GlobalForumToChannel bool `yaml:"global_forum_to_channel"` - Array bool `yaml:"array"` - Server_dir string `yaml:"server_dir"` - Lotus bool `yaml:"lotus"` - Port string `yaml:"port"` - 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开关 - WsServerToken string `yaml:"ws_server_token,omitempty"` //正向ws token - IdentifyFile bool `yaml:"identify_file"` // 域名校验文件 - Crt string `yaml:"crt"` - Key string `yaml:"key"` - DeveloperLog bool `yaml:"developer_log"` - Username string `yaml:"server_user_name"` - Password string `yaml:"server_user_password"` - 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"` - SandBoxMode bool `yaml:"sandbox_mode"` - Title string `yaml:"title"` - HashID bool `yaml:"hash_id"` - TwoWayEcho bool `yaml:"twoway_echo"` - LazyMessageId bool `yaml:"lazy_message_id"` - WhitePrefixMode bool `yaml:"white_prefix_mode"` - WhitePrefixs []string `yaml:"white_prefixs"` - BlackPrefixMode bool `yaml:"black_prefix_mode"` - BlackPrefixs []string `yaml:"black_prefixs"` - VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` - VisibleIp bool `yaml:"visible_ip"` - ForwardMsgLimit int `yaml:"forward_msg_limit"` - DevMessgeID bool `yaml:"dev_message_id"` - LogLevel int `yaml:"log_level"` - SaveLogs bool `yaml:"save_logs"` - BindPrefix string `yaml:"bind_prefix"` - MePrefix string `yaml:"me_prefix"` - FrpPort string `yaml:"frp_port"` - RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` - ImageLimitB int `yaml:"image_limit"` - RecordSampleRate int `yaml:"record_sampleRate"` - RecordBitRate int `yaml:"record_bitRate"` - NoWhiteResponse string `yaml:"No_White_Response"` - SendError bool `yaml:"send_error"` - AddAtGroup bool `yaml:"add_at_group"` - UrlPicTransfer bool `yaml:"url_pic_transfer"` - LotusPassword string `yaml:"lotus_password"` - WsServerPath string `yaml:"ws_server_path"` - IdmapPro bool `yaml:"idmap_pro"` - CardAndNick string `yaml:"card_nick"` - AutoBind bool `yaml:"auto_bind"` - CustomBotName string `yaml:"custom_bot_name"` - SendDelay int `yaml:"send_delay"` - AtoPCount int `yaml:"AMsgRetryAsPMsg_Count"` - ReconnecTimes int `yaml:"reconnect_times"` - HeartBeatInterval int `yaml:"heart_beat_interval"` - LaunchReconectTimes int `yaml:"launch_reconnect_times"` - UnlockPrefix string `yaml:"unlock_prefix"` - WhiteBypass []int64 `yaml:"white_bypass"` - TransferUrl bool `yaml:"transfer_url"` - HttpAddress string `yaml:"http_address"` - AccessToken string `yaml:"http_access_token"` - HttpVersion int `yaml:"http_version"` - HttpTimeOut int `yaml:"http_timeout"` - PostUrl []string `yaml:"post_url"` - PostSecret []string `yaml:"post_secret"` - PostMaxRetries []int `yaml:"post_max_retries"` - PostRetriesInterval []int `yaml:"post_retries_interval"` - NativeOb11 bool `yaml:"native_ob11"` - RamDomSeq bool `yaml:"ramdom_seq"` - UrlToQrimage bool `yaml:"url_to_qrimage"` - QrSize int `yaml:"qr_size"` - WhiteBypassRevers bool `yaml:"white_bypass_reverse"` - GuildUrlImageToBase64 bool `yaml:"guild_url_image_to_base64"` - TencentBucketName string `yaml:"t_COS_BUCKETNAME"` - TencentBucketRegion string `yaml:"t_COS_REGION"` - TencentCosSecretid string `yaml:"t_COS_SECRETID"` - TencentSecretKey string `yaml:"t_COS_SECRETKEY"` - TencentAudit bool `yaml:"t_audit"` - OssType int `yaml:"oss_type"` - BaiduBOSBucketName string `yaml:"b_BOS_BUCKETNAME"` - BaiduBCEAK string `yaml:"b_BCE_AK"` - BaiduBCESK string `yaml:"b_BCE_SK"` - BaiduAudit int `yaml:"b_audit"` - AliyunEndpoint string `yaml:"a_OSS_EndPoint"` - AliyunAccessKeyId string `yaml:"a_OSS_AccessKeyId"` - AliyunAccessKeySecret string `yaml:"a_OSS_AccessKeySecret"` - AliyunBucketName string `yaml:"a_OSS_BucketName"` - AliyunAudit bool `yaml:"a_audit"` - Alias []string `yaml:"alias"` - SelfIntroduce []string `yaml:"self_introduce"` - WhiteEnable []bool `yaml:"white_enable"` - IdentifyAppids []int64 `yaml:"identify_appids"` - TransFormApiIds bool `yaml:"transform_api_ids"` - CustomTemplateID string `yaml:"custom_template_id"` - KeyBoardID string `yaml:"keyboard_id"` - Uin int64 `yaml:"uin"` - VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` - Enters []string `yaml:"enters"` - LinkPrefix string `yaml:"link_prefix"` - LinkBots []string `yaml:"link_bots"` - LinkText string `yaml:"link_text"` - LinkPic string `yaml:"link_pic"` - MusicPrefix string `yaml:"music_prefix"` - DisableWebui bool `yaml:"disable_webui"` - ShardCount int `yaml:"shard_count"` - ShardID int `yaml:"shard_id"` - BotForumTitle string `yaml:"bot_forum_title"` - GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` - AutoPutInteraction bool `yaml:"auto_put_interaction"` - PutInteractionDelay int `yaml:"put_interaction_delay"` - ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` - Fix11300 bool `yaml:"fix_11300"` - LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` - GetGroupListAllGuilds bool `yaml:"get_g_list_all_guilds"` - GetGroupListGuilds string `yaml:"get_g_list_guilds"` - GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` - GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` - GetGroupListDelay int `yaml:"get_g_list_delay"` - GlobalServerTempQQguild bool `yaml:"global_server_temp_qqguild"` - ServerTempQQguild string `yaml:"server_temp_qqguild"` - ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` + //反向ws设置 + WsAddress []string `yaml:"ws_address"` + WsToken []string `yaml:"ws_token,omitempty"` + ReconnecTimes int `yaml:"reconnect_times"` + HeartBeatInterval int `yaml:"heart_beat_interval"` + LaunchReconectTimes int `yaml:"launch_reconnect_times"` + //基础配置 + AppID uint64 `yaml:"app_id"` + Uin int64 `yaml:"uin"` + Token string `yaml:"token"` + ClientSecret string `yaml:"client_secret"` + ShardCount int `yaml:"shard_count"` + ShardID int `yaml:"shard_id"` + //事件订阅类 + TextIntent []string `yaml:"text_intent"` + //转换类 + GlobalChannelToGroup bool `yaml:"global_channel_to_group"` + GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` + GlobalForumToChannel bool `yaml:"global_forum_to_channel"` + GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` + HashID bool `yaml:"hash_id"` + IdmapPro bool `yaml:"idmap_pro"` + //gensokyo互联类 + Server_dir string `yaml:"server_dir"` + Port string `yaml:"port"` + BackupPort string `yaml:"backup_port"` + Lotus bool `yaml:"lotus"` + LotusPassword string `yaml:"lotus_password"` + LotusWithoutIdmaps bool `yaml:"lotus_without_idmaps"` + //增强配置 + MasterID []string `yaml:"master_id,omitempty"` + RecordSampleRate int `yaml:"record_sampleRate"` + RecordBitRate int `yaml:"record_bitRate"` + CardAndNick string `yaml:"card_nick"` + AutoBind bool `yaml:"auto_bind"` + //发图相关 + OssType int `yaml:"oss_type"` + ImageLimit int `yaml:"image_sizelimit"` + ImageLimitB int `yaml:"image_limit"` + GuildUrlImageToBase64 bool `yaml:"guild_url_image_to_base64"` + UrlPicTransfer bool `yaml:"url_pic_transfer"` + ImgUpApiVtv2 bool `yaml:"img_up_api_ntv2"` + UploadPicV2Base64 bool `yaml:"uploadpicv2_b64"` + GlobalServerTempQQguild bool `yaml:"global_server_temp_qqguild"` + ServerTempQQguild string `yaml:"server_temp_qqguild"` + ServerTempQQguildPool []string `yaml:"server_temp_qqguild_pool"` + //正向ws设置 + WsServerPath string `yaml:"ws_server_path"` + EnableWsServer bool `yaml:"enable_ws_server,omitempty"` + WsServerToken string `yaml:"ws_server_token,omitempty"` + //ssl和链接转换类 + IdentifyFile bool `yaml:"identify_file"` + IdentifyAppids []int64 `yaml:"identify_appids"` + Crt string `yaml:"crt"` + Key string `yaml:"key"` + //日志类 + DeveloperLog bool `yaml:"developer_log"` + LogLevel int `yaml:"log_level"` + SaveLogs bool `yaml:"save_logs"` + //webui相关 + DisableWebui bool `yaml:"disable_webui"` + Username string `yaml:"server_user_name"` + Password string `yaml:"server_user_password"` + //指令魔法类 + RemovePrefix bool `yaml:"remove_prefix"` + RemoveAt bool `yaml:"remove_at"` + RemoveBotAtGroup bool `yaml:"remove_bot_at_group"` + AddAtGroup bool `yaml:"add_at_group"` + WhitePrefixMode bool `yaml:"white_prefix_mode"` + VwhitePrefixMode bool `yaml:"v_white_prefix_mode"` + WhitePrefixs []string `yaml:"white_prefixs"` + WhiteBypass []int64 `yaml:"white_bypass"` + WhiteEnable []bool `yaml:"white_enable"` + WhiteBypassRevers bool `yaml:"white_bypass_reverse"` + NoWhiteResponse string `yaml:"No_White_Response"` + BlackPrefixMode bool `yaml:"black_prefix_mode"` + BlackPrefixs []string `yaml:"black_prefixs"` + Alias []string `yaml:"alias"` + Enters []string `yaml:"enters"` + VisualPrefixs []VisualPrefixConfig `yaml:"visual_prefixs"` + //开发增强类 + DevlopAcDir string `yaml:"develop_access_token_dir"` + DevBotid string `yaml:"develop_bot_id"` + SandBoxMode bool `yaml:"sandbox_mode"` + DevMessgeID bool `yaml:"dev_message_id"` + SendError bool `yaml:"send_error"` + //增长营销类 + SelfIntroduce []string `yaml:"self_introduce"` + //api修改 + GetGroupListAllGuilds bool `yaml:"get_g_list_all_guilds"` + GetGroupListGuilds string `yaml:"get_g_list_guilds"` + GetGroupListReturnGuilds bool `yaml:"get_g_list_return_guilds"` + GetGroupListGuidsType int `yaml:"get_g_list_guilds_type"` + GetGroupListDelay int `yaml:"get_g_list_delay"` + ForwardMsgLimit int `yaml:"forward_msg_limit"` + CustomBotName string `yaml:"custom_bot_name"` + TransFormApiIds bool `yaml:"transform_api_ids"` + AutoPutInteraction bool `yaml:"auto_put_interaction"` + PutInteractionDelay int `yaml:"put_interaction_delay"` + //onebot修改 + TwoWayEcho bool `yaml:"twoway_echo"` + Array bool `yaml:"array"` + NativeOb11 bool `yaml:"native_ob11"` + //url相关 + VisibleIp bool `yaml:"visible_ip"` + UrlToQrimage bool `yaml:"url_to_qrimage"` + QrSize int `yaml:"qr_size"` + TransferUrl bool `yaml:"transfer_url"` + //框架修改 + Title string `yaml:"title"` + FrpPort string `yaml:"frp_port"` + //MD相关 + CustomTemplateID string `yaml:"custom_template_id"` + KeyBoardID string `yaml:"keyboard_id"` + //发送行为修改 + LazyMessageId bool `yaml:"lazy_message_id"` + RamDomSeq bool `yaml:"ramdom_seq"` + BotForumTitle string `yaml:"bot_forum_title"` + AtoPCount int `yaml:"AMsgRetryAsPMsg_Count"` + SendDelay int `yaml:"send_delay"` + //错误临时修复类 + Fix11300 bool `yaml:"fix_11300"` + //内置指令 + BindPrefix string `yaml:"bind_prefix"` + MePrefix string `yaml:"me_prefix"` + UnlockPrefix string `yaml:"unlock_prefix"` + LinkPrefix string `yaml:"link_prefix"` + MusicPrefix string `yaml:"music_prefix"` + LinkBots []string `yaml:"link_bots"` + LinkText string `yaml:"link_text"` + LinkPic string `yaml:"link_pic"` + //HTTP API配置 + HttpAddress string `yaml:"http_address"` + AccessToken string `yaml:"http_access_token"` + HttpVersion int `yaml:"http_version"` + HttpTimeOut int `yaml:"http_timeout"` + PostUrl []string `yaml:"post_url"` + PostSecret []string `yaml:"post_secret"` + PostMaxRetries []int `yaml:"post_max_retries"` + PostRetriesInterval []int `yaml:"post_retries_interval"` + //腾讯云 + TencentBucketName string `yaml:"t_COS_BUCKETNAME"` + TencentBucketRegion string `yaml:"t_COS_REGION"` + TencentCosSecretid string `yaml:"t_COS_SECRETID"` + TencentSecretKey string `yaml:"t_COS_SECRETKEY"` + TencentAudit bool `yaml:"t_audit"` + //百度云 + BaiduBOSBucketName string `yaml:"b_BOS_BUCKETNAME"` + BaiduBCEAK string `yaml:"b_BCE_AK"` + BaiduBCESK string `yaml:"b_BCE_SK"` + BaiduAudit int `yaml:"b_audit"` + //阿里云 + AliyunEndpoint string `yaml:"a_OSS_EndPoint"` + AliyunAccessKeyId string `yaml:"a_OSS_AccessKeyId"` + AliyunAccessKeySecret string `yaml:"a_OSS_AccessKeySecret"` + AliyunBucketName string `yaml:"a_OSS_BucketName"` + AliyunAudit bool `yaml:"a_audit"` +} + +// CommentInfo 用于存储注释及其定位信息 +type CommentBlock struct { + Comments []string // 一个或多个连续的注释 + TargetKey string // 注释所指向的键(如果有) + Offset int // 注释与目标键之间的行数 } // LoadConfig 从文件中加载配置并初始化单例配置 @@ -204,6 +239,169 @@ func LoadConfig(path string) (*Config, error) { return instance, nil } +func CreateAndWriteConfigTemp() error { + // 读取config.yml + configFile, err := os.ReadFile("config.yml") + if err != nil { + return err + } + + // 获取当前日期 + currentDate := time.Now().Format("2006-1-2") + // 重命名原始config.yml文件 + err = os.Rename("config.yml", "config"+currentDate+".yml") + if err != nil { + return err + } + + var config Config + err = yaml.Unmarshal(configFile, &config) + if err != nil { + return err + } + + // 创建config_temp.yml文件 + tempFile, err := os.Create("config.yml") + if err != nil { + return err + } + defer tempFile.Close() + + // 使用yaml.Encoder写入,以保留注释 + encoder := yaml.NewEncoder(tempFile) + encoder.SetIndent(2) // 设置缩进 + err = encoder.Encode(config) + if err != nil { + return err + } + + // 处理注释并重命名文件 + err = addCommentsToConfigTemp(template.ConfigTemplate, "config.yml") + if err != nil { + return err + } + + return nil +} + +func parseTemplate(template string) ([]CommentBlock, map[string]string) { + var blocks []CommentBlock + lines := strings.Split(template, "\n") + + var currentBlock CommentBlock + var lastKey string + + directComments := make(map[string]string) + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "#") { + currentBlock.Comments = append(currentBlock.Comments, trimmed) // 收集注释行 + } else { + if containsKey(trimmed) { + key := strings.SplitN(trimmed, ":", 2)[0] + trimmedKey := strings.TrimSpace(key) + + if len(currentBlock.Comments) > 0 { + currentBlock.TargetKey = lastKey // 关联到上一个找到的键 + blocks = append(blocks, currentBlock) + currentBlock = CommentBlock{} // 重置为新的注释块 + } + + // 如果当前行包含注释,则单独处理 + if parts := strings.SplitN(trimmed, "#", 2); len(parts) > 1 { + directComments[trimmedKey] = "#" + parts[1] + } + lastKey = trimmedKey // 更新最后一个键 + } else if len(currentBlock.Comments) > 0 { + // 如果当前行不是注释行且存在挂起的注释,但并没有新的键出现,将其作为独立的注释块 + blocks = append(blocks, currentBlock) + currentBlock = CommentBlock{} // 重置为新的注释块 + } + } + } + + // 处理文件末尾的挂起注释块 + if len(currentBlock.Comments) > 0 { + blocks = append(blocks, currentBlock) + } + + return blocks, directComments +} + +func addCommentsToConfigTemp(template, tempFilePath string) error { + commentBlocks, directComments := parseTemplate(template) + //fmt.Printf("%v\n", directComments) + + // 读取并分割新生成的配置文件内容 + content, err := os.ReadFile(tempFilePath) + if err != nil { + return err + } + lines := strings.Split(string(content), "\n") + + // 处理并插入注释 + for _, block := range commentBlocks { + // 根据注释块的目标键,找到插入位置并插入注释 + for i, line := range lines { + if containsKey(line) { + key := strings.SplitN(line, ":", 2)[0] + if strings.TrimSpace(key) == block.TargetKey { + // 在目标键之前插入注释 + insertionPoint := i + block.Offset + if insertionPoint >= len(lines) { + lines = append(lines, block.Comments...) + } else { + lines = append(lines[:insertionPoint], append(block.Comments, lines[insertionPoint:]...)...) + } + break + } + } + } + } + + // 处理直接跟在键后面的注释 + // 接着处理直接跟在键后面的注释 + for i, line := range lines { + if containsKey(line) { + key := strings.SplitN(line, ":", 2)[0] + trimmedKey := strings.TrimSpace(key) + //fmt.Printf("%v\n", trimmedKey) + if comment, exists := directComments[trimmedKey]; exists { + // 如果这个键有直接的注释 + lines[i] = line + " " + comment + } + } + } + + // 重新组合lines为一个字符串,准备写回文件 + updatedContent := strings.Join(lines, "\n") + + // 写回更新后的内容到原配置文件 + err = os.WriteFile(tempFilePath, []byte(updatedContent), 0644) + if err != nil { + return err + } + + return nil +} + +// containsKey 检查给定的字符串行是否可能包含YAML键。 +// 它尝试排除注释行和冒号用于其他目的的行(例如,在URLs中)。 +func containsKey(line string) bool { + // 去除行首和行尾的空格 + trimmedLine := strings.TrimSpace(line) + + // 如果行是注释,直接返回false + if strings.HasPrefix(trimmedLine, "#") { + return false + } + + // 检查是否存在冒号,如果不存在,则直接返回false + colonIndex := strings.Index(trimmedLine, ":") + return colonIndex != -1 +} + // 确保配置完整性 func ensureConfigComplete(path string) error { // 读取配置文件到缓冲区 @@ -1998,3 +2196,15 @@ func GetServerTempQQguildPool() []string { } return nil // 返回nil,如果instance为nil } + +// 获取UploadPicV2Base64开关 +func GetUploadPicV2Base64() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to UploadPicV2 value.") + return false + } + return instance.Settings.UploadPicV2Base64 +} diff --git a/images/upload_api.go b/images/upload_api.go index f70bff01..8287f3fc 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -73,26 +73,27 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, return downloadURL, 0, width, height, nil } - extraPicAuditingType := config.GetOssType() - - switch extraPicAuditingType { - case 0: - picURL, err = originalUploadBehavior(base64Image) - case 1: - picURL, err = oss.UploadAndAuditImage(base64Image) // 腾讯 - case 2: - picURL, err = oss.UploadAndAuditImageB(base64Image) // 百度 - case 3: - picURL, err = oss.UploadAndAuditImageA(base64Image) // 阿里 - default: - return "", 0, 0, 0, errors.New("invalid extraPicAuditingType") - } - if err != nil { - return "", 0, 0, 0, err + //v2接口是否使用base64 + if !config.GetUploadPicV2Base64() { + extraPicAuditingType := config.GetOssType() + switch extraPicAuditingType { + case 0: + picURL, err = originalUploadBehavior(base64Image) + case 1: + picURL, err = oss.UploadAndAuditImage(base64Image) // 腾讯 + case 2: + picURL, err = oss.UploadAndAuditImageB(base64Image) // 百度 + case 3: + picURL, err = oss.UploadAndAuditImageA(base64Image) // 阿里 + default: + return "", 0, 0, 0, errors.New("invalid extraPicAuditingType") + } + if err != nil { + return "", 0, 0, 0, err + } } if config.GetImgUpApiVtv2() && groupID != "" { - if msgid == "" { msgid = echo.GetLazyMessagesId(groupID) } @@ -109,13 +110,25 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 用originalGroupID更新groupID groupID = originalGroupID } - richMediaMessage := &dto.RichMediaMessage{ - EventID: msgid, - FileType: 1, // 1代表图片 - URL: picURL, - Content: "", // 这个字段文档没有了 - SrvSendMsg: false, + var richMediaMessage *dto.RichMediaMessage + if !config.GetUploadPicV2Base64() { + richMediaMessage = &dto.RichMediaMessage{ + EventID: msgid, + FileType: 1, // 1代表图片 + URL: picURL, + Content: "", // 这个字段文档没有了 + SrvSendMsg: false, + } + } else { + richMediaMessage = &dto.RichMediaMessage{ + EventID: msgid, + FileType: 1, // 1代表图片 + FileData: base64Image, + Content: "", // 这个字段文档没有了 + SrvSendMsg: false, + } } + var fileInfo string //尝试群聊发图 fileInfo, err = uploadMedia(context.TODO(), groupID, richMediaMessage, apiv2) @@ -146,6 +159,7 @@ func UploadBase64ImageToServer(msgid string, base64Image string, groupID string, // 从Proto消息中读取值 realGroupID := mainMessage.GetA().GetB().GetInfo().GetDetail().GetGroupInfo().GetGroupNumber() downloadURL := mainMessage.GetA().GetImageData().GetImageInfo().GetUrl() + downloadURL = "https://multimedia.nt.qq.com.cn" + downloadURL width := mainMessage.GetA().GetImageData().GetWidth() height := mainMessage.GetA().GetImageData().GetHeight() diff --git a/main.go b/main.go index cf690003..6dd281e0 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,7 @@ var p *Processor.Processors func main() { // 定义faststart命令行标志。默认为false。 fastStart := flag.Bool("faststart", false, "start without initialization if set") + tidy := flag.Bool("tidy", false, "backup and tidy your config.yml") // 解析命令行参数到定义的标志。 flag.Parse() @@ -52,6 +53,12 @@ func main() { if !*fastStart { sys.InitBase() // 如果不是faststart模式,则执行初始化 } + if *tidy { + //备份配置 并刷新 + config.CreateAndWriteConfigTemp() + log.Println("配置文件已更新为新版,当前配置文件已备份.如产生问题请到群196173384反馈开发者。") + return + } if _, err := os.Stat("config.yml"); os.IsNotExist(err) { var ip string var err error diff --git a/template/config_template.go b/template/config_template.go index 92fa87d6..76c9fc7f 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -6,11 +6,19 @@ settings: #反向ws设置 ws_address: ["ws://<YOUR_WS_ADDRESS>:<YOUR_WS_PORT>"] # WebSocket服务的地址 支持多个["","",""] ws_token: ["","",""] #连接wss地址时服务器所需的token,按顺序一一对应,如果是ws地址,没有密钥,请留空. + reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 + heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 + launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 + + #基础设置 app_id: 12345 # 你的应用ID uin : 0 # 你的机器人QQ号,点击机器人资料卡查看 token: "<YOUR_APP_TOKEN>" # 你的应用令牌 client_secret: "<YOUR_CLIENT_SECRET>" # 你的客户端密钥 + shard_count: 1 #分片数量 默认1 + shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html + #事件订阅 text_intent: # 请根据公域 私域来选择intent,错误的intent将连接失败 - "ATMessageEventHandler" # 频道at信息 - "DirectMessageHandler" # 私域频道私信(dms) @@ -25,71 +33,63 @@ settings: # - "C2CMessageEventHandler" # 群私聊 仅频道机器人时候需要注释 # - "ThreadEventHandler" # 频道发帖事件 仅频道私域机器人可用 - + #转换类 global_channel_to_group: true # 是否将频道转换成群 默认true global_private_to_channel: false # 是否将私聊转换成频道 如果是群场景 会将私聊转为群(方便提审\测试) global_forum_to_channel: false # 是否将频道帖子信息转化为频道 子频道信息 如果开启global_channel_to_group会进一步转换为群信息 global_interaction_to_message : false # 是否将按钮和表态回调转化为消息 仅在设置了按钮回调中的message时有效 - bot_forum_title : "机器人帖子" # 机器人发帖子回复默认标题 - array: false # 连接trss云崽请开启array hash_id : false # 使用hash来进行idmaps转换,可以让user_id不是123开始的递增值 + idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. - server_dir: "<YOUR_SERVER_DIR>" # 提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 - port: "15630" # idmaps和图床对外开放的端口号 + #Gensokyo互联类 + server_dir: "<YOUR_SERVER_DIR>" # Lotus地址.不带http头的域名或ip,提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 + port: "15630" # Lotus端口.idmaps和图床对外开放的端口号,若要连接到另一个gensokyo,也是链接端口 backup_port : "5200" # 当totus为ture时,port值不再是本地webui的端口,使用lotus_Port来访问webui - - lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。 - # 使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。 - # 如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port - lotus_password : "" # lotus鉴权 设置后,从gsk需要保持相同密码来访问主gsk + lotus: false # lotus特性默认为false,当为true时,将会连接到另一个lotus为false的gensokyo。使用它提供的图床和idmaps服务(场景:同一个机器人在不同服务器运行,或内网需要发送base64图)。如果需要发送base64图片,需要设置正确的公网server_dir和开放对应的port, lotus鉴权 设置后,从gsk需要保持相同密码来访问主gsk + lotus_password : "" + lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. #增强配置项 - - image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 - image_limit : 100 #每分钟上传的最大图片数量,可自行增加 master_id : ["1","2"] #群场景尚未开放获取管理员和列表能力,手动从日志中获取需要设置为管理,的user_id并填入(适用插件有权限判断场景) record_sampleRate : 24000 #语音文件的采样率 最高48000 默认24000 单位Khz record_bitRate : 24000 #语音文件的比特率 默认25000 代表 25 kbps 最高无限 请根据带宽 您发送的实际码率调整 card_nick : "" #默认为空,连接mirai-overflow时,请设置为非空,这里是机器人对用户称谓,为空为插件获取,mirai不支持 auto_bind : true #测试功能,后期会移除 - AMsgRetryAsPMsg_Count : 30 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为所有群、频道的主动转被动消息队列最大长度,建议30-100,无上限 - reconnect_times : 100 #反向ws连接失败后的重试次数,希望一直重试,可设置9999 - heart_beat_interval : 10 #反向ws心跳间隔 单位秒 推荐5-10 - launch_reconnect_times : 1 #启动时尝试反向ws连接次数,建议先打开应用端再开启gensokyo,因为启动时连接会阻塞webui启动,默认只连接一次,可自行增大 - native_ob11 : false #如果你的机器人收到事件报错,请开启此选项增加兼容性 - ramdom_seq : false #当多开gensokyo时,如果遇到群信息只能发出一条,请开启每个gsk的此项.(建议使用一个gsk连接多个应用) - url_to_qrimage : false #将信息中的url转换为二维码单独作为图片发出,需要同时设置 #SSL配置类 机器人发送URL设置 的 transfer_url 为 true visible_ip也需要为true - qr_size : 200 #二维码尺寸,单位像素 - guild_url_image_to_base64 : false #解决频道发不了某些url图片,报错40003问题 + + #发图相关 oss_type : 0 #请完善后方具体配置 完成#腾讯云配置...,0代表配置server dir port服务器自行上传(省钱),1,腾讯cos存储桶 2,百度oss存储桶 3,阿里oss存储桶 - self_introduce : ["",""] #自我介绍,可设置多个随机发送,当不为空时,机器人被邀入群会发送自定义自我介绍 需手动添加新textintent - "GroupAddRobotEventHandler" - "GroupDelRobotEventHandler" + image_sizelimit : 0 #代表kb 腾讯api要求图片1500ms完成传输 如果图片发不出 请提升上行或设置此值 默认为0 不压缩 + image_limit : 100 #每分钟上传的最大图片数量,可自行增加 + guild_url_image_to_base64 : false #解决频道发不了某些url图片,报错40003问题 + url_pic_transfer : false #把图片url(任意来源图链)变成你备案的白名单url 需要较高上下行+ssl+自备案域名+设置白名单域名(暂时不需要) + img_up_api_ntv2: false #gsk内建图片上传api 开启后全局有效 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm + uploadpicv2_b64: true #uploadpicv2接口使用base64直接上传 https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm + global_server_temp_qqguild : false #需设置server_temp_qqguild,公域私域均可用,以频道为底层发图,速度快,该接口为进阶接口,使用有一定难度. + server_temp_qqguild : "0" #在v3图片接口采用固定的子频道号,可以是帖子子频道 https://www.yuque.com/km57bt/hlhnxg/uqmnsno3vx1ytp2q + server_temp_qqguild_pool : [] #填写v3发图接口的endpoint http://127.0.0.1:12345/uploadpicv3 当填写多个时采用循环方式负载均衡,注,不包括自身,如需要自身也要填写 #正向ws设置 ws_server_path : "ws" #默认监听0.0.0.0:port/ws_server_path 若有安全需求,可不放通port到公网,或设置ws_server_token 若想监听/ 可改为"",若想监听到不带/地址请写nil enable_ws_server: true #是否启用正向ws服务器 监听server_dir:port/ws_server_path ws_server_token : "12345" #正向ws的token 不启动正向ws可忽略 可为空 - #SSL配置类 机器人发送URL设置 - + #SSL配置类 和 白名单域名自动验证 identify_file : true #自动生成域名校验文件,在q.qq.com配置信息URL,在server_dir填入自己已备案域名,正确解析到机器人所在服务器ip地址,机器人即可发送链接 identify_appids : [] #默认不需要设置,完成SSL配置类+server_dir设置为域名+完成备案+ssl全套设置后,若有多个机器人需要过域名校验(自己名下)可设置,格式为,整数appid,组成的数组 crt : "" #证书路径 从你的域名服务商或云服务商申请签发SSL证书(qq要求SSL) key : "" #密钥路径 Apache(crt文件、key文件)示例: "C:\\123.key" \需要双写成\\ - transfer_url : true #默认开启,关闭后自理url发送,配置server_dir为你的域名,配置crt和key后,将域名/url和/image在q.qq.com后台通过校验,自动使用302跳转处理机器人发出的所有域名. - + #日志类 - developer_log : false #开启开发者日志 默认关闭 log_level : 1 # 0=debug 1=info 2=warning 3=error 默认1 save_logs : false #自动储存日志 #webui设置 - + disable_webui: false #禁用webui server_user_name : "useradmin" #默认网页面板用户名 server_user_password : "admin" #默认网页面板密码 - #指令过滤类 - + #指令魔法类 remove_prefix : false #是否忽略公域机器人指令前第一个/ remove_at : false #是否忽略公域机器人指令前第一个[CQ:aq,qq=机器人] 场景(公域机器人,但插件未适配at开头) remove_bot_at_group : true #因为群聊机器人不支持发at,开启本开关会自动隐藏群机器人发出的at(不影响频道场景) @@ -120,48 +120,59 @@ settings: No_White_Response : "" #开发增强类 - develop_access_token_dir : "" #开发者测试环境access_token自定义获取地址 默认留空 请留空忽略 develop_bot_id : "1234" #开发者环境需自行获取botid 填入 用户请不要设置这两行...开发者调试用 sandbox_mode : false #默认false 如果你只希望沙箱频道使用,请改为true dev_message_id : false #在沙盒和测试环境使用无限制msg_id 仅沙盒有效,正式环境请关闭,内测结束后,tx侧未来会移除 send_error : true #将报错用文本发出,避免机器人被审核报无响应 - url_pic_transfer : false #把图片url(任意来源图链)变成你备案的白名单url 需要较高上下行+ssl+自备案域名+设置白名单域名(暂时不需要) - idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. - send_delay : 300 #单位 毫秒 默认300ms 可以视情况减少到100或者50 - disable_webui: false #禁用webui - shard_count: 1 #分片数量 默认1 - shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html - auto_put_interaction : false #自动回应按钮回调的/interactions/{interaction_id} 注本api需要邮件申请,详细方法参考群公告:196173384 - put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay - img_up_api_ntv2: false #gsk内建图片上传api 是否将图片转换为ntqq图床url(md发图用,自行调用)文档:https://www.yuque.com/km57bt/hlhnxg/ig2nk88fccykn6dm - fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. - lotus_without_idmaps: false #lotus只通过url,图片上传,语音,不通过id转换,在本地当前gsk维护idmaps转换. + #增长营销类(推荐gensokyo-broadcast项目) + self_introduce : ["",""] #自我介绍,可设置多个随机发送,当不为空时,机器人被邀入群会发送自定义自我介绍 需手动添加新textintent - "GroupAddRobotEventHandler" - "GroupDelRobotEventHandler" + + + #API修改 get_g_list_all_guilds : false #在获取群列表api时,轮询获取全部的频道列表(api一次只能获取100个),建议仅在广播公告通知等特别场景时开启. get_g_list_delay : 500 #轮询时的延迟时间,毫秒数. get_g_list_guilds_type : 0 #0=全部返回,1=获取第1个子频道.以此类推.可以缩减返回值的大小. get_g_list_guilds : "10" #在获取群列表api时,一次返回的频道数量.这里是string,不要去掉引号.最大100(5分钟内连续请求=翻页),获取全部请开启get_g_list_return_guilds. get_g_list_return_guilds : true #获取群列表时是否返回频道列表. + forward_msg_limit : 3 #发送折叠转发信息时的最大限制条数 若要发转发信息 请设置lazy_message_id为true + custom_bot_name : "Gensokyo全域机器人" #自定义api返回的机器人名字,会在api调用中返回,默认Gensokyo全域机器人 + transform_api_ids : true #对get_group_menmber_list\get_group_member_info\get_group_list生效,是否在其中返回转换后的值(默认转换,不转换请自行处理插件逻辑,比如调用gsk的http api转换) + auto_put_interaction : false #自动回应按钮回调的/interactions/{interaction_id} 注本api需要邮件申请,详细方法参考群公告:196173384 + put_interaction_delay : 0 #单位毫秒 表示回应已收到回调类型的按钮的毫秒数 会按用户进行区分 非全局delay - global_server_temp_qqguild : false #需设置server_temp_qqguild,公域私域均可用,以频道为底层发图,速度快,该接口为进阶接口,使用有一定难度. - server_temp_qqguild : "0" #在v3图片接口采用固定的子频道号,可以是帖子子频道 https://www.yuque.com/km57bt/hlhnxg/uqmnsno3vx1ytp2q - server_temp_qqguild_pool : [] #填写v3发图接口的endpoint http://127.0.0.1:12345/uploadpicv3 当填写多个时采用循环方式负载均衡,注,不包括自身,如需要自身也要填写 + #Onebot修改 + twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false + array: false # 连接trss云崽请开启array + native_ob11 : false #如果你的机器人收到事件报错,请开启此选项增加兼容性 + #URL相关 + visible_ip : false #转换url时,如果server_dir是ip true将以ip形式发出url 默认隐藏url 将server_dir配置为自己域名可以转换url + url_to_qrimage : false #将信息中的url转换为二维码单独作为图片发出,需要同时设置 #SSL配置类 机器人发送URL设置 的 transfer_url 为 true visible_ip也需要为true + qr_size : 200 #二维码尺寸,单位像素 + transfer_url : true #默认开启,关闭后自理url发送,配置server_dir为你的域名,配置crt和key后,将域名/url和/image在q.qq.com后台通过校验,自动使用302跳转处理机器人发出的所有域名. + + #框架修改 title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 - custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人 + frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里 - twoway_echo : false #是否采用双向echo,根据机器人选择,獭獭\早苗 true 红色问答\椛椛 或者其他 请使用 false + #MD相关 custom_template_id : "" #自动转换图文信息到md所需要的id *需要应用端支持双方向echo keyboard_id : "" #自动转换图文信息到md所需要的按钮id *需要应用端支持双方向echo - lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池 - - visible_ip : false #转换url时,如果server_dir是ip true将以ip形式发出url 默认隐藏url 将server_dir配置为自己域名可以转换url - forward_msg_limit : 3 #发送折叠转发信息时的最大限制条数 若要发转发信息 请设置lazy_message_id为true - transform_api_ids : true #对get_group_menmber_list\get_group_member_info\get_group_list生效,是否在其中返回转换后的值(默认转换,不转换请自行处理插件逻辑,比如调用gsk的http api转换) - #bind指令类 + #发送行为修改 + lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池 + ramdom_seq : false #当多开gensokyo时,如果遇到群信息只能发出一条,请开启每个gsk的此项.(建议使用一个gsk连接多个应用) + bot_forum_title : "机器人帖子" # 机器人发帖子回复默认标题 + AMsgRetryAsPMsg_Count : 30 #当主动信息发送失败时,自动转为后续的被动信息发送,需要开启Lazy message id,该配置项为所有群、频道的主动转被动消息队列最大长度,建议30-100,无上限 + send_delay : 300 #单位 毫秒 默认300ms 可以视情况减少到100或者50 + + #错误临时修复类 + fix_11300: false #修复11300报错,需要在develop_bot_id填入自己机器人的appid. 11300原因暂时未知,临时修复方案. + + #内置指令类 bind_prefix : "/bind" #需设置 #增强配置项 master_id 可触发 me_prefix : "/me" #需设置 #增强配置项 master_id 可触发 unlock_prefix : "/unlock" #频道私信卡住了? gsk可以帮到你 在任意子频道发送unlock 你会收到来自机器人的频道私信 @@ -171,18 +182,13 @@ settings: link_text : "" #友情链接文本 不可为空! link_pic : "" #友情链接图片 可为空 需url图片 可带端口 不填可能会有显示错误 - #穿透\cos\oss类配置(可选!) - frp_port : "0" #不使用请保持为0,frp的端口,frp有内外端口,请在frp软件设置gensokyo的port,并将frp显示的对外端口填入这里 - - #HTTP API配置 - - #正向http + #HTTP API配置-正向http http_address: "" #http监听地址 与websocket独立 示例:0.0.0.0:5700 为空代表不开启 http_access_token: "" #http访问令牌 http_version : 11 #暂时只支持11 http_timeout: 5 #反向 HTTP 超时时间, 单位秒,<5 时将被忽略 - #反向http + #HTTP API配置-反向http post_url: [""] #反向HTTP POST地址列表 为空代表不开启 示例:http://192.168.0.100:5789 post_secret: [""] #密钥 post_max_retries: [3] #最大重试,0 时禁用