package main import ( "fmt" "net/http" "github.com/mattermost/mattermost-server/plugin" "github.com/pkg/errors" "sync" "reflect" "github.com/google/go-cmp/cmp" "os" "github.com/fatih/structs" "github.com/hashicorp/go-multierror" "github.com/kr/pretty" "strings" ) type Config struct { NumDoors uint8 `json:",string"` WatchPath string } func (c *Config) configMap() map[string]interface{} { var configMap map[string]interface{} = structs.Map(c) var lowerConfigMap map[string]interface{} = make(map[string]interface{}) for k, v := range(configMap) { lowerConfigMap[strings.ToLower(k)] = fmt.Sprintf("%v", v) } return lowerConfigMap } type HelloWorldPlugin struct { plugin.MattermostPlugin config *Config configLock sync.RWMutex configChanged chan struct{} doors map[uint8]bool configUpdates int } func (p *HelloWorldPlugin) initDoors() { p.doors = make(map[uint8]bool) for i := uint8(0); i < p.config.NumDoors; i++ { p.doors[i] = false } } func (p *HelloWorldPlugin) init() *HelloWorldPlugin { p.config = &Config{NumDoors: 1, WatchPath: "./"} p.configChanged = make(chan struct{}) p.initDoors() return p; } func (p *HelloWorldPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, world!") } func (p *HelloWorldPlugin) confChangedEvent() { select { case p.configChanged <- struct{}{}: default: } } func (p *HelloWorldPlugin) checkCorrectConfig() { var currentConfig map[string]interface{} = p.getLowerCasePluginConfig() var goodConfig map[string]interface{} = p.config.configMap() if !cmp.Equal(currentConfig, goodConfig) { p.API.LogInfo(fmt.Sprintf("%d: Saving un-matching config\n%# v\n%# v", p.configUpdates, pretty.Formatter(currentConfig), pretty.Formatter(goodConfig))) p.API.SavePluginConfig(goodConfig) } } func (p *HelloWorldPlugin) getLowerCasePluginConfig() map[string]interface{} { var currentConfig map[string]interface{} = p.API.GetPluginConfig() var lowerConfig map[string]interface{} = make(map[string]interface{}) for k, v := range(currentConfig) { lowerConfig[strings.ToLower(k)] = fmt.Sprintf("%v", v) } return lowerConfig } func (p *HelloWorldPlugin) OnConfigurationChange() error { p.configLock.Lock() defer p.configLock.Unlock() var currentConfig map[string]interface{} = p.getLowerCasePluginConfig() if cmp.Equal(currentConfig, p.config.configMap()) { p.API.LogInfo(fmt.Sprintf("%d: Skipping matching config", p.configUpdates)) return nil } defer p.checkCorrectConfig() var newConfig *Config = new(Config); if err := p.API.LoadPluginConfiguration(newConfig); err != nil { return errors.Wrap(err, fmt.Sprintf("%d: Failed to load configuration", p.configUpdates)) } if newConfig == p.config || reflect.ValueOf(*newConfig).NumField() == 0 || cmp.Equal(*newConfig, *p.config) { p.API.LogInfo("Passed same config, or empty?") return nil; } var configErr error = nil if _, err := os.Stat(newConfig.WatchPath); err != nil { newConfig.WatchPath = "./" configErr = multierror.Append(configErr, errors.Wrap(err, "Invalid watch path")) } p.config = newConfig p.configUpdates++ p.initDoors() p.API.LogInfo(fmt.Sprintf("%d: Config: %d %s", p.configUpdates, p.config.NumDoors, p.config.WatchPath)) return configErr } func watchLoop(p *HelloWorldPlugin) { for { p.configLock.Lock() p.configLock.Unlock() select { case <- p.configChanged: p.API.LogInfo(fmt.Sprintf("CONFIG CHANGED, RESTARTING %d", p.configUpdates)) } } } func (p *HelloWorldPlugin) OnActivate() error { go watchLoop(p) return nil } // This example demonstrates a plugin that handles HTTP requests which respond by greeting the // world. func main() { plugin.ClientMain((&HelloWorldPlugin{}).init()) }