func (c *Config) handleModule(b core.Bot, m *slack.Message) error { moduleText := "currently loaded modules:\n" for key := range b.LoadedModules() { moduleText = moduleText + fmt.Sprintf("> `%s`\n", key) } return b.Say(m.Channel, moduleText) }
func (j *Jira) fetchJiraIssues(b core.Bot, issueIds []string) ([]*external.JiraIssue, error) { issues := []*external.JiraIssue{} credentials, hasCredentials := b.Configuration()[ConfigJiraCredentials] if !hasCredentials { return issues, exception.New("Jarvis is not configured with Jira credentials.") } credentialPieces := strings.Split(credentials, ":") if len(credentialPieces) != 2 { return issues, exception.New("Jira credentials are not formatted correctly.") } jiraUser := credentialPieces[0] jiraPassword := credentialPieces[1] jiraHost, hasJiraHost := b.Configuration()[ConfigJiraHost] if !hasJiraHost { return issues, exception.New("Jarvis is not configured with a Jira host.") } var err error var issue *external.JiraIssue for _, issueID := range issueIds { issue, err = external.GetJiraIssue(jiraUser, jiraPassword, jiraHost, issueID) if err == nil { issues = append(issues, issue) } else { return issues, err } } return issues, nil }
func (c *Config) handleConfig(b core.Bot, m *slack.Message) error { configText := "current config:\n" for key, value := range b.Configuration() { if strings.HasPrefix(key, "option.") { configText = configText + fmt.Sprintf("> `%s` = %s\n", key, value) } } return b.Say(m.Channel, configText) }
func (j *Jobs) handleJobEnable(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) pieces := strings.Split(messageWithoutMentions, " ") if len(pieces) > 1 { taskName := pieces[len(pieces)-1] b.JobManager().EnableJob(taskName) return b.Sayf(m.Channel, "enabled job `%s`", taskName) } return exception.New("unhandled response.") }
func (j *Jobs) handleJobsStatus(b core.Bot, m *slack.Message) error { statusText := "current job statuses:\n" for _, status := range b.JobManager().Status() { if len(status.RunningFor) != 0 { statusText = statusText + fmt.Sprintf(">`%s` - state: %s running for: %s\n", status.Name, status.State, status.RunningFor) } else { statusText = statusText + fmt.Sprintf(">`%s` - state: %s\n", status.Name, status.State) } } return b.Say(m.Channel, statusText) }
func (c *Core) handleChannels(b core.Bot, m *slack.Message) error { if len(b.ActiveChannels()) == 0 { return b.Say(m.Channel, "currently listening to *no* channels.") } activeChannelsText := "currently listening to the following channels:\n" for _, channelID := range b.ActiveChannels() { if channel := b.FindChannel(channelID); channel != nil { activeChannelsText = activeChannelsText + fmt.Sprintf(">#%s (id:%s)\n", channel.Name, channel.ID) } } return b.Say(m.Channel, activeChannelsText) }
func (c *Config) handleConfigGet(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) parts := core.ExtractSubMatches(messageWithoutMentions, "^config:(.+)") if len(parts) < 2 { return exception.Newf("malformed message for `%s`", ActionConfigGet) } key := parts[1] value := b.Configuration()[key] return b.Sayf(m.Channel, "> %s: `%s` = %s", ActionConfigGet, key, value) }
func (s *Stocks) announceStocks(b core.Bot, destinationID string, stockInfo []external.StockInfo) error { tickersLabels := []string{} for _, stock := range stockInfo { tickersLabels = append(tickersLabels, fmt.Sprintf("`%s`", stock.Ticker)) } tickersLabel := strings.Join(tickersLabels, " ") leadText := fmt.Sprintf("current equity price info for %s", tickersLabel) message := slack.NewChatMessage(destinationID, leadText) message.AsUser = slack.OptionalBool(true) message.UnfurlLinks = slack.OptionalBool(false) message.Parse = util.OptionalString("full") for _, stock := range stockInfo { change := stock.Change changePct := stock.ChangePercent volume := stock.Volume tickerText := fmt.Sprintf("`%s`", stock.Ticker) nameText := fmt.Sprintf("%s", stock.Name) lastPriceText := fmt.Sprintf("%0.2f USD", stock.LastPrice) volumeText := humanize.Comma(volume) changeText := fmt.Sprintf("%.2f USD", change) changePctText := util.StripQuotes(changePct) var barColor = "#00FF00" if change < 0 { barColor = "#FF0000" } item := slack.ChatMessageAttachment{ Color: slack.OptionalString(barColor), Fields: []slack.Field{ slack.Field{Title: "Ticker", Value: tickerText, Short: true}, slack.Field{Title: "Name", Value: nameText, Short: true}, slack.Field{Title: "Last", Value: lastPriceText, Short: true}, slack.Field{Title: "Volume", Value: volumeText, Short: true}, slack.Field{Title: "Change ∆", Value: changeText, Short: true}, slack.Field{Title: "Change %", Value: changePctText, Short: true}, }, } message.Attachments = append(message.Attachments, item) } _, err := b.Client().ChatPostMessage(message) return err }
func (j *Jira) handleJira(b core.Bot, m *slack.Message) error { text := core.LessMentions(m.Text) issueIds := j.extractJiraIssues(text) if len(issueIds) == 0 { return nil } issues, err := j.fetchJiraIssues(b, issueIds) if err != nil { return err } if len(issues) == 0 { return nil } user := b.FindUser(m.User) leadText := fmt.Sprintf("*%s* has mentioned the following jira issues (%d): ", user.Profile.FirstName, len(issues)) message := slack.NewChatMessage(m.Channel, leadText) message.AsUser = slack.OptionalBool(true) message.UnfurlLinks = slack.OptionalBool(false) for _, issue := range issues { if !util.IsEmpty(issue.Key) { var itemText string if issue.Fields != nil { assignee := "Unassigned" if issue.Fields.Assignee != nil { assignee = issue.Fields.Assignee.DisplayName } itemText = fmt.Sprintf("%s %s\nAssigned To: %s", fmt.Sprintf("https://%s/browse/%s", b.Configuration()[ConfigJiraHost], issue.Key), issue.Fields.Summary, assignee, ) } else { itemText = fmt.Sprintf("%s\n%s", issue.Key, fmt.Sprintf("https://%s/browse/%s", b.Configuration()[ConfigJiraHost], issue.Key)) } item := slack.ChatMessageAttachment{ Color: slack.OptionalString("#3572b0"), Text: slack.OptionalString(itemText), } message.Attachments = append(message.Attachments, item) } } _, err = b.Client().ChatPostMessage(message) if err != nil { fmt.Printf("issue posting message: %v\n", err) } return err }
func (j *Jobs) handleJobRun(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) pieces := strings.Split(messageWithoutMentions, " ") if len(pieces) > 1 { jobName := pieces[len(pieces)-1] b.JobManager().RunJob(jobName) return b.Sayf(m.Channel, "ran job `%s`", jobName) } b.JobManager().RunAllJobs() return b.Say(m.Channel, "ran all jobs") }
func (c *Config) handleConfigSet(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) parts := core.ExtractSubMatches(messageWithoutMentions, "^config:(.+) (.+)") if len(parts) < 3 { return exception.Newf("malformed message for `%s`", ActionConfigSet) } key := parts[1] value := parts[2] setting := value if core.LikeAny(value, "true", "yes", "on", "1") { setting = "true" } else if core.LikeAny(value, "false", "off", "0") { setting = "false" } b.Configuration()[key] = setting return b.Sayf(m.Channel, "> %s: `%s` = %s", ActionConfigSet, key, setting) }
func (c *Core) handleTell(b core.Bot, m *slack.Message) error { messageText := core.LessSpecificMention(m.Text, b.ID()) words := strings.Split(messageText, " ") destinationUser := "" tellMessage := "" for x := 0; x < len(words); x++ { word := words[x] if core.Like(word, "tell") { continue } else if core.IsMention(word) { destinationUser = word tellMessage = strings.Join(words[x+1:], " ") } } tellMessage = core.ReplaceAny(tellMessage, "you are", "shes", "she's", "she is", "hes", "he's", "he is", "theyre", "they're", "they are") resultMessage := fmt.Sprintf("%s %s", destinationUser, tellMessage) return b.Say(m.Channel, resultMessage) }
func (c *Core) handleTime(b core.Bot, m *slack.Message) error { timeText := fmt.Sprintf("%s UTC", time.Now().UTC().Format(time.Kitchen)) message := slack.NewChatMessage(m.Channel, "") message.AsUser = slack.OptionalBool(true) message.UnfurlLinks = slack.OptionalBool(false) message.UnfurlMedia = slack.OptionalBool(false) message.Attachments = []slack.ChatMessageAttachment{ slack.ChatMessageAttachment{ Fallback: slack.OptionalString(fmt.Sprintf("The time is now:\n>%s", timeText)), Color: slack.OptionalString("#4099FF"), Pretext: slack.OptionalString("The time is now:"), Text: slack.OptionalString(timeText), }, } _, err := b.Client().ChatPostMessage(message) if err != nil { fmt.Printf("issue posting message: %v\n", err) } return err }
func (s *Stocks) handleStockChart(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) args := core.ExtractSubMatches(messageWithoutMentions, "^stock:chart (.*)") if len(args) < 2 { return exception.Newf("invalid input for %s", ActionStockPrice) } pieces := strings.Split(args[1], " ") ticker := pieces[0] timeframe := "1M" if len(pieces) > 1 { timeframe = pieces[1] } var imageURL string if strings.Contains(ticker, "+") { tickerPieces := strings.Split(ticker, "+") if len(tickerPieces) < 2 { return errors.New("invalid combination ticker") } imageURL = fmt.Sprintf("https://chart-service.charczuk.com/stock/chart/%s/%s?width=768&height=280&use_pct=true&add_sma=true&format=png&compare=%s", tickerPieces[0], timeframe, tickerPieces[1]) } else { imageURL = fmt.Sprintf("https://chart-service.charczuk.com/stock/chart/%s/%s?width=768&height=280&add_sma=true&format=png", ticker, timeframe) } leadText := fmt.Sprintf("Historical Chart for `%s`", ticker) message := slack.NewChatMessage(m.Channel, leadText) message.AsUser = slack.OptionalBool(true) message.UnfurlLinks = slack.OptionalBool(false) message.Parse = util.OptionalString("full") message.Attachments = []slack.ChatMessageAttachment{ slack.ChatMessageAttachment{ Title: util.OptionalString("Chart"), ImageURL: util.OptionalString(imageURL), }, } _, err := b.Client().ChatPostMessage(message) return err }
func (s *Stocks) handleStockPrice(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) pieces := core.ExtractSubMatches(messageWithoutMentions, "^stock:price (.*)") if len(pieces) < 2 { return exception.Newf("invalid input for %s", ActionStockPrice) } rawTicker := pieces[1] tickers := []string{} if strings.Contains(rawTicker, ",") { tickers = strings.Split(rawTicker, ",") } else { tickers = []string{rawTicker} } stockInfo, err := external.StockPrice(tickers) if err != nil { return err } if len(stockInfo) == 0 { return b.Sayf(m.Channel, "No stock information returned for: `%s`", strings.Join(tickers, ", ")) } return s.announceStocks(b, m.Channel, stockInfo) }
func (c *Core) handlePassiveCatchAll(b core.Bot, m *slack.Message) error { message := util.TrimWhitespace(core.LessMentions(m.Text)) if optionValue, hasOption := b.Configuration()[ConfigOptionPassiveCatchAll]; hasOption && optionValue == "true" { if core.IsAngry(message) { user := b.FindUser(m.User) response := []string{"slow down %s", "maybe calm down %s", "%s you should really relax", "chill %s", "it's ok %s, let it out"} return b.Sayf(m.Channel, core.Random(response), strings.ToLower(user.Profile.FirstName)) } } return nil }
func (u Util) handleUserID(b core.Bot, m *slack.Message) error { messageText := core.LessSpecificMention(m.Text, b.ID()) mentionedUserIDs := core.Mentions(messageText) outputText := "I looked up the following users:\n" for _, userID := range mentionedUserIDs { user := b.FindUser(userID) outputText = outputText + fmt.Sprintf("> %s : %s %s", userID, user.Profile.FirstName, user.Profile.LastName) } return b.Say(m.Channel, outputText) }
func (c *Core) handleHelp(b core.Bot, m *slack.Message) error { responseText := "Here are the commands that are currently configured:" for _, actionHandler := range b.Actions() { if !actionHandler.Passive { if len(actionHandler.MessagePattern) != 0 { responseText = responseText + fmt.Sprintf("\n>`%s` - %s", actionHandler.MessagePattern, actionHandler.Description) } else { responseText = responseText + fmt.Sprintf("\n>`*` - %s", actionHandler.Description) } } } responseText = responseText + "\nWith the following passive commands:" for _, actionHandler := range b.Actions() { if actionHandler.Passive { if len(actionHandler.MessagePattern) != 0 { responseText = responseText + fmt.Sprintf("\n>`%s` - %s", actionHandler.MessagePattern, actionHandler.Description) } else { responseText = responseText + fmt.Sprintf("\n>`*` - %s", actionHandler.Description) } } } return b.Say(m.Channel, responseText) }
// Init does nothing right now. func (j *Jobs) Init(b core.Bot) error { b.JobManager().LoadJob(jobs.NewClock(b)) b.JobManager().DisableJob("clock") return nil }
func (c *Core) handleUnknown(b core.Bot, m *slack.Message) error { return b.Sayf(m.Channel, "I don't know how to respond to this\n>%s", m.Text) }
func (c *Core) handleSalutation(b core.Bot, m *slack.Message) error { user := b.FindUser(m.User) salutation := []string{"hey %s", "hi %s", "hello %s", "ohayo gozaimasu %s", "salut %s", "bonjour %s", "yo %s", "sup %s"} return b.Sayf(m.Channel, core.Random(salutation), strings.ToLower(user.Profile.FirstName)) }
// Init for this module does nothing. func (j *Jira) Init(b core.Bot) error { if _, hasEntry := b.Configuration()[ConfigJiraCredentials]; !hasEntry { envCredentials := os.Getenv(EnvironmentJiraCredentials) if len(envCredentials) != 0 { b.Configuration()[ConfigJiraCredentials] = envCredentials } else { b.Logf("No `%s` provided, module `%s` cannot load.", EnvironmentJiraHost, ModuleJira) return nil } } if _, hasEntry := b.Configuration()[ConfigJiraHost]; !hasEntry { envHost := os.Getenv(EnvironmentJiraHost) if len(envHost) != 0 { b.Configuration()[ConfigJiraHost] = envHost } else { b.Logf("No `%s` provided, module `%s` cannot load.", EnvironmentJiraHost, ModuleJira) return nil } } return nil }
func (c *Config) handleUnloadModule(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessMentions(m.Text)) parts := core.ExtractSubMatches(messageWithoutMentions, "^module:unload (.+)") if len(parts) < 2 { return exception.Newf("malformed message for `%s`", ActionModuleUnload) } key := parts[1] if !b.LoadedModules().Contains(key) { return b.Sayf(m.Channel, "Module `%s` isn't loaded.", key) } if !b.RegisteredModules().Contains(key) { return b.Sayf(m.Channel, "Module `%s` isn't registered.", key) } b.UnloadModule(key) return b.Sayf(m.Channel, "Unloaded Module `%s`.", key) }
func (cr *ConsoleRunner) handleConsoleRunnerRun(b core.Bot, m *slack.Message) error { messageWithoutMentions := util.TrimWhitespace(core.LessSpecificMention(m.Text, b.ID())) cleanedMessage := core.FixLinks(messageWithoutMentions) pieces := strings.Split(cleanedMessage, " ") if len(pieces) < 2 { return exception.Newf("invalid arguments for `%s`", ActionConsoleRunnerRun) } commandWithArguments := pieces[1:] command := commandWithArguments[0] args := []string{} if len(commandWithArguments) > 1 { args = commandWithArguments[1:] } if !cr.isWhitelistedCommand(command) { return exception.Newf("`%s` cannot run %s", ActionConsoleRunnerRun, command) } cmdFullPath, lookErr := exec.LookPath(command) if lookErr != nil { return exception.Wrap(lookErr) } stdoutBuffer := bytes.NewBuffer([]byte{}) subCmd := exec.Command(cmdFullPath, args...) subCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} subCmd.StdoutPipe() subCmd.StderrPipe() subCmd.Stdout = stdoutBuffer subCmd.Stderr = stdoutBuffer startErr := subCmd.Start() if startErr != nil { return startErr } started := time.Now().UTC() didTimeout := false go func() { for { now := time.Now().UTC() if now.Sub(started) > ConsoleRunnerTimeout { didTimeout = true pgid, err := syscall.Getpgid(subCmd.Process.Pid) if err != nil { return } syscall.Kill(-pgid, 15) } time.Sleep(50 * time.Millisecond) } }() subCmd.Wait() timedOutText := "" if didTimeout { timedOutText = " (timed out)" } stdout := stdoutBuffer.String() outputText := fmt.Sprintf("console runner stdout%s:\n", timedOutText) if len(stdout) != 0 { prefixed := strings.Replace(stdout, "\n", "\n>", -1) outputText = outputText + fmt.Sprintf(">%s", prefixed) } else { outputText = "> empty" } return b.Say(m.Channel, outputText) }
// Init for this module does nothing. func (c *Config) Init(b core.Bot) error { b.Configuration()[ConfigOptionPassive] = "true" return nil }