Esempio n. 1
0
func (hook *SlackHook) Fire(sourceEntry *logrus.Entry) error {
	hook.client = slack.New(hook.token)

	params := slack.PostMessageParameters{
		Username:  hook.Username,
		IconURL:   hook.IconURL,
		IconEmoji: hook.IconEmoji,
	}

	var messageFields []slack.AttachmentField

	for key, value := range sourceEntry.Data {
		message := slack.AttachmentField{
			Title: key,
			Value: value.(string),
			Short: true,
		}

		messageFields = append(messageFields, message)
	}

	attachment := slack.Attachment{
		Color:      getColor(sourceEntry.Level),
		AuthorName: hook.AuthorName,
		Fields:     messageFields,
		Text:       sourceEntry.Message,
	}

	params.Attachments = []slack.Attachment{attachment}
	_, _, err := hook.client.PostMessage(hook.Channel, "", params)

	return err
}
Esempio n. 2
0
// SendMessage reads the configuration file, and posts a message about Kocho's invocation to Slack.
func SendMessage(version, build string) error {
	expanded, err := homedir.Expand(configPath)
	if err != nil {
		return err
	}
	if _, err := os.Stat(expanded); os.IsNotExist(err) {
		return errgo.Mask(ErrNotConfigured, errgo.Any)
	}

	slackConfiguration := SlackConfiguration{
		NotificationUsername: "******",
		EmojiIcon:            ":robot_face:",
	}

	configFile, err := os.Open(expanded)
	if err != nil {
		return errgo.WithCausef(err, ErrInvalidConfiguration, "couldn't open Slack configuration file")
	}
	defer configFile.Close()

	if err := json.NewDecoder(configFile).Decode(&slackConfiguration); err != nil {
		return errgo.WithCausef(err, ErrInvalidConfiguration, "couldn't decode Slack configuration")
	}

	client := slack.New(slackConfiguration.Token)

	params := slack.PostMessageParameters{}
	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Color: "#2484BE",
			Text:  fmt.Sprintf("*Kocho*: %s ran `%s`", slackConfiguration.Username, strings.Join(os.Args, " ")),
			Fields: []slack.AttachmentField{
				slack.AttachmentField{
					Title: "Kocho Version",
					Value: version,
					Short: true,
				},
				slack.AttachmentField{
					Title: "Kocho Build",
					Value: build,
					Short: true,
				},
			},
			MarkdownIn: []string{"text"},
		},
	}
	params.Username = slackConfiguration.NotificationUsername
	params.IconEmoji = slackConfiguration.EmojiIcon

	if _, _, err := client.PostMessage(slackConfiguration.NotificationChannel, "", params); err != nil {
		return err
	}

	return nil
}
Esempio n. 3
0
func main() {
	api := slack.New(os.Getenv("SLACK_TOKEN"))
	params := slack.PostMessageParameters{
		Username:  "******",
		IconEmoji: ":eye:", // or :ghost:, :eyes:
	}

	// https://api.slack.com/docs/attachments
	attachment := slack.Attachment{
		Color: "#439FE0", // good, warning, danger, or any hex color code (eg. #439FE0)

		AuthorName:    "Author Name",
		AuthorSubname: "Author Subname",

		Title:     "Title",
		TitleLink: "TitleLink",
		Pretext:   "Attachment pretext",
		Text:      "Attachment Text (with markup).\n*bold* `code` _italic_",
		// ImageURL:  "http://daniel-lauzon.com/img/about-me-trans.png",
		ThumbURL: "https://en.gravatar.com/userimage/2562383/0a6632e59a6821599a51eace02d754ea.jpeg",

		MarkdownIn: []string{"text"}, // ["text", "pretext","fields"]

		// Uncomment the following part to send a field too
		Fields: []slack.AttachmentField{
			slack.AttachmentField{
				Title: "Reported by",
				Value: "Snookr",
				Short: true,
			},
			slack.AttachmentField{
				Title: "Priority",
				Value: "Must Respond",
				Short: true,
			},
		},
	}
	params.Attachments = []slack.Attachment{attachment}
	channelID, timestamp, err := api.PostMessage("#qcic", "Some text mentioning @daniel", params)
	if err != nil {
		fmt.Printf("%s\n", err)
		return
	}
	fmt.Printf("Message successfully sent to channel %s at %s\n", channelID, timestamp)
}
Esempio n. 4
0
func (s *Slack) onMessage(message *Message) error {
	postMessage := slack.PostMessageParameters{
		Username:  message.Name,
		LinkNames: 1,
	}

	re := regexp.MustCompile("^:.*:$")
	if re.MatchString(message.Icon) {
		postMessage.IconEmoji = message.Icon
	} else {
		postMessage.IconURL = message.Icon
	}

	if message.Attachment != nil {
		attachment := slack.Attachment{
			Fallback:   message.Attachment.Fallback,
			Color:      message.Attachment.Color,
			Pretext:    message.Attachment.Pretext,
			AuthorName: message.Attachment.AuthorName,
			AuthorLink: message.Attachment.AuthorLink,
			AuthorIcon: message.Attachment.AuthorIcon,
			Title:      message.Attachment.Title,
			TitleLink:  message.Attachment.TitleLink,
			Text:       message.Attachment.Text,
			ImageURL:   message.Attachment.ImageURL,
			MarkdownIn: []string{"text", "pretext", "fields"},
		}
		if len(message.Attachment.Fields) > 0 {
			fields := make([]slack.AttachmentField, len(message.Attachment.Fields))
			for i := range fields {
				fields[i].Title = message.Attachment.Fields[i].Title
				fields[i].Value = message.Attachment.Fields[i].Value
				fields[i].Short = message.Attachment.Fields[i].Short
			}
			attachment.Fields = fields
		}
		postMessage.Attachments = []slack.Attachment{attachment}
	}

	_, _, err := s.Client.PostMessage(message.Channel, message.Message, postMessage)
	return err
}
Esempio n. 5
0
func sendMessage(issue Issue, channel string) error {
	params := slack.PostMessageParameters{}
	text := fmt.Sprintf("*%s*\n\n *Assignee* %s *Priority* %s ", issue.Fields.Summary, issue.Fields.Assignee.DisplayName, issue.Fields.Priority.Name)
	attachment := slack.Attachment{
		Title:      issue.Key,
		TitleLink:  fmt.Sprintf("%s/browse/%s", jiraHostURL, issue.Key),
		Text:       text,
		Color:      getColor(issue.Fields.Status.Name),
		MarkdownIn: []string{"text", "pretext"},
	}
	params.Attachments = []slack.Attachment{attachment}
	params.IconURL = jiraIcon
	params.Username = "******"
	_, _, err := Slack.PostMessage(channel, "", params)
	if err != nil {
		fmt.Printf("%s\n", err)
		return err
	}
	return nil

}
Esempio n. 6
0
func post(rtm *slack.RTM, channel string, message Message, debug bool) {
	t := time.Now()
	ts := t.Format("Mon Jan 2 15:04:05 -0700 MST 2006")
	params := slack.PostMessageParameters{
		Username: "******",
	}
	attachment := slack.Attachment{
		Pretext: message.Subject,
		Text:    message.Detail,
	}
	params.Attachments = []slack.Attachment{attachment}

	title := fmt.Sprintf("Alert *%s* with Severity %d (Magnitude: %d, Floater: %.4f)", message.Subject, message.Severity, message.Magnitude, message.Floater)
	channelID, timestamp, err := rtm.PostMessage(channel, title, params)
	if err != nil {
		fmt.Printf("[%s] ERROR Error received: %s\n", ts, err)
		return
	}
	if debug {
		fmt.Printf("[%s] INFO Message %+v successfully sent to channel %s at %s", ts, message, channelID, timestamp)
	}
}
Esempio n. 7
0
func main() {
	api := slack.New("YOUR_TOKEN_HERE")
	params := slack.PostMessageParameters{}
	attachment := slack.Attachment{
		Pretext: "some pretext",
		Text:    "some text",
		// Uncomment the following part to send a field too
		/*
			Fields: []slack.AttachmentField{
				slack.AttachmentField{
					Title: "a",
					Value: "no",
				},
			},
		*/
	}
	params.Attachments = []slack.Attachment{attachment}
	channelID, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params)
	if err != nil {
		fmt.Printf("%s\n", err)
		return
	}
	fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
}
// ReplyWithAttachments replys to a message event with a Slack Attachments message.
func (b *Bot) ReplyWithAttachments(channel string, attachments []slack.Attachment, typing bool) {
	params := slack.PostMessageParameters{AsUser: true}
	params.Attachments = attachments

	b.Client.PostMessage(channel, "", params)
}
Esempio n. 9
0
// createMessage generates the message to post to Slack.
func createMessage(p Plugin, user *slack.User) slack.PostMessageParameters {
	var messageOptions MessageOptions
	var color string
	var messageTitle string

	// Determine if the build was a success
	if p.Build.Status == "success" {
		messageOptions = p.Config.Success
		color = "good"
		messageTitle = "Build succeeded"
	} else {
		messageOptions = p.Config.Failure
		color = "danger"
		messageTitle = "Build failed"
	}

	// setup the message
	messageParams := slack.PostMessageParameters{
		Username: messageOptions.Username,
	}

	if strings.HasPrefix(messageOptions.Icon, "http") {
		log.Info("Icon is a URL")
		messageParams.IconURL = messageOptions.Icon
	} else {
		log.Info("Icon is an emoji")
		messageParams.IconEmoji = messageOptions.Icon
	}

	// setup the payload
	payload := templatePayload{
		Build:     p.Build,
		Repo:      p.Repo,
		BuildLast: p.BuildLast,
		User:      user,
	}

	messageText, err := template.Render(messageOptions.Template, &payload)

	if err != nil {
		log.Error("Could not parse template")
	}

	// create the attachment
	attachment := slack.Attachment{
		Color:     color,
		Text:      messageText,
		Title:     messageTitle,
		TitleLink: p.Build.Link,
	}

	// Add image if any are provided
	imageCount := len(messageOptions.ImageAttachments)

	if imageCount > 0 {
		log.WithFields(log.Fields{
			"count": imageCount,
		}).Info("Choosing from images")
		rand.Seed(time.Now().UTC().UnixNano())
		attachment.ImageURL = messageOptions.ImageAttachments[rand.Intn(imageCount)]
	}

	messageParams.Attachments = []slack.Attachment{attachment}

	return messageParams
}
Esempio n. 10
0
// ReplyWithAttachments replys to a message event with a Slack Attachments message.
func (b *Bot) ReplyWithAttachments(evt *slack.MessageEvent, attachments []slack.Attachment, typing bool) {
	params := slack.PostMessageParameters{AsUser: true}
	params.Attachments = attachments

	b.Client.PostMessage(evt.Msg.Channel, "", params)
}
Esempio n. 11
0
func pull(rtm *slack.RTM, o Opts) {
	/*
		Using a FIFO queue
	*/
	t := time.Now()
	ts := t.Format("Mon Jan 2 15:04:05 -0700 MST 2006")
	rc, err := cluster.NewCluster(o.redis_connection)

	if err != nil {
		fmt.Printf("[%s] ERROR Redis connection error: %s\n", ts, err)
		os.Exit(1)
	}
	r := rc.Cmd("SELECT", o.redis_db)

	for {
		t = time.Now()
		ts = t.Format("Mon Jan 2 15:04:05 -0700 MST 2006")
		r = rc.Cmd("RPOP", o.redis_list)

		switch r.Type {
		case redis.ErrorReply:
			fmt.Printf("[%s] ERROR ErrorReply received: %s\n", ts, r.Err.Error())
		case redis.NilReply:
			if o.debug {
				fmt.Printf("[%s] INFO NilReply reply received\n", ts)
			}
		case redis.StatusReply:
			if o.debug {
				fmt.Printf("[%s] INFO StatusReply reply received: not processing\n", ts)
			}
		case redis.BulkReply:
			// Send to Slack
			data, err := r.Bytes()
			if err != nil {
				fmt.Printf("[%s] ERROR Error received: %s\n", ts, err)
			} else {
				if o.json {
					type Message struct {
						Name   string
						Source string
						Detail string
					}
					var message Message
					err := json.Unmarshal(data, &message)
					if err != nil {
						fmt.Printf("[%s] ERROR Error received: %s\n", ts, err)
					}
					params := slack.PostMessageParameters{
						Username: "******",
					}
					attachment := slack.Attachment{
						Pretext: message.Source,
						Text:    message.Detail,
					}
					params.Attachments = []slack.Attachment{attachment}

					channelID, timestamp, err := rtm.PostMessage(o.slack_channel, string(message.Name), params)
					if err != nil {
						fmt.Printf("[%s] ERROR Error received: %s\n", ts, err)
						return
					}
					if o.debug {
						fmt.Printf("[%s] INFO Message %+v successfully sent to channel %s at %s", ts, message, channelID, timestamp)
					}
				} else {
					if o.debug {
						fmt.Printf("[%s] INFO BulkReply reply received: %s\n", ts, data)
					}
					rtm.SendMessage(rtm.NewOutgoingMessage(string(data), o.slack_channel))
				}
			}
		case redis.MultiReply:
			if o.debug {
				fmt.Printf("[%s] INFO MultiReply reply received: not processing\n", ts)
			}
		case redis.IntegerReply:
			if o.debug {
				fmt.Printf("[%s] INFO IntegerReply reply received: not processing\n", ts)
			}
		default:
			if o.debug {
				fmt.Printf("[%s] INFO Unknown reply received: not processing\n", ts)
			}
		}

		time.Sleep(time.Duration(o.watch_interval) * time.Millisecond)
	}
}
Esempio n. 12
0
// Post summary to Slack channel
func postDigest() {

	channel := os.Getenv("DIGEST_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
		os.Exit(1)
	}

	s := slack.New(botToken)
	users, err := s.GetUsers()

	if err != nil {
		log.Fatal("Cannot get users")
		os.Exit(1)
	}

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	log.Info("Preparing data")
	// If count > 0, it means there is data to show
	count := 0
	title := " >> Yesterday I did: "
	params := slack.PostMessageParameters{}
	fields := []slack.AttachmentField{}

	yesterday := arrow.Yesterday().UTC()
	toDate := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, time.UTC)

	// Prepare attachment of done items
	for _, user := range users {

		if user.IsBot || user.Deleted {
			continue
		}

		// log.Info("Process user: "******" - " + user.Id)

		// Query done items from Database
		var values string
		var items []Item

		err = ctx.C("items").Find(bson.M{"$and": []bson.M{
			bson.M{"user_id": user.Id},
			bson.M{"created_at": bson.M{"$gt": toDate}},
		},
		}).All(&items)

		if err != nil {
			log.Fatal("Cannot query done items.")
			os.Exit(1)
		}

		for _, item := range items {
			values = values + " + " + item.Text + "\n\t"
		}

		// <@U024BE7LH|bob>
		if len(items) > 0 {

			count = count + 1
			field := slack.AttachmentField{
				Title: "@" + user.Name,
				Value: values,
			}

			fields = append(fields, field)
		}
	}

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Color:  "#7CD197",
			Fields: fields,
		},
	}

	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "******"

	if count > 0 {
		s.PostMessage(channel, title, params)
	}
}
Esempio n. 13
0
					Value: sensuhandler.DefineSensuEnv(sensuEnv.Sensu.Environment),
					Short: true,
				},
				slack.AttachmentField{
					Title: "Check Event ID",
					Value: sensuEvent.ID,
					Short: true,
				},
				slack.AttachmentField{
					Title: "Check Output",
					Value: sensuhandler.CleanOutput(sensuEvent.Check.Output),
					Short: true,
				},
			},
		}
		params.Attachments = []slack.Attachment{attachment}
		_, _, err := api.PostMessage(channelID, "", params)
		if err != nil {
			syslogLog.WithFields(logrus.Fields{
				"check":   "sensupluginsslack",
				"client":  host,
				"version": version.AppVersion(),
				"error":   err,
			}).Error(`Slack attachment could not be sent`)
			sensuutil.Exit("RUNTIMEERROR")
		}
		syslogLog.WithFields(logrus.Fields{
			"check":   "sensupluginsslack",
			"client":  host,
			"version": version.AppVersion(),
		}).Error(`Slack attachment has been sent`)
