Replaces gopkg.in/yaml.v3 with github.com/BurntSushi/toml. Updates
struct tags, default config path, Dockerfile CMD, README, and ships
config.example.toml in place of the YAML example. ${ENV_VAR}
expansion still runs on the raw file before parsing, so the behavior
is unchanged.
133 lines
3.5 KiB
Go
133 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"regexp"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
)
|
|
|
|
var envBracketRe = regexp.MustCompile(`\$\{([^}]+)\}`)
|
|
|
|
type Config struct {
|
|
Matrix MatrixConfig `toml:"matrix"`
|
|
Services ServicesConfig `toml:"services"`
|
|
}
|
|
|
|
type MatrixConfig struct {
|
|
Homeserver string `toml:"homeserver"`
|
|
UserID string `toml:"user_id"`
|
|
Password string `toml:"password"`
|
|
PickleKey string `toml:"pickle_key"`
|
|
DisplayName string `toml:"display_name"`
|
|
DataDir string `toml:"data_dir"`
|
|
CommandPrefix string `toml:"command_prefix"`
|
|
// AllowedRooms is the set of room IDs the bot listens for commands in.
|
|
// Messages in any other room are ignored.
|
|
AllowedRooms []string `toml:"allowed_rooms"`
|
|
}
|
|
|
|
type ServicesConfig struct {
|
|
Radarr *ArrConfig `toml:"radarr"`
|
|
Sonarr *ArrConfig `toml:"sonarr"`
|
|
Lidarr *ArrConfig `toml:"lidarr"`
|
|
}
|
|
|
|
type ArrConfig struct {
|
|
URL string `toml:"url"`
|
|
APIKey string `toml:"api_key"`
|
|
QualityProfileID int `toml:"quality_profile_id"`
|
|
RootFolder string `toml:"root_folder"`
|
|
// MetadataProfileID is required by Lidarr; ignored by Radarr/Sonarr.
|
|
MetadataProfileID int `toml:"metadata_profile_id"`
|
|
}
|
|
|
|
func Load(path string) (*Config, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config: %w", err)
|
|
}
|
|
|
|
expanded := envBracketRe.ReplaceAllStringFunc(string(data), func(match string) string {
|
|
varName := match[2 : len(match)-1]
|
|
val := os.Getenv(varName)
|
|
if val == "" {
|
|
slog.Warn("config: env var referenced but not set", "var", varName)
|
|
}
|
|
return val
|
|
})
|
|
|
|
var cfg Config
|
|
if _, err := toml.Decode(expanded, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config: %w", err)
|
|
}
|
|
|
|
cfg.applyDefaults()
|
|
|
|
if err := cfg.validate(); err != nil {
|
|
return nil, fmt.Errorf("validate config: %w", err)
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
func (c *Config) validate() error {
|
|
if c.Matrix.Homeserver == "" {
|
|
return fmt.Errorf("matrix.homeserver is required")
|
|
}
|
|
if c.Matrix.UserID == "" {
|
|
return fmt.Errorf("matrix.user_id is required")
|
|
}
|
|
if c.Matrix.Password == "" {
|
|
return fmt.Errorf("matrix.password is required")
|
|
}
|
|
if len(c.Matrix.AllowedRooms) == 0 {
|
|
return fmt.Errorf("matrix.allowed_rooms must have at least one entry")
|
|
}
|
|
if c.Services.Radarr == nil && c.Services.Sonarr == nil && c.Services.Lidarr == nil {
|
|
return fmt.Errorf("at least one of services.radarr/sonarr/lidarr must be configured")
|
|
}
|
|
for name, svc := range map[string]*ArrConfig{
|
|
"radarr": c.Services.Radarr,
|
|
"sonarr": c.Services.Sonarr,
|
|
"lidarr": c.Services.Lidarr,
|
|
} {
|
|
if svc == nil {
|
|
continue
|
|
}
|
|
if svc.URL == "" {
|
|
return fmt.Errorf("services.%s.url is required when service is set", name)
|
|
}
|
|
if svc.APIKey == "" {
|
|
return fmt.Errorf("services.%s.api_key is required when service is set", name)
|
|
}
|
|
if svc.RootFolder == "" {
|
|
return fmt.Errorf("services.%s.root_folder is required when service is set", name)
|
|
}
|
|
if svc.QualityProfileID == 0 {
|
|
return fmt.Errorf("services.%s.quality_profile_id is required when service is set", name)
|
|
}
|
|
}
|
|
if c.Services.Lidarr != nil && c.Services.Lidarr.MetadataProfileID == 0 {
|
|
return fmt.Errorf("services.lidarr.metadata_profile_id is required when lidarr is set")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) applyDefaults() {
|
|
if c.Matrix.DataDir == "" {
|
|
c.Matrix.DataDir = "./data"
|
|
}
|
|
if c.Matrix.DisplayName == "" {
|
|
c.Matrix.DisplayName = "Bellhop"
|
|
}
|
|
if c.Matrix.PickleKey == "" {
|
|
c.Matrix.PickleKey = "bellhop_pickle_key"
|
|
}
|
|
if c.Matrix.CommandPrefix == "" {
|
|
c.Matrix.CommandPrefix = "!"
|
|
}
|
|
}
|