Exemple #1
0
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)
}
Exemple #2
0
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
}
Exemple #3
0
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)
}
Exemple #4
0
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.")
}
Exemple #5
0
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)
}
Exemple #6
0
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)
}
Exemple #7
0
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)
}
Exemple #8
0
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
}
Exemple #9
0
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
}
Exemple #10
0
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")
}
Exemple #11
0
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)
}
Exemple #12
0
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)
}
Exemple #13
0
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
}
Exemple #14
0
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
}
Exemple #15
0
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)
}
Exemple #16
0
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
}
Exemple #17
0
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)
}
Exemple #18
0
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)
}
Exemple #19
0
// 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
}
Exemple #20
0
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)
}
Exemple #21
0
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))
}
Exemple #22
0
// 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
}
Exemple #23
0
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)
}
Exemple #24
0
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)
}
Exemple #25
0
// Init for this module does nothing.
func (c *Config) Init(b core.Bot) error {
	b.Configuration()[ConfigOptionPassive] = "true"
	return nil
}