package config import ( "fmt" "sync" "github.com/BurntSushi/toml" "github.com/fsnotify/fsnotify" "go.uber.org/zap" ) // Config 应用配置结构 type Config struct { Server ServerConfig `toml:"server"` Log LogConfig `toml:"log"` Protocol ProtocolConfig `toml:"protocol"` Bots []BotConfig `toml:"bots"` } // ServerConfig 服务器配置 type ServerConfig struct { Host string `toml:"host"` Port int `toml:"port"` } // LogConfig 日志配置 type LogConfig struct { Level string `toml:"level"` Output string `toml:"output"` MaxSize int `toml:"max_size"` MaxBackups int `toml:"max_backups"` MaxAge int `toml:"max_age"` } // ProtocolConfig 协议配置 type ProtocolConfig struct { Name string `toml:"name"` Version string `toml:"version"` Options map[string]string `toml:"options"` } // BotConfig Bot 配置 type BotConfig struct { ID string `toml:"id"` Protocol string `toml:"protocol"` Enabled bool `toml:"enabled"` Milky MilkyConfig `toml:"milky"` OneBot11 OneBot11Config `toml:"onebot11"` } // MilkyConfig Milky 协议配置 type MilkyConfig struct { ProtocolURL string `toml:"protocol_url"` AccessToken string `toml:"access_token"` EventMode string `toml:"event_mode"` WebhookListenAddr string `toml:"webhook_listen_addr"` Timeout int `toml:"timeout"` RetryCount int `toml:"retry_count"` } // OneBot11Config OneBot11 协议配置 type OneBot11Config struct { ConnectionType string `toml:"connection_type"` // ws, ws-reverse, http, http-post Host string `toml:"host"` Port int `toml:"port"` AccessToken string `toml:"access_token"` WSUrl string `toml:"ws_url"` // 正向WS地址 WSReverseUrl string `toml:"ws_reverse_url"` // 反向WS监听地址 HTTPUrl string `toml:"http_url"` // 正向HTTP地址 HTTPPostUrl string `toml:"http_post_url"` // HTTP POST上报地址 Secret string `toml:"secret"` // 签名密钥 Timeout int `toml:"timeout"` // 超时时间(秒) Heartbeat int `toml:"heartbeat"` // 心跳间隔(秒) ReconnectInterval int `toml:"reconnect_interval"` // 重连间隔(秒) SelfID string `toml:"self_id"` // 机器人QQ号 Nickname string `toml:"nickname"` // 机器人昵称 } // ConfigManager 配置管理器 type ConfigManager struct { configPath string config *Config logger *zap.Logger mu sync.RWMutex callbacks []func(*Config) } // NewConfigManager 创建配置管理器 func NewConfigManager(configPath string, logger *zap.Logger) *ConfigManager { return &ConfigManager{ configPath: configPath, logger: logger, callbacks: make([]func(*Config), 0), } } // Load 加载配置文件 func (cm *ConfigManager) Load() error { cm.mu.Lock() defer cm.mu.Unlock() var cfg Config if _, err := toml.DecodeFile(cm.configPath, &cfg); err != nil { return fmt.Errorf("failed to decode config: %w", err) } cm.config = &cfg cm.logger.Info("Config loaded successfully", zap.String("path", cm.configPath), zap.String("server", fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)), ) // 触发回调 for _, cb := range cm.callbacks { cb(cm.config) } return nil } // Get 获取当前配置 func (cm *ConfigManager) Get() *Config { cm.mu.RLock() defer cm.mu.RUnlock() return cm.config } // Reload 重新加载配置 func (cm *ConfigManager) Reload() error { return cm.Load() } // RegisterCallback 注册配置变更回调 func (cm *ConfigManager) RegisterCallback(callback func(*Config)) { cm.mu.Lock() defer cm.mu.Unlock() cm.callbacks = append(cm.callbacks, callback) } // Watch 监听配置文件变化 func (cm *ConfigManager) Watch() error { watcher, err := fsnotify.NewWatcher() if err != nil { return fmt.Errorf("failed to create watcher: %w", err) } if err := watcher.Add(cm.configPath); err != nil { return fmt.Errorf("failed to watch config file: %w", err) } go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } if event.Op&fsnotify.Write == fsnotify.Write { cm.logger.Info("Config file changed, reloading...", zap.String("file", event.Name)) if err := cm.Reload(); err != nil { cm.logger.Error("Failed to reload config", zap.Error(err)) } } case err, ok := <-watcher.Errors: if !ok { return } cm.logger.Error("Watcher error", zap.Error(err)) } } }() return nil } // Close 关闭配置管理器 func (cm *ConfigManager) Close() error { return nil }