package config import ( "bytes" "io/ioutil" "strings" "sync" "time" "github.com/fsnotify/fsnotify" prefixed "github.com/matterbridge/logrus-prefixed-formatter" log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) const ( EVENT_JOIN_LEAVE = "join_leave" EVENT_TOPIC_CHANGE = "topic_change" EVENT_FAILURE = "failure" EVENT_FILE_FAILURE_SIZE = "file_failure_size" EVENT_AVATAR_DOWNLOAD = "avatar_download" EVENT_REJOIN_CHANNELS = "rejoin_channels" EVENT_USER_ACTION = "user_action" EVENT_MSG_DELETE = "msg_delete" EVENT_API_CONNECTED = "api_connected" EVENT_USER_TYPING = "user_typing" ) type Message struct { Text string `json:"text"` Channel string `json:"channel"` Username string `json:"username"` UserID string `json:"userid"` // userid on the bridge Avatar string `json:"avatar"` Account string `json:"account"` Event string `json:"event"` Protocol string `json:"protocol"` Gateway string `json:"gateway"` ParentID string `json:"parent_id"` Timestamp time.Time `json:"timestamp"` ID string `json:"id"` Extra map[string][]interface{} } type FileInfo struct { Name string Data *[]byte Comment string URL string Size int64 Avatar bool SHA string } type ChannelInfo struct { Name string Account string Direction string ID string SameChannel map[string]bool Options ChannelOptions } type Protocol struct { AuthCode string // steam BindAddress string // mattermost, slack // DEPRECATED Buffer int // api Charset string // irc ColorNicks bool // only irc for now Debug bool // general DebugLevel int // only for irc now EditSuffix string // mattermost, slack, discord, telegram, gitter EditDisable bool // mattermost, slack, discord, telegram, gitter IconURL string // mattermost, slack IgnoreNicks string // all protocols IgnoreMessages string // all protocols Jid string // xmpp Label string // all protocols Login string // mattermost, matrix MediaDownloadBlackList []string MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server. MediaDownloadSize int // all protocols MediaServerDownload string MediaServerUpload string MessageDelay int // IRC, time in millisecond to wait between messages MessageFormat string // telegram 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 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 NoSendJoinPart bool // all protocols NoTLS bool // mattermost Password string // IRC,mattermost,XMPP,matrix PrefixMessagesWithNick bool // mattemost, slack PreserveThreading bool // slack Protocol string // all protocols QuoteDisable bool // telegram QuoteFormat string // telegram RejoinDelay int // IRC ReplaceMessages [][]string // all protocols ReplaceNicks [][]string // all protocols RemoteNickFormat string // all protocols Server string // IRC,mattermost,XMPP,discord ShowJoinPart bool // all protocols ShowTopicChange bool // slack ShowUserTyping bool // slack ShowEmbeds bool // discord SkipTLSVerify bool // IRC, mattermost StripNick bool // all protocols Team string // mattermost Token string // gitter, slack, discord, api Topic string // zulip URL string // mattermost, slack // DEPRECATED UseAPI bool // mattermost, slack UseSASL bool // IRC UseTLS bool // IRC UseFirstName bool // telegram UseUserName bool // discord UseInsecureURL bool // telegram WebhookBindAddress string // mattermost, slack WebhookURL string // mattermost, slack WebhookUse string // mattermost, slack, discord } type ChannelOptions struct { Key string // irc, xmpp WebhookURL string // discord } type Bridge struct { Account string Channel string Options ChannelOptions SameChannel bool } type Gateway struct { Name string Enable bool In []Bridge Out []Bridge InOut []Bridge } type SameChannelGateway struct { Name string Enable bool Channels []string Accounts []string } type ConfigValues struct { Api map[string]Protocol Irc map[string]Protocol Mattermost map[string]Protocol Matrix map[string]Protocol Slack map[string]Protocol SlackLegacy map[string]Protocol Steam map[string]Protocol Gitter map[string]Protocol Xmpp map[string]Protocol Discord map[string]Protocol Telegram map[string]Protocol Rocketchat map[string]Protocol Sshchat map[string]Protocol Zulip map[string]Protocol General Protocol Gateway []Gateway SameChannelGateway []SameChannelGateway } type Config struct { v *viper.Viper *ConfigValues sync.RWMutex } func NewConfig(cfgfile string) *Config { log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false}) flog := log.WithFields(log.Fields{"prefix": "config"}) viper.SetConfigFile(cfgfile) input, err := getFileContents(cfgfile) if err != nil { log.Fatal(err) } mycfg := NewConfigFromString(input) if mycfg.ConfigValues.General.MediaDownloadSize == 0 { mycfg.ConfigValues.General.MediaDownloadSize = 1000000 } viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { flog.Println("Config file changed:", e.Name) }) return mycfg } func getFileContents(filename string) ([]byte, error) { input, err := ioutil.ReadFile(filename) if err != nil { log.Fatal(err) return []byte(nil), err } return input, nil } func NewConfigFromString(input []byte) *Config { var cfg ConfigValues viper.SetConfigType("toml") viper.SetEnvPrefix("matterbridge") viper.AddConfigPath(".") viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) viper.AutomaticEnv() err := viper.ReadConfig(bytes.NewBuffer(input)) if err != nil { log.Fatal(err) } err = viper.Unmarshal(&cfg) if err != nil { log.Fatal(err) } mycfg := new(Config) mycfg.v = viper.GetViper() mycfg.ConfigValues = &cfg return mycfg } func (c *Config) GetBool(key string) bool { c.RLock() defer c.RUnlock() // log.Debugf("getting bool %s = %#v", key, c.v.GetBool(key)) return c.v.GetBool(key) } func (c *Config) GetInt(key string) int { c.RLock() defer c.RUnlock() // log.Debugf("getting int %s = %d", key, c.v.GetInt(key)) return c.v.GetInt(key) } func (c *Config) GetString(key string) string { c.RLock() defer c.RUnlock() // log.Debugf("getting String %s = %s", key, c.v.GetString(key)) return c.v.GetString(key) } func (c *Config) GetStringSlice(key string) []string { c.RLock() defer c.RUnlock() // log.Debugf("getting StringSlice %s = %#v", key, c.v.GetStringSlice(key)) return c.v.GetStringSlice(key) } func (c *Config) GetStringSlice2D(key string) [][]string { c.RLock() defer c.RUnlock() result := [][]string{} if res, ok := c.v.Get(key).([]interface{}); ok { for _, entry := range res { result2 := []string{} for _, entry2 := range entry.([]interface{}) { result2 = append(result2, entry2.(string)) } result = append(result, result2) } return result } return result } func GetIconURL(msg *Message, iconURL string) string { info := strings.Split(msg.Account, ".") protocol := info[0] name := info[1] iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1) iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1) iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1) return iconURL }