diff --git a/bridge/api/api.go b/bridge/api/api.go index b4415267..54eea4a7 100644 --- a/bridge/api/api.go +++ b/bridge/api/api.go @@ -15,7 +15,7 @@ import ( type Api struct { Messages ring.Ring sync.RWMutex - *config.BridgeConfig + *bridge.Config } type ApiMessage struct { @@ -26,27 +26,27 @@ 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 93e1cd8f..8135cabf 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -15,7 +15,6 @@ type Bridger interface { } type Bridge struct { - Config config.Protocol Bridger Name string Account string @@ -23,10 +22,19 @@ 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) @@ -59,3 +67,42 @@ 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 9e3f854e..a89f9aa7 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -1,11 +1,11 @@ package config import ( - "github.com/BurntSushi/toml" log "github.com/sirupsen/logrus" + "github.com/spf13/viper" "os" - "reflect" "strings" + "sync" "time" ) @@ -141,7 +141,7 @@ type SameChannelGateway struct { Accounts []string } -type Config struct { +type ConfigValues struct { Api map[string]Protocol Irc map[string]Protocol Mattermost map[string]Protocol @@ -159,88 +159,78 @@ 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 - } + err = viper.ReadConfig(f) + if err != nil { + log.Fatal(err) } - 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 - } - } - 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 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) 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() + 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] @@ -249,17 +239,3 @@ 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 cfc6a81d..ca4e4185 100644 --- a/bridge/discord/discord.go +++ b/bridge/discord/discord.go @@ -23,32 +23,33 @@ 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 } @@ -71,7 +72,7 @@ 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 { @@ -83,7 +84,7 @@ func (b *Bdiscord) Connect() error { } func (b *Bdiscord) Disconnect() error { - return nil + return b.c.Close() } func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error { @@ -186,13 +187,13 @@ 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)) } } @@ -236,14 +237,14 @@ 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" } @@ -367,7 +368,7 @@ 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 { @@ -380,8 +381,8 @@ 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 b7f69b1f..853640f5 100644 --- a/bridge/gitter/gitter.go +++ b/bridge/gitter/gitter.go @@ -14,17 +14,17 @@ 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 5f89d295..8053bb4b 100644 --- a/bridge/irc/irc.go +++ b/bridge/irc/irc.go @@ -23,30 +23,31 @@ 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 + 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 - *config.BridgeConfig + *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 @@ -63,9 +64,9 @@ 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 } @@ -74,7 +75,7 @@ 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" @@ -85,18 +86,18 @@ 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) @@ -127,7 +128,7 @@ 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() @@ -135,7 +136,7 @@ func (b *Birc) Connect() error { } func (b *Birc) Disconnect() error { - //b.i.Disconnect() + b.i.Close() close(b.Local) return nil } @@ -164,9 +165,9 @@ 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 @@ -197,19 +198,19 @@ 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(" ")] + if len(text) > b.MessageLength { + text = text[:b.MessageLength-len(" ")] if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError { text = text[:len(text)-size] } text += " " } - 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 + " " } b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} @@ -221,13 +222,14 @@ 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) } } @@ -277,7 +279,7 @@ 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 } @@ -299,15 +301,15 @@ 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()) @@ -322,9 +324,9 @@ 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")) } } @@ -369,7 +371,7 @@ 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 d6fd68e7..ed3d5bf2 100644 --- a/bridge/matrix/matrix.go +++ b/bridge/matrix/matrix.go @@ -18,26 +18,26 @@ 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 @@ -162,7 +162,7 @@ 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`) } @@ -207,7 +207,7 @@ 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 dffb421d..47dce849 100644 --- a/bridge/mattermost/mattermost.go +++ b/bridge/mattermost/mattermost.go @@ -15,12 +15,13 @@ 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 } @@ -29,19 +30,19 @@ 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 { @@ -49,26 +50,26 @@ 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 { @@ -77,14 +78,14 @@ 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 { @@ -92,7 +93,7 @@ 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 @@ -104,7 +105,7 @@ 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) @@ -128,7 +129,7 @@ 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) } @@ -151,7 +152,7 @@ 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 } @@ -166,11 +167,11 @@ 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") @@ -221,8 +222,8 @@ 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" { @@ -250,18 +251,18 @@ 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 @@ -344,7 +345,7 @@ 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}) @@ -359,13 +360,13 @@ 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) } @@ -381,7 +382,7 @@ 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 } @@ -406,7 +407,7 @@ 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 fa6bdd5b..fa2c31d7 100644 --- a/bridge/rocketchat/rocketchat.go +++ b/bridge/rocketchat/rocketchat.go @@ -15,11 +15,11 @@ 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 { @@ -28,10 +28,10 @@ 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 } @@ -53,7 +53,7 @@ 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) } @@ -67,7 +67,7 @@ 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 = "" @@ -85,7 +85,7 @@ 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 0d482a3a..c7c2fac8 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -12,6 +12,7 @@ import ( "html" "regexp" "strings" + "sync" "time" ) @@ -23,13 +24,14 @@ 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 { @@ -37,64 +39,65 @@ 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 } @@ -117,7 +120,7 @@ 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) } @@ -143,7 +146,7 @@ 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 } @@ -159,12 +162,12 @@ 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 } @@ -198,6 +201,10 @@ 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 { @@ -236,7 +243,7 @@ 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 { @@ -419,7 +426,7 @@ 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) } @@ -454,10 +461,10 @@ 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 @@ -493,7 +500,7 @@ 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 @@ -568,14 +575,14 @@ 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) } @@ -598,7 +605,7 @@ 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 } @@ -618,7 +625,7 @@ 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 } @@ -629,7 +636,7 @@ 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 c11d3daa..3204d734 100644 --- a/bridge/sshchat/sshchat.go +++ b/bridge/sshchat/sshchat.go @@ -14,18 +14,18 @@ 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 a0bf757c..6065b2a8 100644 --- a/bridge/steam/steam.go +++ b/bridge/steam/steam.go @@ -18,11 +18,11 @@ 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 @@ -81,9 +81,9 @@ 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 23060863..466ae29c 100644 --- a/bridge/telegram/telegram.go +++ b/bridge/telegram/telegram.go @@ -14,18 +14,18 @@ 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 @@ -64,7 +64,7 @@ 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) } @@ -99,11 +99,11 @@ 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 } @@ -137,9 +137,9 @@ 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 @@ -148,9 +148,9 @@ 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 @@ -160,7 +160,7 @@ 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 == "" { @@ -189,7 +189,7 @@ 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 == "" { @@ -208,7 +208,7 @@ 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 == "" { @@ -338,7 +338,7 @@ 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 @@ -382,13 +382,13 @@ 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 b1c12555..8dfa22a7 100644 --- a/bridge/xmpp/xmpp.go +++ b/bridge/xmpp/xmpp.go @@ -14,18 +14,18 @@ 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) @@ -63,7 +63,7 @@ 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 } @@ -77,7 +77,7 @@ 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) @@ -85,7 +85,7 @@ 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 } @@ -94,17 +94,16 @@ 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: "", @@ -150,6 +149,7 @@ 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 @@ -188,7 +188,7 @@ 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 } @@ -218,7 +218,7 @@ 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 27ffed70..a637dcf7 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -50,17 +50,17 @@ type BrMsgID struct { var flog *log.Entry var bridgeMap = map[string]bridge.Factory{ - "mattermost": bmattermost.New, - "irc": birc.New, - "gitter": bgitter.New, - "matrix": bmatrix.New, - "slack": bslack.New, "api": api.New, - "telegram": btelegram.New, "discord": bdiscord.New, - "steam": bsteam.New, - "sshchat": bsshchat.New, + "gitter": bgitter.New, + "irc": birc.New, + "mattermost": bmattermost.New, + "matrix": bmatrix.New, "rocketchat": brocketchat.New, + "slack": bslack.New, + "sshchat": bsshchat.New, + "steam": bsteam.New, + "telegram": btelegram.New, "xmpp": bxmpp.New, } @@ -81,16 +81,10 @@ 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) } @@ -226,12 +220,12 @@ 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 } @@ -308,7 +302,7 @@ 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 @@ -317,7 +311,7 @@ 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 { @@ -336,17 +330,17 @@ 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 @@ -373,7 +367,7 @@ 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 } @@ -381,7 +375,7 @@ 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 == "" { @@ -396,7 +390,7 @@ 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