package sco import ( "os" "os/signal" "regexp" "strings" "github.com/mattermost/mattermost-server/v5/model" ) type Bot struct { botName string apiURL string websocketURL string accountEmail string accountPassword string accountUsername string accountFirstname string accountLastname string teamName string debuggingChannelName string client *model.Client4 webSocketClient *model.WebSocketClient botUser *model.User botTeam *model.Team debuggingChannel *model.Channel } func New(options ...func(s *Bot)) *Bot { b := new(Bot) for _, opt := range options { opt(b) } return b } func (b *Bot) Main() { println(b.botName) b.SetupGracefulShutdown() b.client = model.NewAPIv4Client(b.apiURL) // Lets test to see if the mattermost server is up and running b.MakeSureServerIsRunning() // lets attempt to login to the Mattermost server as the bot user // This will set the token required for all future calls // You can get this token with client.AuthToken b.LoginAsTheBotUser() // If the bot user doesn't have the correct information lets update his profile b.UpdateTheBotUserIfNeeded() // Lets find our bot team b.FindBotTeam() // This is an important step. Lets make sure we use the botTeam // for all future web service requests that require a team. //client.SetTeamId(botTeam.Id) // Lets create a bot channel for logging debug messages into b.CreateBotDebuggingChannelIfNeeded() b.SendMsgToDebuggingChannel("_"+b.botName+" has **started** running_", "") // Lets start listening to some channels via the websocket! var err *model.AppError b.webSocketClient, err = model.NewWebSocketClient4(b.websocketURL, b.client.AuthToken) if err != nil { println("We failed to connect to the web socket") PrintError(err) } b.webSocketClient.Listen() go func() { for { select { case resp := <-b.webSocketClient.EventChannel: b.HandleWebSocketResponse(resp) } } }() // You can block forever with select {} } func (b *Bot) MakeSureServerIsRunning() { if props, resp := b.client.GetOldClientConfig(""); resp.Error != nil { println("There was a problem pinging the Mattermost server. Are you sure it's running?") PrintError(resp.Error) os.Exit(1) } else { println("Server detected and is running version " + props["Version"]) } } func (b *Bot) LoginAsTheBotUser() { if user, resp := b.client.Login(b.accountEmail, b.accountPassword); resp.Error != nil { println("There was a problem logging into the Mattermost server. Are you sure ran the setup steps from the README.md?") PrintError(resp.Error) os.Exit(1) } else { b.botUser = user } } func (b *Bot) UpdateTheBotUserIfNeeded() { if b.botUser.FirstName != b.accountFirstname || b.botUser.LastName != b.accountLastname || b.botUser.Username != b.accountUsername { b.botUser.FirstName = b.accountFirstname b.botUser.LastName = b.accountLastname b.botUser.Username = b.accountUsername if user, resp := b.client.UpdateUser(b.botUser); resp.Error != nil { println("We failed to update the Bot user account") PrintError(resp.Error) os.Exit(1) } else { b.botUser = user println("Looks like this might be the first run so we've updated the bots account settings") } } } func (b *Bot) FindBotTeam() { if team, resp := b.client.GetTeamByName(b.teamName, ""); resp.Error != nil { println("We failed to get the initial load") println("or we do not appear to be a member of the team '" + b.teamName + "'") PrintError(resp.Error) os.Exit(1) } else { b.botTeam = team } } func (b *Bot) CreateBotDebuggingChannelIfNeeded() { if rchannel, resp := b.client.GetChannelByName(b.debuggingChannelName, b.botTeam.Id, ""); resp.Error != nil { println("We failed to get the channels") PrintError(resp.Error) } else { b.debuggingChannel = rchannel return } // Looks like we need to create the logging channel channel := &model.Channel{} channel.Name = b.debuggingChannelName channel.DisplayName = "Debugging For Bot" channel.Purpose = "This is used as a test channel for logging bot debug messages" channel.Type = model.CHANNEL_OPEN channel.TeamId = b.botTeam.Id if rchannel, resp := b.client.CreateChannel(channel); resp.Error != nil { println("We failed to create the channel " + b.debuggingChannelName) PrintError(resp.Error) } else { b.debuggingChannel = rchannel println("Looks like this might be the first run so we've created the channel " + b.debuggingChannelName) } } func (b *Bot) SendMsgToDebuggingChannel(msg string, replyToId string) { post := &model.Post{} post.ChannelId = b.debuggingChannel.Id post.Message = msg post.RootId = replyToId if _, resp := b.client.CreatePost(post); resp.Error != nil { println("We failed to send a message to the logging channel") PrintError(resp.Error) } } func (b *Bot) HandleWebSocketResponse(event *model.WebSocketEvent) { b.HandleMsgFromDebuggingChannel(event) } func (b *Bot) HandleMsgFromDebuggingChannel(event *model.WebSocketEvent) { // If this isn't the debugging channel then lets ingore it if event.Broadcast.ChannelId != b.debuggingChannel.Id { return } // Lets only reponded to messaged posted events if event.Event != model.WEBSOCKET_EVENT_POSTED { return } println("responding to debugging channel msg") post := model.PostFromJson(strings.NewReader(event.Data["post"].(string))) if post != nil { // ignore my events if post.UserId == b.botUser.Id { return } // if you see any word matching 'alive' then respond if matched, _ := regexp.MatchString(`(?:^|\W)alive(?:$|\W)`, post.Message); matched { b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) return } // if you see any word matching 'up' then respond if matched, _ := regexp.MatchString(`(?:^|\W)up(?:$|\W)`, post.Message); matched { b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) return } // if you see any word matching 'running' then respond if matched, _ := regexp.MatchString(`(?:^|\W)running(?:$|\W)`, post.Message); matched { b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) return } // if you see any word matching 'hello' then respond if matched, _ := regexp.MatchString(`(?:^|\W)hello(?:$|\W)`, post.Message); matched { b.SendMsgToDebuggingChannel("Yes I'm running", post.Id) return } } b.SendMsgToDebuggingChannel("I did not understand you!", post.Id) } func PrintError(err *model.AppError) { println("\tError Details:") println("\t\t" + err.Message) println("\t\t" + err.Id) println("\t\t" + err.DetailedError) } func (b *Bot) SetupGracefulShutdown() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for _ = range c { if b.webSocketClient != nil { b.webSocketClient.Close() } b.SendMsgToDebuggingChannel("_"+b.botName+" has **stopped** running_", "") os.Exit(0) } }() }