Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

commit 14ca102dbf8c19a463208912485684b7edfd1d0f Author: Wim <wim@42.be> Date: Sun Mar 04 23:52:14 2018 +0000 Use viper (github.com/spf13/viper) for configuration diff --git a/bridge/api/api.go b/bridge/api/api.go index b441526..54eea4a 100644 --- a/bridge/api/api.go +++ b/bridge/api/api.go @@ -157 +157 @@ import (  type Api struct {   Messages ring.Ring   sync.RWMutex - *config.BridgeConfig + *bridge.Config  }    type ApiMessage struct { @@ -2627 +2627 @@ type ApiMessage struct {   Gateway string `json:"gateway"`  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Api{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Api{Config: cfg}   e := echo.New()   e.HideBanner = true   e.HidePort = true   b.Messages = ring.Ring{} - b.Messages.SetCapacity(b.Config.Buffer) - if b.Config.Token != "" { + b.Messages.SetCapacity(b.GetInt("Buffer")) + if b.GetString("Token") != "" {   e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { - return key == b.Config.Token, nil + return key == b.GetString("Token"), nil   }))   }   e.GET("/api/messages", b.handleMessages)   e.GET("/api/stream", b.handleStream)   e.POST("/api/message", b.handlePostMessage)   go func() { - if b.Config.BindAddress == "" { + if b.GetString("BindAddress") == "" {   b.Log.Fatalf("No BindAddress configured.")   } - b.Log.Infof("Listening on %s", b.Config.BindAddress) - b.Log.Fatal(e.Start(b.Config.BindAddress)) + b.Log.Infof("Listening on %s", b.GetString("BindAddress")) + b.Log.Fatal(e.Start(b.GetString("BindAddress")))   }()   return b  } diff --git a/bridge/bridge.go b/bridge/bridge.go index 93e1cd8..8135cab 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -157 +156 @@ type Bridger interface {  }    type Bridge struct { - Config config.Protocol   Bridger   Name string   Account string @@ -2310 +2219 @@ type Bridge struct {   Channels map[string]config.ChannelInfo   Joined map[string]bool   Log *log.Entry + Config *config.Config + General *config.Protocol +} + +type Config struct { + // General *config.Protocol + Remote chan config.Message + Log *log.Entry + *Bridge  }    // Factory is the factory function to create a bridge -type Factory func(*config.BridgeConfig) Bridger +type Factory func(*Config) Bridger    func New(bridge *config.Bridge) *Bridge {   b := new(Bridge) @@ -593 +6742 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map   }   return nil  } + +func (b *Bridge) ReloadConfig() { + return +} + +func (b *Bridge) GetBool(key string) bool { + if b.Config.GetBool(b.Account+"."+key) != false { + return b.Config.GetBool(b.Account + "." + key) + } + return b.Config.GetBool("general." + key) +} + +func (b *Bridge) GetInt(key string) int { + if b.Config.GetInt(b.Account+"."+key) != 0 { + return b.Config.GetInt(b.Account + "." + key) + } + return b.Config.GetInt("general." + key) +} + +func (b *Bridge) GetString(key string) string { + if b.Config.GetString(b.Account+"."+key) != "" { + return b.Config.GetString(b.Account + "." + key) + } + return b.Config.GetString("general." + key) +} + +func (b *Bridge) GetStringSlice(key string) []string { + if len(b.Config.GetStringSlice(b.Account+"."+key)) != 0 { + return b.Config.GetStringSlice(b.Account + "." + key) + } + return b.Config.GetStringSlice("general." + key) +} + +func (b *Bridge) GetStringSlice2D(key string) [][]string { + if len(b.Config.GetStringSlice2D(b.Account+"."+key)) != 0 { + return b.Config.GetStringSlice2D(b.Account + "." + key) + } + return b.Config.GetStringSlice2D("general." + key) +} diff --git a/bridge/config/config.go b/bridge/config/config.go index 9e3f854..a89f9aa 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -111 +111 @@  package config    import ( - "github.com/BurntSushi/toml"   log "github.com/sirupsen/logrus" + "github.com/spf13/viper"   "os" - "reflect"   "strings" + "sync"   "time"  )   @@ -1417 +1417 @@ type SameChannelGateway struct {   Accounts []string  }   -type Config struct { +type ConfigValues struct {   Api map[string]Protocol   Irc map[string]Protocol   Mattermost map[string]Protocol @@ -15988 +15978 @@ type Config struct {   SameChannelGateway []SameChannelGateway  }   -type BridgeConfig struct { - Config Protocol - General *Protocol - Account string - Remote chan Message - Log *log.Entry +type Config struct { + v *viper.Viper + *ConfigValues + sync.RWMutex  }    func NewConfig(cfgfile string) *Config { - var cfg Config - if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil { + var cfg ConfigValues + viper.SetConfigType("toml") + viper.SetEnvPrefix("matterbridge") + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() + f, err := os.Open(cfgfile) + if err != nil {   log.Fatal(err)   } - fail := false - for k, v := range cfg.Mattermost { - res := Deprecated(v, "mattermost."+k) - if res { - fail = res - } - } - for k, v := range cfg.Slack { - res := Deprecated(v, "slack."+k) - if res { - fail = res - } - } - for k, v := range cfg.Rocketchat { - res := Deprecated(v, "rocketchat."+k) - if res { - fail = res - } + err = viper.ReadConfig(f) + if err != nil { + log.Fatal(err)   } - if fail { - log.Fatalf("Fix your config. Please see changelog for more information") + err = viper.Unmarshal(&cfg) + if err != nil { + log.Fatal("blah", err)   } + mycfg := new(Config) + mycfg.v = viper.GetViper()   if cfg.General.MediaDownloadSize == 0 {   cfg.General.MediaDownloadSize = 1000000   } - return &cfg + 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 OverrideCfgFromEnv(cfg *Config, protocol string, account string) { - var protoCfg Protocol - val := reflect.ValueOf(cfg).Elem() - // loop over the Config struct - for i := 0; i < val.NumField(); i++ { - typeField := val.Type().Field(i) - // look for the protocol map (both lowercase) - if strings.ToLower(typeField.Name) == protocol { - // get the Protocol struct from the map - data := val.Field(i).MapIndex(reflect.ValueOf(account)) - protoCfg = data.Interface().(Protocol) - protoStruct := reflect.ValueOf(&protoCfg).Elem() - // loop over the found protocol struct - for i := 0; i < protoStruct.NumField(); i++ { - typeField := protoStruct.Type().Field(i) - // build our environment key (eg MATTERBRIDGE_MATTERMOST_WORK_LOGIN) - key := "matterbridge_" + protocol + "_" + account + "_" + typeField.Name - key = strings.ToUpper(key) - // search the environment - res := os.Getenv(key) - // if it exists and the current field is a string - // then update the current field - if res != "" { - fieldVal := protoStruct.Field(i) - if fieldVal.Kind() == reflect.String { - log.WithFields(log.Fields{ - "prefix": "config", - }).Infof("overriding %s from env with %s\n", key, res) - fieldVal.Set(reflect.ValueOf(res)) - } - } - } - // update the map with the modified Protocol (cfg.Protocol[account] = Protocol) - val.Field(i).SetMapIndex(reflect.ValueOf(account), reflect.ValueOf(protoCfg)) - break - } +func (c *Config) GetStringSlice2D(key string) [][]string { + c.RLock() + defer c.RUnlock() + if res, ok := c.v.Get(key).([][]string); ok { + return res   } + // log.Debugf("getting StringSlice2D %s = %#v", key, c.v.Get(key)) + return [][]string{}  }   -func GetIconURL(msg *Message, cfg *Protocol) string { - iconURL := cfg.IconURL +func GetIconURL(msg *Message, iconURL string) string {   info := strings.Split(msg.Account, ".")   protocol := info[0]   name := info[1] @@ -24917 +2393 @@ func GetIconURL(msg *Message, cfg *Protocol) string {   iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)   return iconURL  } - -func Deprecated(cfg Protocol, account string) bool { - if cfg.BindAddress != "" { - log.Printf("ERROR: %s BindAddress is deprecated, you need to change it to WebhookBindAddress.", account) - } else if cfg.URL != "" { - log.Printf("ERROR: %s URL is deprecated, you need to change it to WebhookURL.", account) - } else if cfg.UseAPI { - log.Printf("ERROR: %s UseAPI is deprecated, it's enabled by default, please remove it from your config file.", account) - } else { - return false - } - return true - //log.Fatalf("ERROR: Fix your config: %s", account) -} diff --git a/bridge/discord/discord.go b/bridge/discord/discord.go index cfc6a81..ca4e418 100644 --- a/bridge/discord/discord.go +++ b/bridge/discord/discord.go @@ -2332 +2333 @@ type Bdiscord struct {   webhookToken string   channelInfoMap map[string]*config.ChannelInfo   sync.RWMutex - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Bdiscord{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Bdiscord{Config: cfg}   b.userMemberMap = make(map[string]*discordgo.Member)   b.channelInfoMap = make(map[string]*config.ChannelInfo) - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   b.Log.Debug("Configuring Discord Incoming Webhook") - b.webhookID, b.webhookToken = b.splitURL(b.Config.WebhookURL) + b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))   }   return b  }    func (b *Bdiscord) Connect() error {   var err error + var token string   b.Log.Info("Connecting") - if b.Config.WebhookURL == "" { + if b.GetString("WebhookURL") == "" {   b.Log.Info("Connecting using token")   } else {   b.Log.Info("Connecting using webhookurl (for posting) and token")   } - if !strings.HasPrefix(b.Config.Token, "Bot ") { - b.Config.Token = "Bot " + b.Config.Token + if !strings.HasPrefix(b.GetString("Token"), "Bot ") { + token = "Bot " + b.GetString("Token")   } - b.c, err = discordgo.New(b.Config.Token) + b.c, err = discordgo.New(token)   if err != nil {   return err   } @@ -717 +727 @@ func (b *Bdiscord) Connect() error {   }   b.Nick = userinfo.Username   for _, guild := range guilds { - if guild.Name == b.Config.Server { + if guild.Name == b.GetString("Server") {   b.Channels, err = b.c.GuildChannels(guild.ID)   b.guildID = guild.ID   if err != nil { @@ -837 +847 @@ func (b *Bdiscord) Connect() error {  }    func (b *Bdiscord) Disconnect() error { - return nil + return b.c.Close()  }    func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error { @@ -18613 +18713 @@ func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelet  }    func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { - if b.Config.EditDisable { + if b.GetBool("EditDisable") {   return   }   // only when message is actually edited   if m.Message.EditedTimestamp != "" {   b.Log.Debugf("Sending edit message") - m.Content = m.Content + b.Config.EditSuffix + m.Content = m.Content + b.GetString("EditSuffix")   b.messageCreate(s, (*discordgo.MessageCreate)(m))   }  } @@ -23614 +23714 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat   }     // set username - if !b.Config.UseUserName { + if !b.GetBool("UseUserName") {   rmsg.Username = b.getNick(m.Author)   } else {   rmsg.Username = m.Author.Username   }     // if we have embedded content add it to text - if b.Config.ShowEmbeds && m.Message.Embeds != nil { + if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil {   for _, embed := range m.Message.Embeds {   rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n"   } @@ -3677 +3687 @@ func (b *Bdiscord) splitURL(url string) (string, string) {    // useWebhook returns true if we have a webhook defined somewhere  func (b *Bdiscord) useWebhook() bool { - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   return true   }   for _, channel := range b.channelInfoMap { @@ -3808 +3818 @@ func (b *Bdiscord) useWebhook() bool {    // isWebhookID returns true if the specified id is used in a defined webhook  func (b *Bdiscord) isWebhookID(id string) bool { - if b.Config.WebhookURL != "" { - wID, _ := b.splitURL(b.Config.WebhookURL) + if b.GetString("WebhookURL") != "" { + wID, _ := b.splitURL(b.GetString("WebhookURL"))   if wID == id {   return true   } diff --git a/bridge/gitter/gitter.go b/bridge/gitter/gitter.go index b7f69b1..853640f 100644 --- a/bridge/gitter/gitter.go +++ b/bridge/gitter/gitter.go @@ -1417 +1417 @@ type Bgitter struct {   User *gitter.User   Users []gitter.User   Rooms []gitter.Room - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - return &Bgitter{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + return &Bgitter{Config: cfg}  }    func (b *Bgitter) Connect() error {   var err error   b.Log.Info("Connecting") - b.c = gitter.New(b.Config.Token) + b.c = gitter.New(b.GetString("Token"))   b.User, err = b.c.GetUser()   if err != nil {   return err diff --git a/bridge/irc/irc.go b/bridge/irc/irc.go index 5f89d29..8053bb4 100644 --- a/bridge/irc/irc.go +++ b/bridge/irc/irc.go @@ -2330 +2331 @@ import (  )    type Birc struct { - i *girc.Client - Nick string - names map[string][]string - connected chan struct{} - Local chan config.Message // local queue for flood control - FirstConnection bool - - *config.BridgeConfig + i *girc.Client + Nick string + names map[string][]string + connected chan struct{} + Local chan config.Message // local queue for flood control + FirstConnection bool + MessageDelay, MessageQueue, MessageLength int + + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { +func New(cfg *bridge.Config) bridge.Bridger {   b := &Birc{} - b.BridgeConfig = cfg - b.Nick = b.Config.Nick + b.Config = cfg + b.Nick = b.GetString("Nick")   b.names = make(map[string][]string)   b.connected = make(chan struct{}) - if b.Config.MessageDelay == 0 { - b.Config.MessageDelay = 1300 + if b.GetInt("MessageDelay") == 0 { + b.MessageDelay = 1300   } - if b.Config.MessageQueue == 0 { - b.Config.MessageQueue = 30 + if b.GetInt("MessageQueue") == 0 { + b.MessageQueue = 30   } - if b.Config.MessageLength == 0 { - b.Config.MessageLength = 400 + if b.GetInt("MessageLength") == 0 { + b.MessageLength = 400   }   b.FirstConnection = true   return b @@ -639 +649 @@ func (b *Birc) Command(msg *config.Message) string {  }    func (b *Birc) Connect() error { - b.Local = make(chan config.Message, b.Config.MessageQueue+10) - b.Log.Infof("Connecting %s", b.Config.Server) - server, portstr, err := net.SplitHostPort(b.Config.Server) + b.Local = make(chan config.Message, b.GetInt("MessageQueue")+10) + b.Log.Infof("Connecting %s", b.GetString("Server")) + server, portstr, err := net.SplitHostPort(b.GetString("Server"))   if err != nil {   return err   } @@ -747 +757 @@ func (b *Birc) Connect() error {   return err   }   // fix strict user handling of girc - user := b.Config.Nick + user := b.GetString("Nick")   for !girc.IsValidUser(user) {   if len(user) == 1 {   user = "matterbridge" @@ -8518 +8618 @@ func (b *Birc) Connect() error {     i := girc.New(girc.Config{   Server: server, - ServerPass: b.Config.Password, + ServerPass: b.GetString("Password"),   Port: port, - Nick: b.Config.Nick, + Nick: b.GetString("Nick"),   User: user, - Name: b.Config.Nick, - SSL: b.Config.UseTLS, - TLSConfig: &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, ServerName: server}, + Name: b.GetString("Nick"), + SSL: b.GetBool("UseTLS"), + TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server},   PingDelay: time.Minute,   })   - if b.Config.UseSASL { - i.Config.SASL = &girc.SASLPlain{b.Config.NickServNick, b.Config.NickServPassword} + if b.GetBool("UseSASL") { + i.Config.SASL = &girc.SASLPlain{b.GetString("NickServNick"), b.GetString("NickServPassword")}   }     i.Handlers.Add(girc.RPL_WELCOME, b.handleNewConnection) @@ -1277 +1287 @@ func (b *Birc) Connect() error {   return fmt.Errorf("connection timed out")   }   //i.Debug = false - if b.Config.DebugLevel == 0 { + if b.GetInt("DebugLevel") == 0 {   i.Handlers.Clear(girc.ALL_EVENTS)   }   go b.doSend() @@ -1357 +1367 @@ func (b *Birc) Connect() error {  }    func (b *Birc) Disconnect() error { - //b.i.Disconnect() + b.i.Close()   close(b.Local)   return nil  } @@ -1649 +1659 @@ func (b *Birc) Send(msg config.Message) (string, error) {   }     // convert to specified charset - if b.Config.Charset != "" { + if b.GetString("Charset") != "" {   buf := new(bytes.Buffer) - w, err := charset.NewWriter(b.Config.Charset, buf) + w, err := charset.NewWriter(b.GetString("Charset"), buf)   if err != nil {   b.Log.Errorf("charset from utf-8 conversion failed: %s", err)   return "", err @@ -19719 +19819 @@ func (b *Birc) Send(msg config.Message) (string, error) {   }     // split long messages on messageLength, to avoid clipped messages #281 - if b.Config.MessageSplit { - msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength) + if b.GetBool("MessageSplit") { + msg.Text = helper.SplitStringLength(msg.Text, b.GetInt("MessageLength"))   }   for _, text := range strings.Split(msg.Text, "\n") { - if len(text) > b.Config.MessageLength { - text = text[:b.Config.MessageLength-len(" <message clipped>")] + if len(text) > b.MessageLength { + text = text[:b.MessageLength-len(" <message clipped>")]   if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {   text = text[:len(text)-size]   }   text += " <message clipped>"   } - if len(b.Local) < b.Config.MessageQueue { - if len(b.Local) == b.Config.MessageQueue-1 { + if len(b.Local) < b.GetInt("MessageQueue") { + if len(b.Local) == b.GetInt("MessageQueue")-1 {   text = text + " <message clipped>"   }   b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} @@ -22113 +22214 @@ func (b *Birc) Send(msg config.Message) (string, error) {  }    func (b *Birc) doSend() { - rate := time.Millisecond * time.Duration(b.Config.MessageDelay) + rate := time.Millisecond * time.Duration(b.MessageDelay)   throttle := time.NewTicker(rate)   for msg := range b.Local {   <-throttle.C   if msg.Event == config.EVENT_USER_ACTION {   b.i.Cmd.Action(msg.Channel, msg.Username+msg.Text)   } else { + b.Log.Debugf("Sending to channel %s", msg.Channel)   b.i.Cmd.Message(msg.Channel, msg.Username+msg.Text)   }   } @@ -2777 +2797 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {   channel := strings.ToLower(event.Params[0])   if event.Command == "KICK" {   b.Log.Infof("Got kicked from %s by %s", channel, event.Source.Name) - time.Sleep(time.Duration(b.Config.RejoinDelay) * time.Second) + time.Sleep(time.Duration(b.GetInt("RejoinDelay")) * time.Second)   b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}   return   } @@ -29915 +30115 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {  }    func (b *Birc) handleNotice(client *girc.Client, event girc.Event) { - if strings.Contains(event.String(), "This nickname is registered") && event.Source.Name == b.Config.NickServNick { - b.i.Cmd.Message(b.Config.NickServNick, "IDENTIFY "+b.Config.NickServPassword) + if strings.Contains(event.String(), "This nickname is registered") && event.Source.Name == b.GetString("NickServNick") { + b.i.Cmd.Message(b.GetString("NickServNick"), "IDENTIFY "+b.GetString("NickServPassword"))   } else {   b.handlePrivMsg(client, event)   }  }    func (b *Birc) handleOther(client *girc.Client, event girc.Event) { - if b.Config.DebugLevel == 1 { + if b.GetInt("DebugLevel") == 1 {   if event.Command != "CLIENT_STATE_UPDATED" &&   event.Command != "CLIENT_GENERAL_UPDATED" {   b.Log.Debugf("%#v", event.String()) @@ -3229 +3249 @@ func (b *Birc) handleOther(client *girc.Client, event girc.Event) {  }    func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) { - if strings.EqualFold(b.Config.NickServNick, "Q@CServe.quakenet.org") { - b.Log.Debugf("Authenticating %s against %s", b.Config.NickServUsername, b.Config.NickServNick) - b.i.Cmd.Message(b.Config.NickServNick, "AUTH "+b.Config.NickServUsername+" "+b.Config.NickServPassword) + if strings.EqualFold(b.GetString("NickServNick"), "Q@CServe.quakenet.org") { + b.Log.Debugf("Authenticating %s against %s", b.GetString("NickServUsername"), b.GetString("NickServNick")) + b.i.Cmd.Message(b.GetString("NickServNick"), "AUTH "+b.GetString("NickServUsername")+" "+b.GetString("NickServPassword"))   }  }   @@ -3697 +3717 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {   // start detecting the charset   var r io.Reader   var err error - mycharset := b.Config.Charset + mycharset := b.GetString("Charset")   if mycharset == "" {   // detect what were sending so that we convert it to utf-8   detector := chardet.NewTextDetector() diff --git a/bridge/matrix/matrix.go b/bridge/matrix/matrix.go index d6fd68e..ed3d5bf 100644 --- a/bridge/matrix/matrix.go +++ b/bridge/matrix/matrix.go @@ -1826 +1826 @@ type Bmatrix struct {   UserID string   RoomMap map[string]string   sync.RWMutex - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Bmatrix{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Bmatrix{Config: cfg}   b.RoomMap = make(map[string]string)   return b  }    func (b *Bmatrix) Connect() error {   var err error - b.Log.Infof("Connecting %s", b.Config.Server) - b.mc, err = matrix.NewClient(b.Config.Server, "", "") + b.Log.Infof("Connecting %s", b.GetString("Server")) + b.mc, err = matrix.NewClient(b.GetString("Server"), "", "")   if err != nil {   return err   }   resp, err := b.mc.Login(&matrix.ReqLogin{   Type: "m.login.password", - User: b.Config.Login, - Password: b.Config.Password, + User: b.GetString("Login"), + Password: b.GetString("Password"),   })   if err != nil {   return err @@ -1627 +1627 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {   }     // Remove homeserver suffix if configured - if b.Config.NoHomeServerSuffix { + if b.GetBool("NoHomeServerSuffix") {   re := regexp.MustCompile("(.*?):.*")   rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)   } @@ -2077 +2077 @@ func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]in   if url, ok = content["url"].(string); !ok {   return fmt.Errorf("url isn't a %T", url)   } - url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1) + url = strings.Replace(url, "mxc://", b.GetString("Server")+"/_matrix/media/v1/download/", -1)     if info, ok = content["info"].(map[string]interface{}); !ok {   return fmt.Errorf("info isn't a %T", info) diff --git a/bridge/mattermost/mattermost.go b/bridge/mattermost/mattermost.go index dffb421..47dce84 100644 --- a/bridge/mattermost/mattermost.go +++ b/bridge/mattermost/mattermost.go @@ -1512 +1513 @@ type Bmattermost struct {   mh *matterhook.Client   mc *matterclient.MMClient   TeamID string - *config.BridgeConfig + *bridge.Config   avatarMap map[string]string  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Bmattermost{BridgeConfig: cfg, avatarMap: make(map[string]string)} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Bmattermost{Config: cfg, avatarMap: make(map[string]string)} + b.Log.Debugf("MATTERMOST %#v", b.General)   return b  }   @@ -2919 +3019 @@ func (b *Bmattermost) Command(cmd string) string {  }    func (b *Bmattermost) Connect() error { - if b.Config.WebhookBindAddress != "" { - if b.Config.WebhookURL != "" { + if b.GetString("WebhookBindAddress") != "" { + if b.GetString("WebhookURL") != "" {   b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, - BindAddress: b.Config.WebhookBindAddress}) - } else if b.Config.Token != "" { + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), + BindAddress: b.GetString("WebhookBindAddress")}) + } else if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (sending)")   err := b.apiLogin()   if err != nil {   return err   } - } else if b.Config.Login != "" { + } else if b.GetString("Login") != "" {   b.Log.Info("Connecting using login/password (sending)")   err := b.apiLogin()   if err != nil { @@ -4926 +5026 @@ func (b *Bmattermost) Connect() error {   }   } else {   b.Log.Info("Connecting using webhookbindaddress (receiving)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, - BindAddress: b.Config.WebhookBindAddress}) + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), + BindAddress: b.GetString("WebhookBindAddress")})   }   go b.handleMatter()   return nil   } - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   b.Log.Info("Connecting using webhookurl (sending)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),   DisableServer: true}) - if b.Config.Token != "" { + if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (receiving)")   err := b.apiLogin()   if err != nil {   return err   }   go b.handleMatter() - } else if b.Config.Login != "" { + } else if b.GetString("Login") != "" {   b.Log.Info("Connecting using login/password (receiving)")   err := b.apiLogin()   if err != nil { @@ -7714 +7814 @@ func (b *Bmattermost) Connect() error {   go b.handleMatter()   }   return nil - } else if b.Config.Token != "" { + } else if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (sending and receiving)")   err := b.apiLogin()   if err != nil {   return err   }   go b.handleMatter() - } else if b.Config.Login != "" { + } else if b.GetString("Login") != "" {   b.Log.Info("Connecting using login/password (sending and receiving)")   err := b.apiLogin()   if err != nil { @@ -927 +937 @@ func (b *Bmattermost) Connect() error {   }   go b.handleMatter()   } - if b.Config.WebhookBindAddress == "" && b.Config.WebhookURL == "" && b.Config.Login == "" && b.Config.Token == "" { + if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" && b.GetString("Login") == "" && b.GetString("Token") == "" {   return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Token/Login/Password/Server/Team configured")   }   return nil @@ -1047 +1057 @@ func (b *Bmattermost) Disconnect() error {    func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {   // we can only join channels using the API - if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" { + if b.GetString("WebhookURL") == "" && b.GetString("WebhookBindAddress") == "" {   id := b.mc.GetChannelId(channel.Name, "")   if id == "" {   return fmt.Errorf("Could not find channel ID for channel %s", channel.Name) @@ -1287 +1297 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {   }     // Use webhook to send the message - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   return b.sendWebhook(msg)   }   @@ -1517 +1527 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {   }     // Prepend nick if configured - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   msg.Text = msg.Username + msg.Text   }   @@ -16611 +16711 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {    func (b *Bmattermost) handleMatter() {   messages := make(chan *config.Message) - if b.Config.WebhookBindAddress != "" { + if b.GetString("WebhookBindAddress") != "" {   b.Log.Debugf("Choosing webhooks based receiving")   go b.handleMatterHook(messages)   } else { - if b.Config.Token != "" { + if b.GetString("Token") != "" {   b.Log.Debugf("Choosing token based receiving")   } else {   b.Log.Debugf("Choosing login/password based receiving") @@ -2218 +2228 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {   }     // create a text for bridges that don't support native editing - if message.Raw.Event == "post_edited" && !b.Config.EditDisable { - rmsg.Text = message.Text + b.Config.EditSuffix + if message.Raw.Event == "post_edited" && !b.GetBool("EditDisable") { + rmsg.Text = message.Text + b.GetString("EditSuffix")   }     if message.Raw.Event == "post_deleted" { @@ -25018 +25118 @@ func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {  }    func (b *Bmattermost) apiLogin() error { - password := b.Config.Password - if b.Config.Token != "" { - password = "MMAUTHTOKEN=" + b.Config.Token + password := b.GetString("Password") + if b.GetString("Token") != "" { + password = "MMAUTHTOKEN=" + b.GetString("Token")   }   - b.mc = matterclient.New(b.Config.Login, password, b.Config.Team, b.Config.Server) - if b.General.Debug { + b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server")) + if b.GetBool("debug") {   b.mc.SetLogLevel("debug")   } - b.mc.SkipTLSVerify = b.Config.SkipTLSVerify - b.mc.NoTLS = b.Config.NoTLS - b.Log.Infof("Connecting %s (team: %s) on %s", b.Config.Login, b.Config.Team, b.Config.Server) + b.mc.SkipTLSVerify = b.GetBool("SkipTLSVerify") + b.mc.NoTLS = b.GetBool("NoTLS") + b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))   err := b.mc.Login()   if err != nil {   return err @@ -3447 +3457 @@ func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {   return "", err   }   msg.Text = fi.Comment - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   msg.Text = msg.Username + msg.Text   }   res, err = b.mc.PostMessageWithFiles(channelID, msg.Text, []string{id}) @@ -35913 +36013 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {   return "", nil   }   - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   msg.Text = msg.Username + msg.Text   }   if msg.Extra != nil {   // this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE   for _, rmsg := range helper.HandleExtra(&msg, b.General) { - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: rmsg.Channel, UserName: rmsg.Username, Text: rmsg.Text, Props: make(map[string]interface{})} + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: rmsg.Channel, UserName: rmsg.Username, Text: rmsg.Text, Props: make(map[string]interface{})}   matterMessage.Props["matterbridge"] = true   b.mh.Send(matterMessage)   } @@ -3817 +3827 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {   }   }   - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: msg.Channel, UserName: msg.Username, Text: msg.Text, Props: make(map[string]interface{})} + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: msg.Channel, UserName: msg.Username, Text: msg.Text, Props: make(map[string]interface{})}   if msg.Avatar != "" {   matterMessage.IconURL = msg.Avatar   } @@ -4067 +4077 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {   }     // Handle edited messages - if (message.Raw.Event == "post_edited") && b.Config.EditDisable { + if (message.Raw.Event == "post_edited") && b.GetBool("EditDisable") {   return true   }   diff --git a/bridge/rocketchat/rocketchat.go b/bridge/rocketchat/rocketchat.go index fa6bdd5..fa2c31d 100644 --- a/bridge/rocketchat/rocketchat.go +++ b/bridge/rocketchat/rocketchat.go @@ -1511 +1511 @@ type MMhook struct {    type Brocketchat struct {   MMhook - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - return &Brocketchat{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + return &Brocketchat{Config: cfg}  }    func (b *Brocketchat) Command(cmd string) string { @@ -2810 +2810 @@ func (b *Brocketchat) Command(cmd string) string {    func (b *Brocketchat) Connect() error {   b.Log.Info("Connecting webhooks") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),   DisableServer: true}) - b.rh = rockethook.New(b.Config.WebhookURL, rockethook.Config{BindAddress: b.Config.WebhookBindAddress}) + b.rh = rockethook.New(b.GetString("WebhookURL"), rockethook.Config{BindAddress: b.GetString("WebhookBindAddress")})   go b.handleRocketHook()   return nil  } @@ -537 +537 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {   b.Log.Debugf("=> Receiving %#v", msg)   if msg.Extra != nil {   for _, rmsg := range helper.HandleExtra(&msg, b.General) { - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: rmsg.Channel, UserName: rmsg.Username, + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: rmsg.Channel, UserName: rmsg.Username,   Text: rmsg.Text}   b.mh.Send(matterMessage)   } @@ -677 +677 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {   }   }   - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL} + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL")}   matterMessage.Channel = msg.Channel   matterMessage.UserName = msg.Username   matterMessage.Type = "" @@ -857 +857 @@ func (b *Brocketchat) handleRocketHook() {   message := b.rh.Receive()   b.Log.Debugf("Receiving from rockethook %#v", message)   // do not loop - if message.UserName == b.Config.Nick { + if message.UserName == b.GetString("Nick") {   continue   }   b.Log.Debugf("<= Sending message from %s on %s to gateway", message.UserName, b.Account) diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index 0d482a3..c7c2fac 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -126 +127 @@ import (   "html"   "regexp"   "strings" + "sync"   "time"  )   @@ -2313 +2414 @@ type Bslack struct {   Usergroups []slack.UserGroup   si *slack.Info   channels []slack.Channel - *config.BridgeConfig + *bridge.Config + sync.RWMutex  }    const messageDeleted = "message_deleted"   -func New(cfg *config.BridgeConfig) bridge.Bridger { - return &Bslack{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + return &Bslack{Config: cfg}  }    func (b *Bslack) Command(cmd string) string { @@ -3764 +3965 @@ func (b *Bslack) Command(cmd string) string {  }    func (b *Bslack) Connect() error { - if b.Config.WebhookBindAddress != "" { - if b.Config.WebhookURL != "" { + b.RLock() + defer b.RUnlock() + if b.GetString("WebhookBindAddress") != "" { + if b.GetString("WebhookURL") != "" {   b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, - BindAddress: b.Config.WebhookBindAddress}) - } else if b.Config.Token != "" { + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), + BindAddress: b.GetString("WebhookBindAddress")}) + } else if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (sending)") - b.sc = slack.New(b.Config.Token) + b.sc = slack.New(b.GetString("Token"))   b.rtm = b.sc.NewRTM()   go b.rtm.ManageConnection()   b.Log.Info("Connecting using webhookbindaddress (receiving)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, - BindAddress: b.Config.WebhookBindAddress}) + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), + BindAddress: b.GetString("WebhookBindAddress")})   } else {   b.Log.Info("Connecting using webhookbindaddress (receiving)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, - BindAddress: b.Config.WebhookBindAddress}) + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), + BindAddress: b.GetString("WebhookBindAddress")})   }   go b.handleSlack()   return nil   } - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   b.Log.Info("Connecting using webhookurl (sending)") - b.mh = matterhook.New(b.Config.WebhookURL, - matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, + b.mh = matterhook.New(b.GetString("WebhookURL"), + matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),   DisableServer: true}) - if b.Config.Token != "" { + if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (receiving)") - b.sc = slack.New(b.Config.Token) + b.sc = slack.New(b.GetString("Token"))   b.rtm = b.sc.NewRTM()   go b.rtm.ManageConnection()   go b.handleSlack()   } - } else if b.Config.Token != "" { + } else if b.GetString("Token") != "" {   b.Log.Info("Connecting using token (sending and receiving)") - b.sc = slack.New(b.Config.Token) + b.sc = slack.New(b.GetString("Token"))   b.rtm = b.sc.NewRTM()   go b.rtm.ManageConnection()   go b.handleSlack()   } - if b.Config.WebhookBindAddress == "" && b.Config.WebhookURL == "" && b.Config.Token == "" { + if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" && b.GetString("Token") == "" {   return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Token configured")   }   return nil  }    func (b *Bslack) Disconnect() error { - return nil - + return b.rtm.Disconnect()  }    func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {   // we can only join channels using the API   if b.sc != nil { - if strings.HasPrefix(b.Config.Token, "xoxb") { + if strings.HasPrefix(b.GetString("Token"), "xoxb") {   // TODO check if bot has already joined channel   return nil   } @@ -1177 +1207 @@ func (b *Bslack) Send(msg config.Message) (string, error) {   }     // Use webhook to send the message - if b.Config.WebhookURL != "" { + if b.GetString("WebhookURL") != "" {   return b.sendWebhook(msg)   }   @@ -1437 +1467 @@ func (b *Bslack) Send(msg config.Message) (string, error) {   }     // Prepend nick if configured - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   msg.Text = msg.Username + msg.Text   }   @@ -15912 +16212 @@ func (b *Bslack) Send(msg config.Message) (string, error) {     // create slack new post parameters   np := slack.NewPostMessageParameters() - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   np.AsUser = true   }   np.Username = msg.Username   np.LinkNames = 1 // replace mentions - np.IconURL = config.GetIconURL(&msg, &b.Config) + np.IconURL = config.GetIconURL(&msg, b.GetString("iconurl"))   if msg.Avatar != "" {   np.IconURL = msg.Avatar   } @@ -1986 +20110 @@ func (b *Bslack) Send(msg config.Message) (string, error) {   return "slack " + id, nil  }   +func (b *Bslack) Reload(cfg *bridge.Config) (string, error) { + return "", nil +} +  func (b *Bslack) getAvatar(user string) string {   var avatar string   if b.Users != nil { @@ -2367 +2437 @@ func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {    func (b *Bslack) handleSlack() {   messages := make(chan *config.Message) - if b.Config.WebhookBindAddress != "" { + if b.GetString("WebhookBindAddress") != "" {   b.Log.Debugf("Choosing webhooks based receiving")   go b.handleMatterHook(messages)   } else { @@ -4197 +4267 @@ func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File) erro   return err   }   // actually download the file - data, err := helper.DownloadFileAuth(file.URLPrivateDownload, "Bearer "+b.Config.Token) + data, err := helper.DownloadFileAuth(file.URLPrivateDownload, "Bearer "+b.GetString("Token"))   if err != nil {   return fmt.Errorf("download %s failed %#v", file.URLPrivateDownload, err)   } @@ -45410 +46110 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er   }     // Edit message - if !b.Config.EditDisable && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp { + if !b.GetBool("EditDisable") && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {   b.Log.Debugf("SubMessage %#v", ev.SubMessage)   ev.User = ev.SubMessage.User - ev.Text = ev.SubMessage.Text + b.Config.EditSuffix + ev.Text = ev.SubMessage.Text + b.GetString("EditSuffix")   }     // use our own func because rtm.GetChannelInfo doesn't work for private channels @@ -4937 +5007 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er   }     // when using webhookURL we can't check if it's our webhook or not for now - if ev.BotID != "" && b.Config.WebhookURL == "" { + if ev.BotID != "" && b.GetString("WebhookURL") == "" {   bot, err := b.rtm.GetBotInfo(ev.BotID)   if err != nil {   return nil, err @@ -56814 +57514 @@ func (b *Bslack) sendWebhook(msg config.Message) (string, error) {   return "", nil   }   - if b.Config.PrefixMessagesWithNick { + if b.GetBool("PrefixMessagesWithNick") {   msg.Text = msg.Username + msg.Text   }     if msg.Extra != nil {   // this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE   for _, rmsg := range helper.HandleExtra(&msg, b.General) { - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: msg.Channel, UserName: rmsg.Username, Text: rmsg.Text} + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Channel: msg.Channel, UserName: rmsg.Username, Text: rmsg.Text}   b.mh.Send(matterMessage)   }   @@ -5987 +6057 @@ func (b *Bslack) sendWebhook(msg config.Message) (string, error) {   }   }   - matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Attachments: attachs, Channel: msg.Channel, UserName: msg.Username, Text: msg.Text} + matterMessage := matterhook.OMessage{IconURL: b.GetString("IconURL"), Attachments: attachs, Channel: msg.Channel, UserName: msg.Username, Text: msg.Text}   if msg.Avatar != "" {   matterMessage.IconURL = msg.Avatar   } @@ -6187 +6257 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {   }     // do not send messages from ourself - if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && ev.Username == b.si.User.Name { + if b.GetString("WebhookURL") == "" && b.GetString("WebhookBindAddress") == "" && ev.Username == b.si.User.Name {   return true   }   @@ -6297 +6367 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {   }   }   - if !b.Config.EditDisable && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp { + if !b.GetBool("EditDisable") && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {   // it seems ev.SubMessage.Edited == nil when slack unfurls   // do not forward these messages #266   if ev.SubMessage.Edited == nil { diff --git a/bridge/sshchat/sshchat.go b/bridge/sshchat/sshchat.go index c11d3da..3204d73 100644 --- a/bridge/sshchat/sshchat.go +++ b/bridge/sshchat/sshchat.go @@ -1418 +1418 @@ import (  type Bsshchat struct {   r *bufio.Scanner   w io.WriteCloser - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - return &Bsshchat{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + return &Bsshchat{Config: cfg}  }    func (b *Bsshchat) Connect() error {   var err error - b.Log.Infof("Connecting %s", b.Config.Server) + b.Log.Infof("Connecting %s", b.GetString("Server"))   go func() { - err = sshd.ConnectShell(b.Config.Server, b.Config.Nick, func(r io.Reader, w io.WriteCloser) error { + err = sshd.ConnectShell(b.GetString("Server"), b.GetString("Nick"), func(r io.Reader, w io.WriteCloser) error {   b.r = bufio.NewScanner(r)   b.w = w   b.r.Scan() diff --git a/bridge/steam/steam.go b/bridge/steam/steam.go index a0bf757..6065b2a 100644 --- a/bridge/steam/steam.go +++ b/bridge/steam/steam.go @@ -1811 +1811 @@ type Bsteam struct {   connected chan struct{}   userMap map[steamid.SteamId]string   sync.RWMutex - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Bsteam{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Bsteam{Config: cfg}   b.userMap = make(map[steamid.SteamId]string)   b.connected = make(chan struct{})   return b @@ -819 +819 @@ func (b *Bsteam) getNick(id steamid.SteamId) string {    func (b *Bsteam) handleEvents() {   myLoginInfo := new(steam.LogOnDetails) - myLoginInfo.Username = b.Config.Login - myLoginInfo.Password = b.Config.Password - myLoginInfo.AuthCode = b.Config.AuthCode + myLoginInfo.Username = b.GetString("Login") + myLoginInfo.Password = b.GetString("Password") + myLoginInfo.AuthCode = b.GetString("AuthCode")   // Attempt to read existing auth hash to avoid steam guard.   // Maybe works   //myLoginInfo.SentryFileHash, _ = ioutil.ReadFile("sentry") diff --git a/bridge/telegram/telegram.go b/bridge/telegram/telegram.go index 2306086..466ae29 100644 --- a/bridge/telegram/telegram.go +++ b/bridge/telegram/telegram.go @@ -1418 +1418 @@ import (    type Btelegram struct {   c *tgbotapi.BotAPI - *config.BridgeConfig + *bridge.Config   avatarMap map[string]string // keep cache of userid and avatar sha  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - return &Btelegram{BridgeConfig: cfg, avatarMap: make(map[string]string)} +func New(cfg *bridge.Config) bridge.Bridger { + return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}  }    func (b *Btelegram) Connect() error {   var err error   b.Log.Info("Connecting") - b.c, err = tgbotapi.NewBotAPI(b.Config.Token) + b.c, err = tgbotapi.NewBotAPI(b.GetString("Token"))   if err != nil {   b.Log.Debugf("%#v", err)   return err @@ -647 +647 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {   return b.cacheAvatar(&msg)   }   - if b.Config.MessageFormat == "HTML" { + if b.GetString("MessageFormat") == "HTML" {   msg.Text = makeHTML(msg.Text)   }   @@ -9911 +9911 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {   return "", err   }   m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text) - if b.Config.MessageFormat == "HTML" { + if b.GetString("MessageFormat") == "HTML" {   b.Log.Debug("Using mode HTML")   m.ParseMode = tgbotapi.ModeHTML   } - if b.Config.MessageFormat == "Markdown" { + if b.GetString("MessageFormat") == "Markdown" {   b.Log.Debug("Using mode markdown")   m.ParseMode = tgbotapi.ModeMarkdown   } @@ -1379 +1379 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {   }     // edited channel message - if update.EditedChannelPost != nil && !b.Config.EditDisable { + if update.EditedChannelPost != nil && !b.GetBool("EditDisable") {   message = update.EditedChannelPost - rmsg.Text = rmsg.Text + message.Text + b.Config.EditSuffix + rmsg.Text = rmsg.Text + message.Text + b.GetString("EditSuffix")   }     // handle groups @@ -1489 +1489 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {   }     // edited group message - if update.EditedMessage != nil && !b.Config.EditDisable { + if update.EditedMessage != nil && !b.GetBool("EditDisable") {   message = update.EditedMessage - rmsg.Text = rmsg.Text + message.Text + b.Config.EditSuffix + rmsg.Text = rmsg.Text + message.Text + b.GetString("EditSuffix")   }     // set the ID's from the channel or group message @@ -1607 +1607 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {     // handle username   if message.From != nil { - if b.Config.UseFirstName { + if b.GetBool("UseFirstName") {   rmsg.Username = message.From.FirstName   }   if rmsg.Username == "" { @@ -1897 +1897 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {   // handle forwarded messages   if message.ForwardFrom != nil {   usernameForward := "" - if b.Config.UseFirstName { + if b.GetBool("UseFirstName") {   usernameForward = message.ForwardFrom.FirstName   }   if usernameForward == "" { @@ -2087 +2087 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {   if message.ReplyToMessage != nil {   usernameReply := ""   if message.ReplyToMessage.From != nil { - if b.Config.UseFirstName { + if b.GetBool("UseFirstName") {   usernameReply = message.ReplyToMessage.From.FirstName   }   if usernameReply == "" { @@ -3387 +3387 @@ func (b *Btelegram) handleDownload(message *tgbotapi.Message, rmsg *config.Messa   return nil   }   // use the URL instead of native upload - if b.Config.UseInsecureURL { + if b.GetBool("UseInsecureURL") {   b.Log.Debugf("Setting message text to :%s", text)   rmsg.Text = rmsg.Text + text   return nil @@ -38213 +38213 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) (string,  func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, error) {   m := tgbotapi.NewMessage(chatid, "")   m.Text = username + text - if b.Config.MessageFormat == "HTML" { + if b.GetString("MessageFormat") == "HTML" {   b.Log.Debug("Using mode HTML")   username = html.EscapeString(username)   m.Text = username + text   m.ParseMode = tgbotapi.ModeHTML   } - if b.Config.MessageFormat == "Markdown" { + if b.GetString("MessageFormat") == "Markdown" {   b.Log.Debug("Using mode markdown")   m.ParseMode = tgbotapi.ModeMarkdown   } diff --git a/bridge/xmpp/xmpp.go b/bridge/xmpp/xmpp.go index b1c1255..8dfa22a 100644 --- a/bridge/xmpp/xmpp.go +++ b/bridge/xmpp/xmpp.go @@ -1418 +1418 @@ import (  type Bxmpp struct {   xc *xmpp.Client   xmppMap map[string]string - *config.BridgeConfig + *bridge.Config  }   -func New(cfg *config.BridgeConfig) bridge.Bridger { - b := &Bxmpp{BridgeConfig: cfg} +func New(cfg *bridge.Config) bridge.Bridger { + b := &Bxmpp{Config: cfg}   b.xmppMap = make(map[string]string)   return b  }    func (b *Bxmpp) Connect() error {   var err error - b.Log.Infof("Connecting %s", b.Config.Server) + b.Log.Infof("Connecting %s", b.GetString("Server"))   b.xc, err = b.createXMPP()   if err != nil {   b.Log.Debugf("%#v", err) @@ -637 +637 @@ func (b *Bxmpp) Disconnect() error {  }    func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error { - b.xc.JoinMUCNoHistory(channel.Name+"@"+b.Config.Muc, b.Config.Nick) + b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))   return nil  }   @@ -777 +777 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {   // Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)   if msg.Extra != nil {   for _, rmsg := range helper.HandleExtra(&msg, b.General) { - b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.Config.Muc, Text: rmsg.Username + rmsg.Text}) + b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})   }   if len(msg.Extra["file"]) > 0 {   return b.handleUploadFile(&msg) @@ -857 +857 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {   }     // Post normal message - _, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text}) + _, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})   if err != nil {   return "", err   } @@ -9417 +9416 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {    func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {   tc := new(tls.Config) - tc.InsecureSkipVerify = b.Config.SkipTLSVerify - tc.ServerName = strings.Split(b.Config.Server, ":")[0] + tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify") + tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]   options := xmpp.Options{ - Host: b.Config.Server, - User: b.Config.Jid, - Password: b.Config.Password, - NoTLS: true, - StartTLS: true, - TLSConfig: tc, - - Debug: b.General.Debug, + Host: b.GetString("Server"), + User: b.GetString("Jid"), + Password: b.GetString("Password"), + NoTLS: true, + StartTLS: true, + TLSConfig: tc, + Debug: b.GetBool("debug"),   Logger: b.Log.Writer(),   Session: true,   Status: "", @@ -1506 +1497 @@ func (b *Bxmpp) handleXMPP() error {   switch v := m.(type) {   case xmpp.Chat:   if v.Type == "groupchat" { + b.Log.Debugf("== Receiving %#v", v)   // skip invalid messages   if b.skipMessage(v) {   continue @@ -1887 +1887 @@ func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {   if fi.URL != "" {   msg.Text += fi.URL   } - _, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text}) + _, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})   if err != nil {   return "", err   } @@ -2187 +2187 @@ func (b *Bxmpp) parseChannel(remote string) string {  // skipMessage skips messages that need to be skipped  func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {   // skip messages from ourselves - if b.parseNick(message.Remote) == b.Config.Nick { + if b.parseNick(message.Remote) == b.GetString("Nick") {   return true   }   diff --git a/gateway/gateway.go b/gateway/gateway.go index 27ffed7..a637dcf 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -5017 +5017 @@ type BrMsgID struct {  var flog *log.Entry    var bridgeMap = map[string]bridge.Factory{ - "mattermost": bmattermost.New, - "irc": birc.New, + "api": api.New, + "discord": bdiscord.New,   "gitter": bgitter.New, + "irc": birc.New, + "mattermost": bmattermost.New,   "matrix": bmatrix.New, + "rocketchat": brocketchat.New,   "slack": bslack.New, - "api": api.New, - "telegram": btelegram.New, - "discord": bdiscord.New, - "steam": bsteam.New,   "sshchat": bsshchat.New, - "rocketchat": brocketchat.New, + "steam": bsteam.New, + "telegram": btelegram.New,   "xmpp": bxmpp.New,  }   @@ -8116 +8110 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {   br := gw.Router.getBridge(cfg.Account)   if br == nil {   br = bridge.New(cfg) - // override config from environment - config.OverrideCfgFromEnv(gw.Config, br.Protocol, br.Name) + br.Config = gw.Router.Config   // set logging   br.Log = log.WithFields(log.Fields{"prefix": "bridge"}) - // get the protocol configuration (eg irc) - pcfg := getField(gw.Config, strings.Title(br.Protocol)) - // get the config for this name (eg freenode, from irc.freenode) - br.Config = pcfg[br.Name] - // create the bridge config - brconfig := &config.BridgeConfig{General: &gw.Config.General, Account: br.Account, Remote: gw.Message, Config: br.Config, Log: log.WithFields(log.Fields{"prefix": br.Protocol})} + brconfig := &bridge.Config{Remote: gw.Message, Log: log.WithFields(log.Fields{"prefix": br.Protocol}), Bridge: br}   // add the actual bridger for this protocol to this bridge using the bridgeMap   br.Bridger = bridgeMap[br.Protocol](brconfig)   } @@ -22612 +22012 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM   }     // only relay join/part when configured - if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart { + if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.GetBool("ShowJoinPart") {   return brMsgIDs   }     // only relay topic change when configured - if msg.Event == config.EVENT_TOPIC_CHANGE && !gw.Bridges[dest.Account].Config.ShowTopicChange { + if msg.Event == config.EVENT_TOPIC_CHANGE && !gw.Bridges[dest.Account].Config.GetBool("ShowTopicChange") {   return brMsgIDs   }   @@ -3087 +3027 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {   }     // is the username in IgnoreNicks field - for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) { + for _, entry := range strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreNicks")) {   if msg.Username == entry {   flog.Debugf("ignoring %s from %s", msg.Username, msg.Account)   return true @@ -3177 +3117 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {     // does the message match regex in IgnoreMessages field   // TODO do not compile regexps everytime - for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) { + for _, entry := range strings.Fields(gw.Bridges[msg.Account].GetString("IgnoreMessages")) {   if entry != "" {   re, err := regexp.Compile(entry)   if err != nil { @@ -33617 +33017 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {  func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {   br := gw.Bridges[msg.Account]   msg.Protocol = br.Protocol - if gw.Config.General.StripNick || dest.Config.StripNick { + if gw.Config.General.StripNick || dest.Config.GetBool("StripNick") {   re := regexp.MustCompile("[^a-zA-Z0-9]+")   msg.Username = re.ReplaceAllString(msg.Username, "")   } - nick := dest.Config.RemoteNickFormat + nick := dest.GetString("RemoteNickFormat")   if nick == "" {   nick = gw.Config.General.RemoteNickFormat   }     // loop to replace nicks - for _, outer := range br.Config.ReplaceNicks { + for _, outer := range br.GetStringSlice2D("ReplaceNicks") {   search := outer[0]   replace := outer[1]   // TODO move compile to bridge init somewhere @@ -3737 +3677 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin     nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)   nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1) - nick = strings.Replace(nick, "{LABEL}", br.Config.Label, -1) + nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1)   nick = strings.Replace(nick, "{NICK}", msg.Username, -1)   return nick  } @@ -3817 +3757 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin  func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {   iconurl := gw.Config.General.IconURL   if iconurl == "" { - iconurl = dest.Config.IconURL + iconurl = dest.GetString("IconURL")   }   iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)   if msg.Avatar == "" { @@ -3967 +3907 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {     br := gw.Bridges[msg.Account]   // loop to replace messages - for _, outer := range br.Config.ReplaceMessages { + for _, outer := range br.GetStringSlice2D("ReplaceMessages") {   search := outer[0]   replace := outer[1]   // TODO move compile to bridge init somewhere