こんにちは。NUTMEGの藤崎です。先日NUTMEG内でLT会が行われ、slack無料プランのメッセージが3ヶ月間で表示されなくなるのをweb+slackAPIを利用してwebアプリを作成し解決した話をしました。今回はその話をもう少し具体的に話していきます。
slackの無料プランでは、3ヶ月間でメッセージ表示されなくなってしまいます。自分達NUTMEGや学祭実行委員会内では、3ヶ月前のメッセージも遡りたいこともありますが、利用しているslackは無料プランなので見ることができません。私たちNUTMEGや学祭実行委員会では、予算の関係上slackの有料プランを利用することが困難です。 そのため、有料プランを使用せずに、3ヶ月前のslackを遡る方法としてslackAPI + web技術を使い解決できると考え開発を開始しました。
本プロジェクトは12月から開始し、個人開発で行っていました。 現在は、興味あるメンバーがいて二人で進めています。
今回利用した技術、選定理由としては以下のようになります。
Bolt for Python
チャンネルのメッセージの監視してくれるbotと収集するロジックはBolt for Pythonを利用しました。 Bolt for PythonはSlack 連携アプリを作るための公式フレームワークです。BoltはWebAPIを利用して簡単にメッセージなどの情報を取得することが簡単にできます。 今回は簡単に早く開発したいのもあり、普段から使い慣れているPythonで書くことのできるBoltを採用しました。
Golang
バックエンドはGolangを利用しました。また、APIサーバーはEchoを利用しました。EchoはWebアプリケーションフレームワークであり、3分ほどでAPIサーバーを構築しやすく、普段から使い慣れているので採用しました。 DBからデータを取ってきて、可視化するだけを考えるとバックエンドは不必要そうですが、slackbot, バックエンド, フロントエンドの三層にして、監視、ロジック、可視化の責務を分けることで今後の保守が楽になると考え、導入しました。
Vue
フロントエンドはVue.jsを採用しました。私は普段はバックエンドを基本的に書いているので、フロントエンドはあまり得意ではなかったのですが、Vueはフロント初心者でも扱いやすいということで採用しました。
MongoDB
データベースはMongoDBを採用しました。MongoDBはNoSqlなので、json形式でデータを保存することが可能です。 slack APIを利用して返ってくるresponseは結構複雑なjsonであり、正規化したりするのは大変だと考えました。そこで取得したjsonをそのままDBに入れられるNoSqlを採用することでこの問題を解決しました。
NUTMEG Cloud
デプロイ先には、NUTMEGのインフラチームが開発しているNUTMEG Cloudを採用しました。NUTMEG CloudとはNUTMEGで運用しているオンプレミスサーバーのことです。複数のミニPCを用いて製作してあり、NUTMEGのプロダクトは基本的にここにデプロイされます。
NUTMEGクラウドをもっと詳しく →NUTMEG クラウド
イメージに表すと以下のようになります。
大まかな流れは以下のようになります。
①botがいるチャンネルで誰かがメッセージを投稿する ②botがチャンネルを監視しており、投稿されたメッセージをMongoDBに保存する ③誰かが、本プロダクトにアクセスする。 ④フロントがAPI requestを行う ⑤バックエンドがDBから必要なデータを取り出し、データを整形する ⑥整形されたデータをフロントに返す ⑦フロントによるメッセージの可視化
このプロダクトで最も苦戦したところは、MongoDBから取り出したデータから必要なデータだけに整形するところでした。 MongoDBにはslack APIのレスポンスがそのまま入っています。 DBには以下のようなデータが入っています。slack API message.channelsより引用
{
"token": "one-long-verification-token",
"team_id": "T061EG9R6",
"api_app_id": "A0PNCHHK2",
"event": {
"type": "message",
"channel": "C024BE91L",
"user": "U2147483697",
"text": "Live long and prospect.",
"ts": "1355517523.000005",
"event_ts": "1355517523.000005",
"channel_type": "channel"
},
"type": "event_callback",
"authed_teams": [
"T061EG9R6"
],
"event_id": "Ev0PV52K21",
"event_time": 1355517523
}message.channels
しかし、実際に必要なデータは、event
key内のvalue
だけでした。そのためMongoDBから取り出したデータからevent
key内の要素だけを取り出し、新しく連想配列に入れていこうとしましたが、なかなかうまくいきませんでした。
原因はmongoDB内で保存されているデータがbson形式だからです。そのため取り出して新しい変数に入れたり、連想配列に格納する際にはキャストする必要がありました。
実際に連想配列に格納するメソッドです。
func (u *mongoDBUsecase) FetchData() []map[string]string {
docs, err := u.repository.AllCollection()
usersInfoMap, err := u.repository.GetUserInfo()
channelsInfoMap, err := u.repository.GetChannelInfo()
usersInfo := usersInfoMap[0]
channelsInfo := channelsInfoMap[0]
var data []map[string]string
for _, v := range docs {
var m = make(map[string]string)
eventTs := v["event"].(bson.M)["event_ts"].(string)
channel := v["event"].(bson.M)["channel"].(string)
text := v["event"].(bson.M)["text"]
threadTs := v["event"].(bson.M)["thread_ts"]
user := v["event"].(bson.M)["user"]
m["eventTs"] = eventTs
channelName := channelsInfo[channel]
m["channelName"] = channelName.(string)
m["channelId"] = channel
if threadTs != nil {
m["threadTs"] = threadTs.(string)
}
if text != nil {
m["text"] = text.(string)
}
if user != nil {
userName := usersInfo[user.(string)]
m["user"] = userName.(string)
}
data = append(data, m)
}
if err != nil {
panic(err)
}
return data
}
もしかしたらもう少し上手にする方法があるかもしれません。本プロダクトではここが一番困難だと感じましたが、改めてgolangの型定義などを学び直すことになりとても勉強になりました。
現在はこんな感じになっています。 サイドバーにはNUTMEGにあるチャンネル一覧があり、クリックするとそのチャンネルの今までのメッセージが見れるページに飛びます。(一部プライバシーのため黒塗りしています。)
まだ完成していない点がいくつかあります。
現在はスレッドにあるメッセージもそのまま並べられているだけです。今後はスレッド機能をつけて、会話の流れを見やすくしたいと考えています。
ログイン機能がないため、現在誰でもアクセスできる状態です。そのためログイン機能をつけてNUTMEGメンバーだけが見れるようにしていきたいと考えています。
メイン機能はほぼできていますが、HOME画面のようなものがないので、簡単でいいので作成していきたいと思います。
優先順としては、HOME→ログイン機能→スレッド機能くらいで考えています。
久しぶりに個人開発をしてみました。最近勉強しているGolangを利用してwebアプリケーションを1から作れたのはいい経験だと思います。またMongoDBやBoltなどの初めて触る技術も多く、とても楽しんで開発できました。また、フロントエンドはほぼ未経験でしたが、先輩に聞きつつ開発を進め、理解度を深めることができました。さらにフロントエンドを触ることで、いつもフロント陣がどのようなことを考えて開発しているのか少しは理解できたのではないかと思っています。 まだ開発段階ではありますが、完成に向けて頑張ります!! また、この記事の技術のところでデタラメをいってたら申し訳ありません。
最後まで読んでいただき、ありがとうございました。 LT会で利用した資料とgithubのリンクも添付するので、宜しければ、こちらもご覧ください。