156 lines
4.2 KiB
Go
156 lines
4.2 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/pkg/errors"
|
|
"io/ioutil"
|
|
|
|
"github.com/santhosh-tekuri/jsonschema"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
// Config is for top-level struct for config
|
|
type Config struct {
|
|
Telegram TelegramConfig `yaml:":telegram"`
|
|
XMPP XMPPConfig `yaml:":xmpp"`
|
|
}
|
|
|
|
// XMPPConfig is for :xmpp: subtree
|
|
type XMPPConfig struct {
|
|
Loglevel string `yaml:":loglevel"`
|
|
Jid string `yaml:":jid"`
|
|
Host string `yaml:":host"`
|
|
Port string `yaml:":port"`
|
|
Password string `yaml:":password"`
|
|
Db string `yaml:":db"`
|
|
}
|
|
|
|
// TelegramConfig is for :telegram: subtree
|
|
type TelegramConfig struct {
|
|
Loglevel string `yaml:":loglevel"`
|
|
Content TelegramContentConfig `yaml:":content"`
|
|
Verbosity uint8 `yaml:":tdlib_verbosity"`
|
|
Tdlib TelegramTdlibConfig `yaml:":tdlib"`
|
|
}
|
|
|
|
// TelegramContentConfig is for :content: subtree
|
|
type TelegramContentConfig struct {
|
|
Path string `yaml:":path"`
|
|
Link string `yaml:":link"`
|
|
Upload string `yaml:":upload"`
|
|
}
|
|
|
|
// TelegramTdlibConfig is for :tdlib: subtree
|
|
type TelegramTdlibConfig struct {
|
|
Datadir string `yaml:":datadir"`
|
|
Client TelegramTdlibClientConfig `yaml:":client"`
|
|
}
|
|
|
|
// TelegramTdlibClientConfig is for :client: subtree
|
|
type TelegramTdlibClientConfig struct {
|
|
APIID string `yaml:":api_id"`
|
|
APIHash string `yaml:":api_hash"`
|
|
DeviceModel string `yaml:":device_model"`
|
|
ApplicationVersion string `yaml:":application_version"`
|
|
UseChatInfoDatabase bool `yaml:":use_chat_info_database"`
|
|
UseSecretChats bool `yaml:":use_secret_chats"`
|
|
CatchTimeout int64 `yaml:":catch_timeout"`
|
|
}
|
|
|
|
// ReadConfig reads the specified config file, validates it and returns a struct
|
|
func ReadConfig(path string, schemaPath string) (Config, error) {
|
|
var config Config
|
|
|
|
file, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return config, errors.Wrap(err, "Can't open config file")
|
|
}
|
|
|
|
err = yaml.Unmarshal(file, &config)
|
|
if err != nil {
|
|
return config, errors.Wrap(err, "Error parsing config")
|
|
}
|
|
|
|
err = validateConfig(file, schemaPath)
|
|
if err != nil {
|
|
return config, errors.Wrap(err, "Validation error")
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func validateConfig(file []byte, schemaPath string) error {
|
|
schema, err := jsonschema.Compile(schemaPath)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Corrupted JSON schema")
|
|
}
|
|
|
|
var configGeneric interface{}
|
|
|
|
err = yaml.Unmarshal(file, &configGeneric)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Error re-parsing config")
|
|
}
|
|
|
|
configGeneric, err = convertToStringKeysRecursive(configGeneric, "")
|
|
if err != nil {
|
|
return errors.Wrap(err, "Config conversion error")
|
|
}
|
|
|
|
err = schema.ValidateInterface(configGeneric)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Config validation error")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// copied and adapted from https://github.com/docker/docker-ce/blob/de14285fad39e215ea9763b8b404a37686811b3f/components/cli/cli/compose/loader/loader.go#L330
|
|
func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
|
|
if mapping, ok := value.(map[interface{}]interface{}); ok {
|
|
dict := make(map[string]interface{})
|
|
for key, entry := range mapping {
|
|
str, ok := key.(string)
|
|
if !ok {
|
|
return nil, formatInvalidKeyError(keyPrefix, key)
|
|
}
|
|
var newKeyPrefix string
|
|
if keyPrefix == "" {
|
|
newKeyPrefix = str
|
|
} else {
|
|
newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str)
|
|
}
|
|
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dict[str] = convertedEntry
|
|
}
|
|
return dict, nil
|
|
}
|
|
if list, ok := value.([]interface{}); ok {
|
|
var convertedList []interface{}
|
|
for index, entry := range list {
|
|
newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index)
|
|
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
convertedList = append(convertedList, convertedEntry)
|
|
}
|
|
return convertedList, nil
|
|
}
|
|
|
|
return value, nil
|
|
}
|
|
|
|
func formatInvalidKeyError(keyPrefix string, key interface{}) error {
|
|
var location string
|
|
if keyPrefix == "" {
|
|
location = "at top level"
|
|
} else {
|
|
location = fmt.Sprintf("in %s", keyPrefix)
|
|
}
|
|
return errors.Errorf("Non-string key %s: %#v", location, key)
|
|
}
|