CTOに対抗してGoでSlack slash command作りました

[LOB Advent Calendar Advent Calendar 2018 - Qiita](https://qiita.com/advent-calendar/2018/lob-inc)カレンダー18日の記事です。


我が社のCTOことmojibkoさんが、[TypeScript で書いた Slack Slash Command を Firebase で動かしたらめちゃくちゃ簡単だった件について](https://qiita.com/mojibakeo/items/93012c4466c7e1c0e3ce#slack-%E5%81%B4%E3%81%A7-slash-command-%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B) でTypeScriptでslash commandを作っていたので、対抗して私はGoでslash commandを作ってみました。(firebaseだとめっちゃ簡単だよ的な所はご紹介できないのですが、すみません。)


## 何作ったんや
```/big :custom絵文字:```を打つとmigawari(slack bot)が画像として大きく表示してくれる
![slash command2.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/174728/0ad421d7-bf0c-1151-a7c4-034a90cde31e.gif)



## どうやって作ったの
1. slash commandの設定をする
    - [CTOの記事] [slack側で slash commandを設定する](https://qiita.com/mojibakeo/items/93012c4466c7e1c0e3ce#slack-%E5%81%B4%E3%81%A7-slash-command-%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)を参考になさってください

2. Goの実装

実装はこんな感じになります。

```go
func main() {
	logger.Infof("Start big-stamp server.")
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), nil))
}

func handle(w http.ResponseWriter, r *http.Request) {
	token := os.Getenv("SLASHCOMMAND")
	if err := r.ParseForm(); err != nil {
		http.Error(w, "Error parsing form.", http.StatusBadRequest)
		return
	}
	channelID := r.Form.Get("channel_id")
	texts := strings.Split(r.Form.Get("text"), " ")
	stampMap := map[string]bool{}
	for _, text := range texts {
		stampMap[text] = false
	}
	sendMsgUrl := "https://slack.com/api/chat.postMessage"

	emojiMsg := emojiList(w, token)
	for k, imgUrl := range emojiMsg {
		ek := fmt.Sprintf(":%s:", k)
		if _, ok := stampMap[ek]; ok {
			sendMsgUrlOption := url.Values{}
			sendMsgUrlOption.Add("token", token)
			sendMsgUrlOption.Add("channel", channelID)
			sendMsgUrlOption.Add("attachments", "[{\"\": \"\", \"text\": \"\", \"image_url\": \""+imgUrl.(string)+"\"}]")
			_, err := http.Post(sendMsgUrl+"?"+sendMsgUrlOption.Encode(), "", nil)
			if err != nil {
				http.Error(w, "can't http post.", http.StatusBadRequest)
			}
		}
	}
}

func emojiList(w http.ResponseWriter, token string) (map[string]interface{}) {
	urlOption := url.Values{}
	urlOption.Add("token", token)

	url := "https://slack.com/api/emoji.list?" + urlOption.Encode()
	resp, err := http.Post(url, "", nil)
	if err != nil {
		http.Error(w, "can not get emoji list.", http.StatusBadRequest)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		http.Error(w, "cnan not response read.", http.StatusBadRequest)
	}

	var result interface{}
	if err = json.Unmarshal(b, &result); err != nil {
		http.Error(w, "Error parse json.", http.StatusBadRequest)
	}
	msg := result.(map[string]interface{})

	var emoji interface{}
	eb, err := json.Marshal(msg["emoji"])
	if err != nil {
		logger.Errorf("can not emoji marshal err: %v", err)
		http.Error(w, "can not emoji.list.", http.StatusBadRequest)
	}

	if eb == nil {
		logger.Error("not set emoji.")
	}

	json.Unmarshal(eb, &emoji)
	emojiMsg := emoji.(map[string]interface{})

	return emojiMsg
}
```
3.サーバーにデプロイ
これは個人的に作ったものなので(*所属している団体、企業とは関係ありません)、AWSのec2インスタンスにデプロイして使用しています。

local環境での開発時は、Slack APIで薦められている[ngrok](https://api.slack.com/tutorials/tunneling-with-ngrok)を使えば、localhostで動いているサーバーを、LANの外からアクセスできるようにできるようになるので、それを`Request URL`に設定すれば、簡単に開発することができます。


# まとめ
今回作ったslash commandはLineのスタンプ感覚で使いたい時などに活躍します。
これからも色々slash commandで遊んでいこうかな〜と思っています。

我が社では、サーバーサイドはGo, フロントエンドはReactとTypeScriptで開発を行なっています。
興味がある方はぜひ我が社へ〜〜〜💨