summaryrefslogtreecommitdiff
path: root/teleirc/matterbridge/bridge/config/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'teleirc/matterbridge/bridge/config/config.go')
-rw-r--r--teleirc/matterbridge/bridge/config/config.go441
1 files changed, 441 insertions, 0 deletions
diff --git a/teleirc/matterbridge/bridge/config/config.go b/teleirc/matterbridge/bridge/config/config.go
new file mode 100644
index 0000000..18c6092
--- /dev/null
+++ b/teleirc/matterbridge/bridge/config/config.go
@@ -0,0 +1,441 @@
+package config
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+)
+
+const (
+ EventJoinLeave = "join_leave"
+ EventTopicChange = "topic_change"
+ EventFailure = "failure"
+ EventFileFailureSize = "file_failure_size"
+ EventAvatarDownload = "avatar_download"
+ EventRejoinChannels = "rejoin_channels"
+ EventUserAction = "user_action"
+ EventMsgDelete = "msg_delete"
+ EventFileDelete = "file_delete"
+ EventAPIConnected = "api_connected"
+ EventUserTyping = "user_typing"
+ EventGetChannelMembers = "get_channel_members"
+ EventNoticeIRC = "notice_irc"
+)
+
+const ParentIDNotFound = "msg-parent-not-found"
+
+type Message struct {
+ Text string `json:"text"`
+ Channel string `json:"channel"`
+ Username string `json:"username"`
+ UserID string `json:"userid"` // userid on the bridge
+ Avatar string `json:"avatar"`
+ Account string `json:"account"`
+ Event string `json:"event"`
+ Protocol string `json:"protocol"`
+ Gateway string `json:"gateway"`
+ ParentID string `json:"parent_id"`
+ Timestamp time.Time `json:"timestamp"`
+ ID string `json:"id"`
+ Extra map[string][]interface{}
+}
+
+func (m Message) ParentNotFound() bool {
+ return m.ParentID == ParentIDNotFound
+}
+
+func (m Message) ParentValid() bool {
+ return m.ParentID != "" && !m.ParentNotFound()
+}
+
+type FileInfo struct {
+ Name string
+ Data *[]byte
+ Comment string
+ URL string
+ Size int64
+ Avatar bool
+ SHA string
+ NativeID string
+}
+
+type ChannelInfo struct {
+ Name string
+ Account string
+ Direction string
+ ID string
+ SameChannel map[string]bool
+ Options ChannelOptions
+}
+
+type ChannelMember struct {
+ Username string
+ Nick string
+ UserID string
+ ChannelID string
+ ChannelName string
+}
+
+type ChannelMembers []ChannelMember
+
+type Protocol struct {
+ AllowMention []string // discord
+ AuthCode string // steam
+ BindAddress string // mattermost, slack // DEPRECATED
+ Buffer int // api
+ Charset string // irc
+ ClientID string // msteams
+ ColorNicks bool // only irc for now
+ Debug bool // general
+ DebugLevel int // only for irc now
+ DisableWebPagePreview bool // telegram
+ EditSuffix string // mattermost, slack, discord, telegram, gitter
+ EditDisable bool // mattermost, slack, discord, telegram, gitter
+ HTMLDisable bool // matrix
+ IconURL string // mattermost, slack
+ IgnoreFailureOnStart bool // general
+ IgnoreNicks string // all protocols
+ IgnoreMessages string // all protocols
+ Jid string // xmpp
+ JoinDelay string // all protocols
+ Label string // all protocols
+ Login string // mattermost, matrix
+ LogFile string // general
+ MediaDownloadBlackList []string
+ MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
+ MediaDownloadSize int // all protocols
+ MediaServerDownload string
+ MediaServerUpload string
+ MediaConvertTgs string // telegram
+ MediaConvertWebPToPNG bool // telegram
+ MessageDelay int // IRC, time in millisecond to wait between messages
+ MessageFormat string // telegram
+ MessageLength int // IRC, max length of a message allowed
+ MessageQueue int // IRC, size of message queue for flood control
+ MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
+ Muc string // xmpp
+ MxID string // matrix
+ Name string // all protocols
+ Nick string // all protocols
+ NickFormatter string // mattermost, slack
+ NickServNick string // IRC
+ NickServUsername string // IRC
+ NickServPassword string // IRC
+ NicksPerRow int // mattermost, slack
+ NoHomeServerSuffix bool // matrix
+ NoSendJoinPart bool // all protocols
+ NoTLS bool // mattermost, xmpp
+ Password string // IRC,mattermost,XMPP,matrix
+ PrefixMessagesWithNick bool // mattemost, slack
+ PreserveThreading bool // slack
+ Protocol string // all protocols
+ QuoteDisable bool // telegram
+ QuoteFormat string // telegram
+ QuoteLengthLimit int // telegram
+ RealName string // IRC
+ RejoinDelay int // IRC
+ ReplaceMessages [][]string // all protocols
+ ReplaceNicks [][]string // all protocols
+ RemoteNickFormat string // all protocols
+ RunCommands []string // IRC
+ Server string // IRC,mattermost,XMPP,discord,matrix
+ SessionFile string // msteams,whatsapp
+ ShowJoinPart bool // all protocols
+ ShowTopicChange bool // slack
+ ShowUserTyping bool // slack
+ ShowEmbeds bool // discord
+ SkipTLSVerify bool // IRC, mattermost
+ SkipVersionCheck bool // mattermost
+ StripNick bool // all protocols
+ StripMarkdown bool // irc
+ SyncTopic bool // slack
+ TengoModifyMessage string // general
+ Team string // mattermost, keybase
+ TeamID string // msteams
+ TenantID string // msteams
+ Token string // gitter, slack, discord, api, matrix
+ Topic string // zulip
+ URL string // mattermost, slack // DEPRECATED
+ UseAPI bool // mattermost, slack
+ UseLocalAvatar []string // discord
+ UseSASL bool // IRC
+ UseTLS bool // IRC
+ UseDiscriminator bool // discord
+ UseFirstName bool // telegram
+ UseUserName bool // discord, matrix, mattermost
+ UseInsecureURL bool // telegram
+ UserName string // IRC
+ VerboseJoinPart bool // IRC
+ WebhookBindAddress string // mattermost, slack
+ WebhookURL string // mattermost, slack
+}
+
+type ChannelOptions struct {
+ Key string // irc, xmpp
+ WebhookURL string // discord
+ Topic string // zulip
+}
+
+type Bridge struct {
+ Account string
+ Channel string
+ Options ChannelOptions
+ SameChannel bool
+}
+
+type Gateway struct {
+ Name string
+ Enable bool
+ In []Bridge
+ Out []Bridge
+ InOut []Bridge
+}
+
+type Tengo struct {
+ InMessage string
+ Message string
+ RemoteNickFormat string
+ OutMessage string
+}
+
+type SameChannelGateway struct {
+ Name string
+ Enable bool
+ Channels []string
+ Accounts []string
+}
+
+type BridgeValues struct {
+ API map[string]Protocol
+ IRC map[string]Protocol
+ Mattermost map[string]Protocol
+ Matrix map[string]Protocol
+ Slack map[string]Protocol
+ SlackLegacy map[string]Protocol
+ Steam map[string]Protocol
+ Gitter map[string]Protocol
+ XMPP map[string]Protocol
+ Discord map[string]Protocol
+ Telegram map[string]Protocol
+ Rocketchat map[string]Protocol
+ SSHChat map[string]Protocol
+ WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
+ Zulip map[string]Protocol
+ Keybase map[string]Protocol
+ Mumble map[string]Protocol
+ General Protocol
+ Tengo Tengo
+ Gateway []Gateway
+ SameChannelGateway []SameChannelGateway
+}
+
+type Config interface {
+ Viper() *viper.Viper
+ BridgeValues() *BridgeValues
+ IsKeySet(key string) bool
+ GetBool(key string) (bool, bool)
+ GetInt(key string) (int, bool)
+ GetString(key string) (string, bool)
+ GetStringSlice(key string) ([]string, bool)
+ GetStringSlice2D(key string) ([][]string, bool)
+}
+
+type config struct {
+ sync.RWMutex
+
+ logger *logrus.Entry
+ v *viper.Viper
+ cv *BridgeValues
+}
+
+// NewConfig instantiates a new configuration based on the specified configuration file path.
+func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
+ logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"})
+
+ viper.SetConfigFile(cfgfile)
+ input, err := ioutil.ReadFile(cfgfile)
+ if err != nil {
+ logger.Fatalf("Failed to read configuration file: %#v", err)
+ }
+
+ cfgtype := detectConfigType(cfgfile)
+ mycfg := newConfigFromString(logger, input, cfgtype)
+ if mycfg.cv.General.LogFile != "" {
+ logfile, err := os.OpenFile(mycfg.cv.General.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
+ if err == nil {
+ logger.Info("Opening log file ", mycfg.cv.General.LogFile)
+ rootLogger.Out = logfile
+ } else {
+ logger.Warn("Failed to open ", mycfg.cv.General.LogFile)
+ }
+ }
+ if mycfg.cv.General.MediaDownloadSize == 0 {
+ mycfg.cv.General.MediaDownloadSize = 1000000
+ }
+ viper.WatchConfig()
+ viper.OnConfigChange(func(e fsnotify.Event) {
+ logger.Println("Config file changed:", e.Name)
+ })
+ return mycfg
+}
+
+// detectConfigType detects JSON and YAML formats, defaults to TOML.
+func detectConfigType(cfgfile string) string {
+ fileExt := filepath.Ext(cfgfile)
+ switch fileExt {
+ case ".json":
+ return "json"
+ case ".yaml", ".yml":
+ return "yaml"
+ }
+ return "toml"
+}
+
+// NewConfigFromString instantiates a new configuration based on the specified string.
+func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config {
+ logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"})
+ return newConfigFromString(logger, input, "toml")
+}
+
+func newConfigFromString(logger *logrus.Entry, input []byte, cfgtype string) *config {
+ viper.SetConfigType(cfgtype)
+ viper.SetEnvPrefix("matterbridge")
+ viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
+ viper.AutomaticEnv()
+
+ if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
+ logger.Fatalf("Failed to parse the configuration: %s", err)
+ }
+
+ cfg := &BridgeValues{}
+ if err := viper.Unmarshal(cfg); err != nil {
+ logger.Fatalf("Failed to load the configuration: %s", err)
+ }
+ return &config{
+ logger: logger,
+ v: viper.GetViper(),
+ cv: cfg,
+ }
+}
+
+func (c *config) BridgeValues() *BridgeValues {
+ return c.cv
+}
+
+func (c *config) Viper() *viper.Viper {
+ return c.v
+}
+
+func (c *config) IsKeySet(key string) bool {
+ c.RLock()
+ defer c.RUnlock()
+ return c.v.IsSet(key)
+}
+
+func (c *config) GetBool(key string) (bool, bool) {
+ c.RLock()
+ defer c.RUnlock()
+ return c.v.GetBool(key), c.v.IsSet(key)
+}
+
+func (c *config) GetInt(key string) (int, bool) {
+ c.RLock()
+ defer c.RUnlock()
+ return c.v.GetInt(key), c.v.IsSet(key)
+}
+
+func (c *config) GetString(key string) (string, bool) {
+ c.RLock()
+ defer c.RUnlock()
+ return c.v.GetString(key), c.v.IsSet(key)
+}
+
+func (c *config) GetStringSlice(key string) ([]string, bool) {
+ c.RLock()
+ defer c.RUnlock()
+ return c.v.GetStringSlice(key), c.v.IsSet(key)
+}
+
+func (c *config) GetStringSlice2D(key string) ([][]string, bool) {
+ c.RLock()
+ defer c.RUnlock()
+
+ res, ok := c.v.Get(key).([]interface{})
+ if !ok {
+ return nil, false
+ }
+ var result [][]string
+ for _, entry := range res {
+ result2 := []string{}
+ for _, entry2 := range entry.([]interface{}) {
+ result2 = append(result2, entry2.(string))
+ }
+ result = append(result, result2)
+ }
+ return result, true
+}
+
+func GetIconURL(msg *Message, iconURL string) string {
+ info := strings.Split(msg.Account, ".")
+ protocol := info[0]
+ name := info[1]
+ iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1)
+ iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1)
+ iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
+ return iconURL
+}
+
+type TestConfig struct {
+ Config
+
+ Overrides map[string]interface{}
+}
+
+func (c *TestConfig) IsKeySet(key string) bool {
+ _, ok := c.Overrides[key]
+ return ok || c.Config.IsKeySet(key)
+}
+
+func (c *TestConfig) GetBool(key string) (bool, bool) {
+ val, ok := c.Overrides[key]
+ if ok {
+ return val.(bool), true
+ }
+ return c.Config.GetBool(key)
+}
+
+func (c *TestConfig) GetInt(key string) (int, bool) {
+ if val, ok := c.Overrides[key]; ok {
+ return val.(int), true
+ }
+ return c.Config.GetInt(key)
+}
+
+func (c *TestConfig) GetString(key string) (string, bool) {
+ if val, ok := c.Overrides[key]; ok {
+ return val.(string), true
+ }
+ return c.Config.GetString(key)
+}
+
+func (c *TestConfig) GetStringSlice(key string) ([]string, bool) {
+ if val, ok := c.Overrides[key]; ok {
+ return val.([]string), true
+ }
+ return c.Config.GetStringSlice(key)
+}
+
+func (c *TestConfig) GetStringSlice2D(key string) ([][]string, bool) {
+ if val, ok := c.Overrides[key]; ok {
+ return val.([][]string), true
+ }
+ return c.Config.GetStringSlice2D(key)
+}