GoogleAppsScript
gas
Slack
Tech DoDay 8

Google Apps ScriptでSlackの新規(ユーザー|チャンネル|絵文字)を通知するBOTを作った

社内のコミュニケーションツールとして、Slackを使用している企業も多いのではないでしょうか。

多くの人が参加するワークスペースでは、日々新たなユーザーが参加し、新たなチャンネルが生まれ、新たな絵文字が追加されていきます。

重要な情報を見逃さないためにも、それらのイベントを特定のチャンネルに通知するBOTをGoogle Apps Scriptで作って運用しています。


なぜGoogle Apps Scriptなのか

元々は、ノンコーディングで実現するためにZapierを使っていました。

しかしながら無料プランだとなにかと制約が厳しく、代替案の検討を余儀なくされました。

当時はまだ社内でAWSが使えなかったこともあり、ひとまずGoogle Apps Scriptで実装することにしたのです。


システム構成図

あえて図にするまでもありませんが、BOTのシステム構成図は以下の通りです。

[Slack] <--> [Google Apps Script]

Slackで発生したイベントをGoogle Apps Scriptに投げ、Google Apps Script側でメッセージを生成してSlackに投稿するといった流れです。

IFTTTで言うところの、トリガーとアクションの両方にSlackを設定している状態ですね。


仕組み

それでは、実際のソースコードを交えつつBOTの仕組みを見ていきましょう。

ここでは新規チャンネル通知BOTを例にあげますが、他のBOTも概ね同じ仕組みです。


Slackで発生したイベントをGoogle Apps Scriptに投げる

Slackで発生したイベントを外部に投げるには、Events APIを使用します。

Events APIでは、「ユーザーが参加した」「チャンネルが作成された」「絵文字が更新された」など、任意のイベントの発生時に指定したURLにHTTPリクエストを投げてくれます。

新規チャンネル通知BOTではchannel_createdというイベントを購読しているため、チャンネルが作成されたときに以下のようなリクエストが投げられます。

{

"type": "channel_created",
"channel": {
"id": "C024BE91L",
"name": "fun",
"created": 1360782804,
"creator": "U024BE7LH"
}
}

余談ですが、SlackはチャンネルIDがCで始まりユーザーIDがUで始まるなど、ID自体でそれが何を表しているのかが分かるようになっていて良いですね。


Google Apps Scriptでリクエストを受け取る

Google Apps ScriptではdoPost関数でPOSTリクエストを受け取ることができるので、その中に処理を書いていきます。

function doPost (e) {

var request = normalizeRequest_(e)
var requestType = request.type

if (requestType === REQUEST_TYPE_URL_VERIFICATION) {
var challenge = request.challenge
return createTextOutput_(challenge)
} else if (requestType === REQUEST_TYPE_EVENT_CALLBACK) {
var event = request.event

if (event.type === EVENT_TYPE_CHANNEL_CREATED) {
var message = createMessage_(event)
postToSlack_(message)
}
}
}

認証のための仕組みとして、リクエストタイプがurl_verificationのときはリクエストに含まれるchallengeの値をオウム返しする必要があります。

また、リクエストタイプがurl_verificationのときとevent_callbackのときとではリクエストの取り出し方が異なるようなので、normalizeRequest_関数であらかじめ正規化しています。

function normalizeRequest_ (e) {

var postData = e.postData
var request = postData.contents || postData.getDataAsString()
return JSON.parse(request)
}


メッセージを生成する

リクエストに含まれているイベントのデータを元に、メッセージを生成します。

function createMessage_ (event) {

var channel = event.channel
channel.created = moment(channel.created * 1000).format(MESSAGE_TEMPLATE_DATE_FORMAT)
return Mustache.render(MESSAGE_TEMPLATE, channel)
}

メッセージの文面を環境変数(スクリプトのプロパティ)で柔軟に設定できるようにするために、以下のライブラリを使用しています。



  • Mustache:テンプレートを元に文字列を生成


  • Moment:日時のフォーマットを指定

たとえば、スクリプトのプロパティで以下のように値を設定したとします。

プロパティ

MESSAGE_TEMPLATE
<#{{id}}> has created by <@{{creator}}> on {{created}}.

MESSAGE_TEMPLATE_DATE_FORMAT
YYYY[/]M[/]D H[:]mm[:]ss

すると、テンプレート内の{{}}で囲まれた変数がMustacheによって実際の値に置き換わり、以下のようなメッセージが生成されます。

#google_apps_script has created by @munieru_jp on 2018/12/7 18:17:13.

テンプレートを書く際の注意点として、APIから投稿する場合は#チャンネル名@ユーザー名のように書いても単なる文字列として扱われてしまうので、それぞれ<#チャンネルID><@ユーザーID>のように書く必要があります。


Slackにメッセージを投稿する

Slackにメッセージを投稿するには、Incoming Webhooksを使用します。

Incoming Webhooksでは、Slackで生成した一意なURLにHTTPリクエストを投げることで特定のチャンネルにメッセージを投稿することができます(このURLが漏洩すると投稿され放題になってしまうので、扱いには留意する必要があります)。

単純なテキストメッセージの場合は、以下のようなJSONを投げるだけです。

{

"text": "Hello World"
}

Google Apps Scriptでは、UrlFetchApp.fetch()関数でHTTPリクエストを送信することができます。

function postToSlack_ (message) {

var params = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
payload: '{"text":"' + message + '"}'
}
UrlFetchApp.fetch(WEBHOOK_URL, params)
}


動作イメージ

各BOTの実際の動作イメージは以下の通りです。


  • 新規ユーザー通知BOT

    45ddad15-c987-ce1a-1264-00ad12c429f9.png

  • 新規チャンネル通知BOT

    4defaffe-3625-a0ed-577e-948509d4643e.png

  • 新規絵文字通知BOT

    570555bf-60c1-3fd0-0958-68f6d9c7a7d2.png

弊社の場合はそれぞれ専用のチャンネルに投稿していますが、ワークスペースの規模によっては1つのチャンネルにまとめてもいいかもしれません。


ソースコード

この記事で紹介したBOTのソースコードは、GitHubで公開しています。

ちなみに、ソースコードはJavaScript Style Guideに準拠しています。1