package config import ( "bufio" "os" "path/filepath" "strconv" "strings" "vrc_osc_go/internal/common" ) type Config struct { OSC OSCConfig VrcLog VrcLogConfig } type OSCConfig struct { Host string Port int } type VrcLogConfig struct { SelfName string GuestNames []string GuestFile string StaffNames []string MissingCount int LogPatterns []string } func Load(path string) (*Config, error) { cfg := &Config{ OSC: OSCConfig{Host: "127.0.0.1", Port: 9001}, } if path == "" { path = filepath.Join(common.RootDir(), "config", "config.toml") } else if !filepath.IsAbs(path) { path = filepath.Join(common.RootDir(), path) } file, err := os.Open(path) if err != nil { return cfg, nil } defer file.Close() var section string scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) line = strings.TrimPrefix(line, "\ufeff") if line == "" || strings.HasPrefix(line, "#") { continue } if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { section = strings.Trim(line, "[]") continue } parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { continue } key := strings.TrimSpace(parts[0]) val := strings.TrimSpace(parts[1]) val = strings.Trim(val, "\"'") switch section { case "osc": switch key { case "host": cfg.OSC.Host = val case "port": if n, err := strconv.Atoi(val); err == nil { cfg.OSC.Port = n } } case "self": if key == "name" { cfg.VrcLog.SelfName = val } case "notice": if key == "missing_count" { if n, err := strconv.Atoi(val); err == nil { cfg.VrcLog.MissingCount = n } } case "vrc_log": if key == "patterns" { cfg.VrcLog.LogPatterns = parseList(val) } case "staff": if key == "names" { cfg.VrcLog.StaffNames = parseList(val) } case "guest": switch key { case "names": cfg.VrcLog.GuestNames = parseList(val) case "file": cfg.VrcLog.GuestFile = val } } } if cfg.VrcLog.GuestFile != "" { if !filepath.IsAbs(cfg.VrcLog.GuestFile) { cfg.VrcLog.GuestFile = filepath.Join(common.RootDir(), cfg.VrcLog.GuestFile) } if names, err := loadLines(cfg.VrcLog.GuestFile); err == nil { cfg.VrcLog.GuestNames = names } } return cfg, scanner.Err() } func parseList(value string) []string { value = strings.TrimSpace(value) value = strings.TrimPrefix(value, "[") value = strings.TrimSuffix(value, "]") if value == "" { return nil } parts := strings.Split(value, ",") out := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(strings.Trim(part, "\"'")) if part != "" { out = append(out, part) } } return out } func loadLines(path string) ([]string, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } lines := strings.Split(string(data), "\n") out := make([]string, 0, len(lines)) for _, line := range lines { line = strings.TrimSpace(strings.TrimPrefix(line, "\ufeff")) if line != "" && !strings.HasPrefix(line, "#") { out = append(out, line) } } return out, nil }