2016-08-14 19:48:51 +00:00
|
|
|
package config
|
2016-07-11 19:23:33 +00:00
|
|
|
|
|
|
|
import (
|
2018-03-04 23:30:46 +00:00
|
|
|
"bytes"
|
2018-11-07 21:32:12 +00:00
|
|
|
"io/ioutil"
|
2016-10-30 21:32:29 +00:00
|
|
|
"strings"
|
2018-03-04 22:52:14 +00:00
|
|
|
"sync"
|
2017-02-18 22:10:22 +00:00
|
|
|
"time"
|
2018-06-08 20:30:35 +00:00
|
|
|
|
|
|
|
"github.com/fsnotify/fsnotify"
|
2018-12-26 14:16:09 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2018-06-08 20:30:35 +00:00
|
|
|
"github.com/spf13/viper"
|
2016-07-11 19:23:33 +00:00
|
|
|
)
|
|
|
|
|
2016-11-14 21:53:06 +00:00
|
|
|
const (
|
2019-01-18 17:35:31 +00:00
|
|
|
EventJoinLeave = "join_leave"
|
|
|
|
EventTopicChange = "topic_change"
|
|
|
|
EventFailure = "failure"
|
|
|
|
EventFileFailureSize = "file_failure_size"
|
|
|
|
EventAvatarDownload = "avatar_download"
|
|
|
|
EventRejoinChannels = "rejoin_channels"
|
|
|
|
EventUserAction = "user_action"
|
|
|
|
EventMsgDelete = "msg_delete"
|
|
|
|
EventAPIConnected = "api_connected"
|
|
|
|
EventUserTyping = "user_typing"
|
|
|
|
EventGetChannelMembers = "get_channel_members"
|
2016-11-14 21:53:06 +00:00
|
|
|
)
|
|
|
|
|
2016-08-14 19:48:51 +00:00
|
|
|
type Message struct {
|
2017-06-05 21:18:13 +00:00
|
|
|
Text string `json:"text"`
|
|
|
|
Channel string `json:"channel"`
|
|
|
|
Username string `json:"username"`
|
2017-06-18 13:44:54 +00:00
|
|
|
UserID string `json:"userid"` // userid on the bridge
|
2017-06-05 21:18:13 +00:00
|
|
|
Avatar string `json:"avatar"`
|
|
|
|
Account string `json:"account"`
|
|
|
|
Event string `json:"event"`
|
|
|
|
Protocol string `json:"protocol"`
|
|
|
|
Gateway string `json:"gateway"`
|
2018-11-07 08:14:31 +00:00
|
|
|
ParentID string `json:"parent_id"`
|
2017-06-05 21:18:13 +00:00
|
|
|
Timestamp time.Time `json:"timestamp"`
|
2017-08-27 22:33:17 +00:00
|
|
|
ID string `json:"id"`
|
2017-09-21 20:35:21 +00:00
|
|
|
Extra map[string][]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type FileInfo struct {
|
2017-11-12 23:20:31 +00:00
|
|
|
Name string
|
|
|
|
Data *[]byte
|
|
|
|
Comment string
|
2017-11-24 21:36:19 +00:00
|
|
|
URL string
|
2018-02-03 00:11:11 +00:00
|
|
|
Size int64
|
2018-02-14 21:20:27 +00:00
|
|
|
Avatar bool
|
2018-02-15 22:18:58 +00:00
|
|
|
SHA string
|
2016-09-18 17:21:15 +00:00
|
|
|
}
|
|
|
|
|
2017-03-28 21:56:58 +00:00
|
|
|
type ChannelInfo struct {
|
2017-04-01 15:24:19 +00:00
|
|
|
Name string
|
|
|
|
Account string
|
|
|
|
Direction string
|
|
|
|
ID string
|
|
|
|
SameChannel map[string]bool
|
|
|
|
Options ChannelOptions
|
2017-03-28 21:56:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-18 17:35:31 +00:00
|
|
|
type ChannelMember struct {
|
|
|
|
Username string
|
|
|
|
Nick string
|
|
|
|
UserID string
|
|
|
|
ChannelID string
|
|
|
|
ChannelName string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ChannelMembers []ChannelMember
|
|
|
|
|
2016-09-18 17:21:15 +00:00
|
|
|
type Protocol struct {
|
2017-11-24 21:36:19 +00:00
|
|
|
AuthCode string // steam
|
|
|
|
BindAddress string // mattermost, slack // DEPRECATED
|
|
|
|
Buffer int // api
|
|
|
|
Charset string // irc
|
2018-05-11 21:02:43 +00:00
|
|
|
ColorNicks bool // only irc for now
|
2018-01-26 20:54:09 +00:00
|
|
|
Debug bool // general
|
2018-02-22 17:56:21 +00:00
|
|
|
DebugLevel int // only for irc now
|
2017-11-24 21:36:19 +00:00
|
|
|
EditSuffix string // mattermost, slack, discord, telegram, gitter
|
|
|
|
EditDisable bool // mattermost, slack, discord, telegram, gitter
|
|
|
|
IconURL string // mattermost, slack
|
2018-11-25 09:35:35 +00:00
|
|
|
IgnoreFailureOnStart bool // general
|
2017-11-24 21:36:19 +00:00
|
|
|
IgnoreNicks string // all protocols
|
|
|
|
IgnoreMessages string // all protocols
|
|
|
|
Jid string // xmpp
|
2018-02-20 17:57:46 +00:00
|
|
|
Label string // all protocols
|
2017-11-24 21:36:19 +00:00
|
|
|
Login string // mattermost, matrix
|
2018-06-09 12:35:02 +00:00
|
|
|
MediaDownloadBlackList []string
|
|
|
|
MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
|
2017-12-19 22:15:03 +00:00
|
|
|
MediaDownloadSize int // all protocols
|
2017-11-24 21:36:19 +00:00
|
|
|
MediaServerDownload string
|
|
|
|
MediaServerUpload string
|
2019-02-26 23:41:50 +00:00
|
|
|
MediaConvertWebPToPNG bool // telegram
|
2017-11-20 22:27:27 +00:00
|
|
|
MessageDelay int // IRC, time in millisecond to wait between messages
|
|
|
|
MessageFormat string // telegram
|
2017-11-24 22:27:13 +00:00
|
|
|
MessageLength int // IRC, max length of a message allowed
|
|
|
|
MessageQueue int // IRC, size of message queue for flood control
|
|
|
|
MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
|
2017-11-15 22:32:49 +00:00
|
|
|
Muc string // xmpp
|
|
|
|
Name string // all protocols
|
|
|
|
Nick string // all protocols
|
|
|
|
NickFormatter string // mattermost, slack
|
|
|
|
NickServNick string // IRC
|
|
|
|
NickServUsername string // IRC
|
|
|
|
NickServPassword string // IRC
|
|
|
|
NicksPerRow int // mattermost, slack
|
|
|
|
NoHomeServerSuffix bool // matrix
|
2018-03-06 20:34:55 +00:00
|
|
|
NoSendJoinPart bool // all protocols
|
2017-11-15 22:32:49 +00:00
|
|
|
NoTLS bool // mattermost
|
|
|
|
Password string // IRC,mattermost,XMPP,matrix
|
|
|
|
PrefixMessagesWithNick bool // mattemost, slack
|
2018-11-07 08:14:31 +00:00
|
|
|
PreserveThreading bool // slack
|
2017-11-20 22:27:27 +00:00
|
|
|
Protocol string // all protocols
|
2018-04-17 21:26:41 +00:00
|
|
|
QuoteDisable bool // telegram
|
2018-05-11 18:59:15 +00:00
|
|
|
QuoteFormat string // telegram
|
2020-01-29 23:02:33 +00:00
|
|
|
QuoteLengthLimit int // telegram
|
2017-12-22 23:11:30 +00:00
|
|
|
RejoinDelay int // IRC
|
2017-11-20 22:27:27 +00:00
|
|
|
ReplaceMessages [][]string // all protocols
|
|
|
|
ReplaceNicks [][]string // all protocols
|
2017-11-15 22:32:49 +00:00
|
|
|
RemoteNickFormat string // all protocols
|
2019-04-18 21:56:05 +00:00
|
|
|
RunCommands []string // IRC
|
2017-11-15 22:32:49 +00:00
|
|
|
Server string // IRC,mattermost,XMPP,discord
|
|
|
|
ShowJoinPart bool // all protocols
|
2018-02-02 20:04:43 +00:00
|
|
|
ShowTopicChange bool // slack
|
2018-11-08 19:45:40 +00:00
|
|
|
ShowUserTyping bool // slack
|
2017-11-15 22:32:49 +00:00
|
|
|
ShowEmbeds bool // discord
|
|
|
|
SkipTLSVerify bool // IRC, mattermost
|
2019-06-16 14:23:50 +00:00
|
|
|
SkipVersionCheck bool // mattermost
|
2017-11-15 22:32:49 +00:00
|
|
|
StripNick bool // all protocols
|
2018-11-26 09:47:04 +00:00
|
|
|
SyncTopic bool // slack
|
2019-02-23 15:39:44 +00:00
|
|
|
TengoModifyMessage string // general
|
2019-08-26 19:00:31 +00:00
|
|
|
Team string // mattermost, keybase
|
2017-11-15 22:32:49 +00:00
|
|
|
Token string // gitter, slack, discord, api
|
2018-05-07 19:35:48 +00:00
|
|
|
Topic string // zulip
|
2017-11-15 22:32:49 +00:00
|
|
|
URL string // mattermost, slack // DEPRECATED
|
|
|
|
UseAPI bool // mattermost, slack
|
|
|
|
UseSASL bool // IRC
|
|
|
|
UseTLS bool // IRC
|
2019-02-22 13:28:27 +00:00
|
|
|
UseDiscriminator bool // discord
|
2017-11-15 22:32:49 +00:00
|
|
|
UseFirstName bool // telegram
|
|
|
|
UseUserName bool // discord
|
|
|
|
UseInsecureURL bool // telegram
|
2019-04-18 21:56:05 +00:00
|
|
|
VerboseJoinPart bool // IRC
|
2017-11-15 22:32:49 +00:00
|
|
|
WebhookBindAddress string // mattermost, slack
|
|
|
|
WebhookURL string // mattermost, slack
|
2016-09-18 17:21:15 +00:00
|
|
|
}
|
|
|
|
|
2017-01-04 13:10:35 +00:00
|
|
|
type ChannelOptions struct {
|
2018-06-18 20:55:45 +00:00
|
|
|
Key string // irc, xmpp
|
2017-08-12 12:51:41 +00:00
|
|
|
WebhookURL string // discord
|
2019-02-17 20:50:05 +00:00
|
|
|
Topic string // zulip
|
2017-01-04 13:10:35 +00:00
|
|
|
}
|
|
|
|
|
2016-09-18 17:21:15 +00:00
|
|
|
type Bridge struct {
|
2017-04-01 15:24:19 +00:00
|
|
|
Account string
|
|
|
|
Channel string
|
|
|
|
Options ChannelOptions
|
|
|
|
SameChannel bool
|
2016-09-18 17:21:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Gateway struct {
|
|
|
|
Name string
|
|
|
|
Enable bool
|
|
|
|
In []Bridge
|
|
|
|
Out []Bridge
|
2016-11-20 22:01:44 +00:00
|
|
|
InOut []Bridge
|
2016-08-14 19:48:51 +00:00
|
|
|
}
|
|
|
|
|
2019-04-08 18:58:21 +00:00
|
|
|
type Tengo struct {
|
2019-04-19 16:27:31 +00:00
|
|
|
InMessage string
|
2019-04-08 18:58:21 +00:00
|
|
|
Message string
|
|
|
|
RemoteNickFormat string
|
2019-04-19 16:27:31 +00:00
|
|
|
OutMessage string
|
2019-04-08 18:58:21 +00:00
|
|
|
}
|
|
|
|
|
2016-09-30 21:19:47 +00:00
|
|
|
type SameChannelGateway struct {
|
|
|
|
Name string
|
|
|
|
Enable bool
|
|
|
|
Channels []string
|
|
|
|
Accounts []string
|
|
|
|
}
|
|
|
|
|
2018-11-15 19:43:43 +00:00
|
|
|
type BridgeValues struct {
|
|
|
|
API map[string]Protocol
|
|
|
|
IRC map[string]Protocol
|
2016-09-30 21:19:47 +00:00
|
|
|
Mattermost map[string]Protocol
|
2017-02-19 23:49:27 +00:00
|
|
|
Matrix map[string]Protocol
|
2016-09-30 21:19:47 +00:00
|
|
|
Slack map[string]Protocol
|
2018-11-13 19:51:19 +00:00
|
|
|
SlackLegacy map[string]Protocol
|
2017-06-21 23:02:05 +00:00
|
|
|
Steam map[string]Protocol
|
2016-09-30 21:19:47 +00:00
|
|
|
Gitter map[string]Protocol
|
2018-11-15 19:43:43 +00:00
|
|
|
XMPP map[string]Protocol
|
2016-09-30 21:19:47 +00:00
|
|
|
Discord map[string]Protocol
|
2016-11-15 22:15:57 +00:00
|
|
|
Telegram map[string]Protocol
|
2016-12-02 23:10:29 +00:00
|
|
|
Rocketchat map[string]Protocol
|
2018-11-15 19:43:43 +00:00
|
|
|
SSHChat map[string]Protocol
|
2019-02-21 19:28:13 +00:00
|
|
|
WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
|
2018-05-07 19:35:48 +00:00
|
|
|
Zulip map[string]Protocol
|
2019-08-26 19:00:31 +00:00
|
|
|
Keybase map[string]Protocol
|
2016-11-20 22:33:41 +00:00
|
|
|
General Protocol
|
2019-04-08 18:58:21 +00:00
|
|
|
Tengo Tengo
|
2016-09-30 21:19:47 +00:00
|
|
|
Gateway []Gateway
|
|
|
|
SameChannelGateway []SameChannelGateway
|
2016-07-11 19:23:33 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
type Config interface {
|
2019-09-09 21:48:00 +00:00
|
|
|
Viper() *viper.Viper
|
2018-11-15 19:43:43 +00:00
|
|
|
BridgeValues() *BridgeValues
|
2018-11-13 22:30:56 +00:00
|
|
|
GetBool(key string) (bool, bool)
|
|
|
|
GetInt(key string) (int, bool)
|
|
|
|
GetString(key string) (string, bool)
|
|
|
|
GetStringSlice(key string) ([]string, bool)
|
|
|
|
GetStringSlice2D(key string) ([][]string, bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
type config struct {
|
2018-03-04 22:52:14 +00:00
|
|
|
sync.RWMutex
|
2018-11-13 22:30:56 +00:00
|
|
|
|
2019-02-23 21:51:27 +00:00
|
|
|
logger *logrus.Entry
|
|
|
|
v *viper.Viper
|
|
|
|
cv *BridgeValues
|
2017-12-19 22:15:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 21:51:27 +00:00
|
|
|
// NewConfig instantiates a new configuration based on the specified configuration file path.
|
|
|
|
func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
|
|
|
|
logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"})
|
|
|
|
|
2018-05-01 20:23:37 +00:00
|
|
|
viper.SetConfigFile(cfgfile)
|
2019-02-23 21:51:27 +00:00
|
|
|
input, err := ioutil.ReadFile(cfgfile)
|
2018-03-04 22:52:14 +00:00
|
|
|
if err != nil {
|
2019-02-23 21:51:27 +00:00
|
|
|
logger.Fatalf("Failed to read configuration file: %#v", err)
|
2016-07-11 19:23:33 +00:00
|
|
|
}
|
2019-02-23 21:51:27 +00:00
|
|
|
|
|
|
|
mycfg := newConfigFromString(logger, input)
|
2018-11-13 22:30:56 +00:00
|
|
|
if mycfg.cv.General.MediaDownloadSize == 0 {
|
|
|
|
mycfg.cv.General.MediaDownloadSize = 1000000
|
2017-12-19 22:44:13 +00:00
|
|
|
}
|
2018-05-01 20:23:37 +00:00
|
|
|
viper.WatchConfig()
|
|
|
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
2019-02-23 21:51:27 +00:00
|
|
|
logger.Println("Config file changed:", e.Name)
|
2018-05-01 20:23:37 +00:00
|
|
|
})
|
2018-03-04 22:52:14 +00:00
|
|
|
return mycfg
|
|
|
|
}
|
|
|
|
|
2019-02-23 21:51:27 +00:00
|
|
|
// NewConfigFromString instantiates a new configuration based on the specified string.
|
|
|
|
func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config {
|
|
|
|
logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"})
|
|
|
|
return newConfigFromString(logger, input)
|
2018-11-13 22:30:56 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 21:51:27 +00:00
|
|
|
func newConfigFromString(logger *logrus.Entry, input []byte) *config {
|
2018-03-04 23:30:46 +00:00
|
|
|
viper.SetConfigType("toml")
|
2018-11-07 21:32:12 +00:00
|
|
|
viper.SetEnvPrefix("matterbridge")
|
|
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
|
|
|
viper.AutomaticEnv()
|
2019-02-23 21:51:27 +00:00
|
|
|
|
|
|
|
if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
|
2019-06-01 20:42:10 +00:00
|
|
|
logger.Fatalf("Failed to parse the configuration: %s", err)
|
2018-03-04 23:30:46 +00:00
|
|
|
}
|
2018-11-13 22:30:56 +00:00
|
|
|
|
2018-11-15 19:43:43 +00:00
|
|
|
cfg := &BridgeValues{}
|
2019-02-23 21:51:27 +00:00
|
|
|
if err := viper.Unmarshal(cfg); err != nil {
|
2019-06-01 20:42:10 +00:00
|
|
|
logger.Fatalf("Failed to load the configuration: %s", err)
|
2018-03-04 23:30:46 +00:00
|
|
|
}
|
2018-11-13 22:30:56 +00:00
|
|
|
return &config{
|
2019-02-23 21:51:27 +00:00
|
|
|
logger: logger,
|
|
|
|
v: viper.GetViper(),
|
|
|
|
cv: cfg,
|
2018-11-13 22:30:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 19:43:43 +00:00
|
|
|
func (c *config) BridgeValues() *BridgeValues {
|
2018-11-13 22:30:56 +00:00
|
|
|
return c.cv
|
2018-03-04 23:30:46 +00:00
|
|
|
}
|
|
|
|
|
2019-09-09 21:48:00 +00:00
|
|
|
func (c *config) Viper() *viper.Viper {
|
|
|
|
return c.v
|
|
|
|
}
|
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
func (c *config) GetBool(key string) (bool, bool) {
|
2018-03-04 22:52:14 +00:00
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
2018-11-13 22:30:56 +00:00
|
|
|
return c.v.GetBool(key), c.v.IsSet(key)
|
2018-03-04 22:52:14 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
func (c *config) GetInt(key string) (int, bool) {
|
2018-03-04 22:52:14 +00:00
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
2018-11-13 22:30:56 +00:00
|
|
|
return c.v.GetInt(key), c.v.IsSet(key)
|
2018-03-04 22:52:14 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
func (c *config) GetString(key string) (string, bool) {
|
2018-03-04 22:52:14 +00:00
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
2018-11-13 22:30:56 +00:00
|
|
|
return c.v.GetString(key), c.v.IsSet(key)
|
2018-03-04 22:52:14 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
func (c *config) GetStringSlice(key string) ([]string, bool) {
|
2018-03-04 22:52:14 +00:00
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
2018-11-13 22:30:56 +00:00
|
|
|
return c.v.GetStringSlice(key), c.v.IsSet(key)
|
2016-07-11 19:23:33 +00:00
|
|
|
}
|
2016-10-30 21:32:29 +00:00
|
|
|
|
2018-11-13 22:30:56 +00:00
|
|
|
func (c *config) GetStringSlice2D(key string) ([][]string, bool) {
|
2018-03-04 22:52:14 +00:00
|
|
|
c.RLock()
|
|
|
|
defer c.RUnlock()
|
2019-02-23 21:51:27 +00:00
|
|
|
|
|
|
|
res, ok := c.v.Get(key).([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
var result [][]string
|
|
|
|
for _, entry := range res {
|
|
|
|
result2 := []string{}
|
|
|
|
for _, entry2 := range entry.([]interface{}) {
|
|
|
|
result2 = append(result2, entry2.(string))
|
2018-04-21 21:26:39 +00:00
|
|
|
}
|
2019-02-23 21:51:27 +00:00
|
|
|
result = append(result, result2)
|
2016-10-30 21:32:29 +00:00
|
|
|
}
|
2019-02-23 21:51:27 +00:00
|
|
|
return result, true
|
2016-10-30 21:32:29 +00:00
|
|
|
}
|
2016-11-05 00:11:28 +00:00
|
|
|
|
2018-03-04 22:52:14 +00:00
|
|
|
func GetIconURL(msg *Message, iconURL string) string {
|
2016-11-13 22:06:37 +00:00
|
|
|
info := strings.Split(msg.Account, ".")
|
|
|
|
protocol := info[0]
|
|
|
|
name := info[1]
|
2016-11-05 00:11:28 +00:00
|
|
|
iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1)
|
2016-11-13 22:06:37 +00:00
|
|
|
iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1)
|
|
|
|
iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
|
2016-11-05 00:11:28 +00:00
|
|
|
return iconURL
|
|
|
|
}
|
2018-11-13 22:30:56 +00:00
|
|
|
|
|
|
|
type TestConfig struct {
|
|
|
|
Config
|
|
|
|
|
|
|
|
Overrides map[string]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TestConfig) GetBool(key string) (bool, bool) {
|
|
|
|
val, ok := c.Overrides[key]
|
|
|
|
if ok {
|
|
|
|
return val.(bool), true
|
|
|
|
}
|
|
|
|
return c.Config.GetBool(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TestConfig) GetInt(key string) (int, bool) {
|
|
|
|
if val, ok := c.Overrides[key]; ok {
|
|
|
|
return val.(int), true
|
|
|
|
}
|
|
|
|
return c.Config.GetInt(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TestConfig) GetString(key string) (string, bool) {
|
|
|
|
if val, ok := c.Overrides[key]; ok {
|
|
|
|
return val.(string), true
|
|
|
|
}
|
|
|
|
return c.Config.GetString(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TestConfig) GetStringSlice(key string) ([]string, bool) {
|
|
|
|
if val, ok := c.Overrides[key]; ok {
|
|
|
|
return val.([]string), true
|
|
|
|
}
|
|
|
|
return c.Config.GetStringSlice(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TestConfig) GetStringSlice2D(key string) ([][]string, bool) {
|
|
|
|
if val, ok := c.Overrides[key]; ok {
|
|
|
|
return val.([][]string), true
|
|
|
|
}
|
|
|
|
return c.Config.GetStringSlice2D(key)
|
|
|
|
}
|