追記
公式でほぼ同じ機能が使えるようになったので、こちらの方法がオススメです
"Scheduled reminders" でGitHub上のやり取りをSlackに通知させよう!
Hello!
**株式会社Patheeのフロントエンド&サーバーサイドエンジニアの@mikan3rdです。
普段は自社サービスのPathee**やCMSの開発を担当しています。
2018年4月より新サービス**Patheeパートナーツール**をリリースしたことがきっかけで
社内のGit管理をBitBucketからGitHubに移行しました!
これまでBitBucketのwebhookを使ってPullRequestの通知を行っていたのですが、
GitHubへの移行に伴い、この通知も改善しようということになりました。
目標
今回は、前々から触ってみようと思っていたGoogleAppsScriptを使って
GitHubで@mention
のついたコメントがあった時、
それぞれのSlackのダイレクトメッセージに送るようにしたいと思います。
なんで Google Apps Script?
- 無料だから
- サーバーを立てなくていいから
- 社内でGoogleDriveをよく利用しているから(ユーザーが増えたりした時に管理しやすい)
- JavaScriptで書けると思っていたから(ちょっと違った)
手順
- GoogleDriveからGASを追加
- GASでアプリケーションのURLを発行
- GitHubでWebhookの設定をする
- GASでpostリクエストを受け取る
- Slackでwebhookの設定をする
- GASからSlackに通知する
- GitHubのユーザーネームをSlackのユーザーネームに変換する
- GitHubのコメントをSlackに通知する
GoogleDriveからGASを追加
- GoogleAppsScriptを初めて使う場合、アプリの追加が必要です
- 新規 => その他 => アプリを追加 を選択
- 接続を押すと追加されます
GASでアプリケーションのURLを発行
- 新規 => その他 に Google Apps Script が追加されています
- 公開 => webアプリケーションとして導入 を選択
- 「次のユーザーとしてアプリケーションを実行」を選択
- 「アプリケーションにアクセスできるユーザー」で「全員(匿名ユーザーを含む)」を選択
- 「現在のウェブアプリケーションのURL」をコピーしておきます
GitHubでWebhookの設定をする
- 権限が「Owner」の場合「Setttings」が表示されるので選択します
- Webhooksの右上のAdd webhook を選択します
- Payload URLに先ほどコピーしたGASのURLを貼り付けます
- Content typeをapplication/jsonに変更します
- Secretは今回は不要です
- 「Send me everything」 または 「Let me select individual events」で必要なイベントを選択します
- 「Add webhook」でGitHubのwebhookの設定は完了です
GASでpostリクエストを受け取る
- GitHubからはmethod=POSTでリクエストが送られてきます
- 参考: GitHub Webhooks Guide
- GASでは
doPost
と記述するとpostリクエストを受け取ります -
JSON.parse(e.postData.getDataAsString())
とすることで受けとったJSONを辞書型objectに変換します -
console.log()
でGoogle Cloud Platformのログに出力されます
// POSTリクエストを受け取る
function doPost(e) {
var data = JSON.parse(e.postData.getDataAsString());
// Google Cloud Platform のログで確認できる
// https://console.cloud.google.com/logs
console.log(data);
}
- 試しにGitHub上でコメントなどをしてみると、ログが確認できるはずです
Slackでwebhookの設定をする
- Slackのアプリ追加ページでincoming-webhooksを検索し、選択します。
- https://slack.com/apps
- デフォルトで通知が送られるチャンネルを選択して追加します
- 名前やアイコンは自由に設定してください
- Webhook URL をコピーしておきます
GASからSlackに通知する
- slackのwebhookにリクエストを送るfunctionを作ります
-
link_names: 1
することで、text内のユーザーネームをリンク化できます -
channel: '@username'
で、その人のslackbotにメッセージが届きます - 新しいユーザーなど、一部の場合は
@username
だと認識されない場合があるようです - そのユーザーについては
@userID
にするとちゃんと届きます - 参考:Slack APIでユーザー宛のメンションができなくなったので対策した
var SLACK_WEBHOOK_URL = 'webhookURLを貼り付ける';
// Slackのwebhookにリクエストを送る
function callSlackWebhook() {
var params = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({
channel: '@mikan3rd', // 自分のslackのユーザーネームに変えてください
text:'テストおおおおおおおおおおおおおおお',
link_names: 1,
})
};
var response = UrlFetchApp.fetch(SLACK_WEBHOOK_URL, params);
console.log("response:", response);
return response;
}
- GASの上部で実行する関数を選択し、▶︎ボタンを押すと関数が実行され、Slackにメッセージが届くはずです
GitHubのユーザーネームをSlackのユーザーネームに変換する
- GitHubのユーザー名をSlackのユーザー名に変換する必要があります
- 次のようにgithubとslackのユーザー名の辞書型objectを用意しましょう
-
@username
では通知できないユーザーは@userID
も記述します
var USER_NAME_LIST = [
{github: '@mikan3rd', slack: '@hiroki-ota'},
{github: '@hoge', slack: '@fuga', slackId: '@U3VSP6T0C'}
];
- また、GASにはJavascriptのfindメソッドがないため、様々な関数が使えるようになるUnderscoreというライブラリを追加します
- ライブラリはリソース => ライブラリ から追加できます
- 「ライブラリを追加」にプロジェクトキー
M3i7wmUA_5n0NSEaa6NnNqOBao7QLBR4j
を入力して追加を押すとライブラリが使えるようになります - 次のように書くことで、
_
をUnderScoreとして使うことができます
// ライブラリUndserScoreを使えるようにする
var _ = Underscore.load();
- GitHubから送られてきたデータにコメントがある場合、コメント内の
@mention
をslackのユーザーネームに置き換える処理を実装します - 先ほどの
USER_NAME_LIST
に一致するものがあった場合、userListに追加するようにしました
// ダイレクトメッセージを送るusernameを格納する通知リスト
var userList = [];
// GitHubのコメントをslackに通知する関数
function notifyslack(data) {
// commentがない場合は通知しない
if (!data.comment) {
return;
}
// コメント内の@mentionをslackのユーザーネームに置き換える
var comment = data.comment.body.replace(/@[a-zA-Z0-9_\-]+/g, convertAndAddUserName);
console.log(comment);
}
// GitHubのユーザーネームをSlackのユーザーネームに変換して送信先に追加する関数
function convertAndAddUserName(githubName) {
// UnderscoreのfindメソッドでUSER_NAME_LISTに対応するものがあるか探す
var user = _.find(USER_NAME_LIST, function(user) {return user.github === githubName});
if (user) {
// slackIDが定義されている場合は優先して使う
var slackName = user.slackId || user.slack
// slackの通知リストに追加
userList.push(slackName);
// <@username>とするとslackでリンク化される
return '<' + slackName + '>';
}
return githubName;
}
GitHubのコメントをSlackに通知する
- これまでの関数をつなげれば、GithubのリクエストをGASで受け取り、ユーザーネームを変換して、slackに通知する機能は完成です
// ライブラリUndserScoreを使えるようにする
var _ = Underscore.load();
var SLACK_WEBHOOK_URL = 'webhookURLを貼り付ける';
var USER_NAME_LIST = [
{github: '@mikan3rd', slack: '@hiroki-ota'},
{github: '@hoge', slack: '@fuga', slackId: '@U3VSP6T0C'}
];
// ダイレクトメッセージを送るusernameを格納する通知リスト
var userList = [];
// POSTリクエストを受け取る
function doPost(e) {
var data = JSON.parse(e.postData.getDataAsString());
notifyslack(data);
}
// GitHubのコメントをslackに通知する関数
function notifyslack(data) {
// commentがない場合は通知しない
if (!data.comment) {
return;
}
// コメント内の@mentionをslackのユーザーネームに置き換える
var comment = data.comment.body.replace(/@[a-zA-Z0-9_\-]+/g, convertAndAddUserName);
// userListのユーザーそれぞれにダイレクトメッセージを送る
for each(var user in userList) {
callSlackWebhook(user, comment);
}
}
// GitHubのユーザーネームをSlackのユーザーネームに変換して送信先に追加する関数
function convertAndAddUserName(githubName) {
// UnderscoreのfindメソッドでUSER_NAME_LISTに対応するものがあるか探す
var user = _.find(USER_NAME_LIST, function(user) {return user.github === githubName});
if (user) {
// slackIDが定義されている場合は優先して使う
var slackName = user.slackId || user.slack
// slackの通知リストに追加
userList.push(slackName);
// <@username>とするとslackでリンク化される
return '<' + slackName + '>';
}
return githubName;
}
// Slackのwebhookにリクエストを送る
function callSlackWebhook(user, comment) {
var params = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({
channel: user,
text: comment,
link_names: 1,
})
};
var response = UrlFetchApp.fetch(SLACK_WEBHOOK_URL, params);
console.log("response:", response);
return response;
}
おまけ
- GitHubから送られてくるactionにはassigned/review_request/approve/mergeなど様々なものがあるので、さらにカスタマイズしていくとこんな感じの賑やかな通知ができます
感想
今回、初めてGASを触ってみたのですが、普段のフロントエンドの開発ではES6で書いているので、
- constが使えない
- 文字出力がテンプレートリテラルでできない
- アロー関数が使えない
のは違和感がありました。また、
- Javascriptの基本的なメソッド(includesとか)で使えないものがある
- for..inでObjectの繰り返し処理できない
- 正規表現の先読み/後読みが対応していない
- リクエストのheaderが取得できない
-
console.log()
がその場で確認できない(Logger.log()
を使う)
など、普通のJavaScriptでできることができない部分もありました。
GASはあくまで"JavaScriptっぽく"書ける、ということだったんですね。
それでも、やりたいことは一通り実装することができたので、
Googleスプレッドシートと連携したスクリプトを書きたい時や、
「サーバーレスでちょっとした処理を作りたい!」という時には良いと思います!
次回
GASでGitHubのAPIを叩いてレビュアーをランダムで選ぶスクリプトを作ったので紹介しようと思います。