Esempio n. 14
0
func (s *Client) sendMessage(issue *redmine.Issue, channel string) (err error) {
	params := slackapi.PostMessageParameters{}
	params.IconURL = botLogo
	params.Username = "******"

	fields := make([]slackapi.AttachmentField, 6)
	var idx int = 3

	fields[0] = slackapi.AttachmentField{
		Title: "Project",
		Value: issue.Project.Name,
		Short: true,
	}
	fields[1] = slackapi.AttachmentField{
		Title: "Status",
		Value: issue.Status.Name,
		Short: true,
	}
	fields[2] = slackapi.AttachmentField{
		Title: "Author",
		Value: issue.Author.Name,
		Short: true,
	}
	if issue.AssignedTo != nil {
		fields[idx] = slackapi.AttachmentField{
			Title: "Assigned To",
			Value: issue.AssignedTo.Name,
			Short: true,
		}
		idx += 1
	}
	if issue.Category != nil {
		fields[idx] = slackapi.AttachmentField{
			Title: "Category",
			Value: issue.Category.Name,
			Short: true,
		}
		idx += 1
	}
	if issue.Version != nil {
		fields[idx] = slackapi.AttachmentField{
			Title: "Version",
			Value: issue.Version.Name,
			Short: true,
		}
	}

	var title string
	if issue.Tracker != nil {
		title = fmt.Sprintf("%s #%d: %s", issue.Tracker.Name, issue.Id, issue.Subject)
	} else {
		title = fmt.Sprintf("#%d: %s", issue.Id, issue.Subject)
	}

	attachment := slackapi.Attachment{
		Title:     title,
		TitleLink: s.redmine.GetIssueUrl(issue),
		Fields:    fields,
	}

	if s.redmine.IssueIsClosed(issue) {
		attachment.Color = "good"
	} else if s.redmine.IssueInHighPriority(issue) {
		attachment.Color = "danger"
	}

	params.Attachments = []slackapi.Attachment{attachment}

	_, _, err = s.slack.PostMessage(channel, "", params)

	return err
}
Esempio n. 15
0
func main() {
	fmt.Printf("Drone Slack Blame Plugin built at %s\n", buildDate)

	repo := plugin.Repo{}
	build := plugin.Build{}
	system := plugin.System{}
	vargs := Slack{}

	plugin.Param("build", &build)
	plugin.Param("system", &system)
	plugin.Param("repo", &repo)
	plugin.Param("vargs", &vargs)

	// parse the parameters
	if err := plugin.Parse(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	// setup the message
	buildLink := fmt.Sprintf("%s/%s/%d", system.Link, repo.FullName, build.Number)
	var messageOptions MessageOptions
	var color string
	var messageText string
	var channelText string

	// Determine if the build was a success
	if build.Status == "success" {
		messageOptions = vargs.Success
		color = "good"
		messageText = fmt.Sprintf("Build succeeded at %s", buildLink)
		channelText = "Thanks"
	} else {
		messageOptions = vargs.Failure
		color = "danger"
		messageText = fmt.Sprintf("Build failed at %s", buildLink)
		channelText = "Blame"
	}

	// set default values
	if len(messageOptions.Username) == 0 {
		messageOptions.Username = "******"
	}

	if len(messageOptions.Icon) == 0 {
		messageOptions.Icon = ":drone:"
	}

	if len(messageOptions.ImageAttachments) == 0 {
		messageOptions.ImageAttachments = []string{""}
	}

	// setup the message
	messageParams := slack.PostMessageParameters{
		Username:  messageOptions.Username,
		IconEmoji: messageOptions.Icon,
	}

	imageCount := len(messageOptions.ImageAttachments)
	rand.Seed(time.Now().UTC().UnixNano())

	attachment := slack.Attachment{
		Color:    color,
		Text:     messageText,
		ImageURL: messageOptions.ImageAttachments[rand.Intn(imageCount)],
	}

	messageParams.Attachments = []slack.Attachment{attachment}

	// get the commit author
	commitAuthor := build.Email

	// create the slack api
	api := slack.New(vargs.Token)

	// get the users
	//
	// Slack doesn't let you search by email so just need to get
	// everything and find the user in question
	var blameUser *slack.User

	users, _ := api.GetUsers()

	for _, user := range users {
		if user.Profile.Email == commitAuthor {
			fmt.Printf("%s\n", user.Name)
			fmt.Printf("%s\n", user.Profile.Email)
			blameUser = &user
			break
		}
	}

	// notify the user if possible
	var userAt string

	if blameUser != nil {
		userAt = fmt.Sprintf("@%s", blameUser.Name)

		// send the message to the user's channel
		//
		// this will appear through slackbot
		_, _, err := api.PostMessage(userAt, messageOptions.Message, messageParams)

		if err == nil {
			fmt.Printf("User %s notified\n", userAt)
		} else {
			fmt.Printf("Could not notify user %s!\n", userAt)
		}
	} else {
		userAt = build.Author
		fmt.Print("User could not be found")
	}

	// notify the channel if requested
	if len(vargs.Channel) != 0 {
		if !strings.HasPrefix(vargs.Channel, "#") {
			vargs.Channel = "#" + vargs.Channel
		}

		_, _, err := api.PostMessage(vargs.Channel, fmt.Sprintf("%s %s %s", messageOptions.Message, channelText, userAt), messageParams)

		if err == nil {
			fmt.Printf("Channel notified\n")
		} else {
			fmt.Printf("Could not notify channel!\n")
		}
	}
}
Esempio n. 16
0
func main() {

	slack_token := os.Getenv("SLACK_TOKEN")
	slack_group := os.Getenv("SLACK_GROUP")
	card_no := os.Getenv("CARD_NO")
	if slack_token == "" || slack_group == "" || card_no == "" {
		log.Fatal("SLACK_TOKEN=xxxx-1111111111-1111111111-11111111111-111111 SLACK_GROUP=GXXXXXXXX CARD_NO=1111222233334444 ./go-ubot-oddday-checker (version: " + version.Version + ")")
	}

	res, err := goreq.Request{Uri: UBOT_ODDDAY_URL}.Do()
	if err != nil {
		log.Fatal(err)
	}
	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	tbCode, found := doc.Find("#tbCode").Attr("value")
	if !found {
		log.Error("Cannot find tbCode.")
		return
	}

	viewstate, found := doc.Find("#__VIEWSTATE").Attr("value")
	if !found {
		log.Error("Cannot find viewstate.")
		return
	}

	eventvalidation, found := doc.Find("#__EVENTVALIDATION").Attr("value")
	if !found {
		log.Error("Cannot find eventvalidation.")
		return
	}

	form := url.Values{}
	form.Add("__EVENTTARGET", "")
	form.Add("__EVENTARGUMENT", "")
	form.Add("__VIEWSTATE", viewstate)
	form.Add("tbCode", tbCode)
	form.Add("__CALLBACKID", "__Page")
	form.Add("__CALLBACKPARAM", "QRY%%"+card_no+"%%"+tbCode+"%%"+tbCode+"%%")
	form.Add("__EVENTVALIDATION", eventvalidation)

	res, err = goreq.Request{
		Method:      "POST",
		Uri:         UBOT_ODDDAY_URL,
		UserAgent:   "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0",
		ContentType: "application/x-www-form-urlencoded",
		Body:        form.Encode(),
	}.WithHeader("Referer", UBOT_ODDDAY_URL).Do()
	if err != nil {
		log.Fatal(err)
	}
	body, _ := res.Body.ToString()

	// Parse the HTML into nodes
	rp := regexp.MustCompile(`LOGINOK@@[^@]+@@([^@]+)@@[^@]+`)
	m := rp.FindStringSubmatch(body)
	if m == nil {
		log.Fatalf("Cannot find expected response: %s", body)
	}

	log.Debugf("Response: %s", m[1])

	doc, err = goquery.NewDocumentFromReader(strings.NewReader(m[1]))
	if err != nil {
		log.Fatal(err)
	}

	api := slack.New(slack_token)
	mParams := slack.PostMessageParameters{}
	attachment := slack.Attachment{}

	doc.Find("tr").Each(func(i int, s *goquery.Selection) {
		sel := s.Find("td")
		month := strings.TrimSpace(sel.Nodes[0].FirstChild.Data)
		count := strings.TrimSpace(sel.Nodes[1].FirstChild.Data)
		money := strings.TrimSpace(sel.Nodes[2].FirstChild.Data)
		log.Debugf("%s,%s,%s", month, count, money)
		field := slack.AttachmentField{
			Title: month,
			Value: count + " (" + money + ")",
		}
		attachment.Fields = append(attachment.Fields, field)
	})

	// Query all logs in past 1 month
	hParams := slack.NewHistoryParameters()
	hParams.Oldest = fmt.Sprint(time.Now().AddDate(0, -1, 0).Unix())
	history, err := api.GetGroupHistory(slack_group, hParams)
	if err != nil {
		log.Fatal(err)
	}
	for _, msg := range history.Messages {
		if msg.Text == TITLE {
			for _, _attachement := range msg.Attachments {
				// Compare attachment
				if reflect.DeepEqual(_attachement, attachment) {
					log.Debug("Found exist in slack. Skip.")
					return
				}
			}
		}
	}

	// Notify new message
	mParams.Attachments = []slack.Attachment{attachment}
	_, _, err = api.PostMessage(slack_group, TITLE, mParams)
	if err != nil {
		log.Fatal(err)
	}
}