こんにちは。 株式会社Holmesでスクラムマスターしてます、id:k_kubouchi です。
Holmesの開発はアジャイルでのスクラム開発で行っています。 スクラム開発のイベントには「スプリントレトロスペクティブ」というスプリントを振り返るイベントが存在します。 振り返りの種類は色々とあり、チームによってやり方は様々かと思います。 振り返りたい内容をレトロ内で出し切れたらいいのですが、スプリントを振り返る際に「何かあったんだけど思い出せない」みたいなシチュエーションも中にはあるのではないでしょうか。 そこの一助になればと思い、都度振り返りを投稿できる SlackBot を導入することにしました。 それでは、以下の項目に沿って導入手順を解説していきたいと思います。
※前提として、振り返りは KPT と呼ばれる振り返りのフレームワークを想定しています。
※2020年から SlackBot の設定手順が変更されたようなので、そちらをベースに解説していきます。
- 1. Slack App の設定(SlackBot)
- 2. GASの設定(Google Apps Script)
- 3. Slack App と GAS の連携(Slack Outgoing WebHook)
- 4. チームでの運用ルール
- 5. まとめ
- 参考記事
1. Slack App の設定(SlackBot)
まず、SlackBot を導入したいワークスペースに管理者権限アカウントでサインインします。
URLはこちらになります。
サインイン後、画面右上にある Create New App
をクリックしてください。
設定ダイアログが表示されるので、 App Name
に好きな bot 名を、 Development Slack Workspace
に導入先の Slack ワークスペースを設定してください。
次に、権限の設定をする必要があるので、最初の画面の Basic Information
クリックで表示されるメニューから、Add features and functionality
→ Permissions
と遷移してください。
Scopes
の真ん中ぐらいにある Add an OAuth Scope
をクリックし、以下2つの権限を追加してください。(選択するだけで選んだことになってます)
- chat:write
- chat:write.public
続いて、 Basic Information
から App Display Name
に行き、 Slack 上で表示する App の内容を設定していきます。
設定項目はそれぞれ以下の通りです。
- App name:Slack App の名前
- Short description:Slack 上での bot の説明
- App icon & Preview:Slack 上での bot アイコン
- Background color:Slack 上での bot 背景
最後に Incoming Webhooks
から、以降に登場する Google Apps Script 上で使用する URL を取得します。
左メニューの Incoming Webhooks
をクリックし、 Webhook URLs for Your Workspace
に表示されている URL をコピーしておいてください。
以上で Slac App の設定は完了です。
2. GASの設定(Google Apps Script)
Slack に投稿した内容を自動的に Google のスプレッドシートに溜めていくような仕組みにしたいです。 そのためには Google Apps Script を使う必要あるので、まずはそちらを準備していきます。
Google Drive の + New
→ Google Sheets
を選択します。
新規スプレッドシートが表示されたら Tools
→ Script editor
を選択します。
編集画面が表示されるので、適当なプロジェクト名を左上のタイトルをクリックし設定します。 編集エディタ上に今回は以下コードを挿入することとします。
var KEEP = 'KEEP'; var PROBLEM = 'PROBLEM'; var TRY = 'TRY'; function doPost(e) { // SlackからのPOSTリクエスト時に発火する switch(e.parameter.text) { // start、end、K: P: T: で場合分け case 'start': start(); break; case 'end': end(); break; default: registerKpt(e.parameter.text, e.parameter.user_name); break; } } function start() { // 開始宣言 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // 先頭のシートを取得 if ((sheet.getName().match(/^[\d]{4}\/[\d]{2}\/[\d]{2} [\d]{2}:[\d]{2}$/) !== null) || (sheet.getLastRow() !== 0)) { // 重複した開始宣言は排除 postSlack('すでに今スプリントの `KPT` は始まってるなっしー!'); return; } var date = new Date(); sheet.setName(Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy/MM/dd hh:mm')); // シート名を現在時刻に変更 postSlack('今スプリントの `KPT` 開始なっしー!'); } function end() { // 終了宣言 var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheets()[0]; var lastRowNum = sheet.getLastRow(); var keepArray = []; var problemArray = []; var tryArray = []; var reviewArray = []; if (lastRowNum === 0) { // 0行の場合は終了できない postSlack('登録された `KPT` がないなっしー...'); return; } var rows = sheet.getRange(1, 1, lastRowNum, 3).getValues(); rows.forEach(function(row) { // 行ごとにKPT4要素でまとめる switch(row[0]) { case KEEP: keepArray.push(row[1] + ': @' + row[2]); break; case PROBLEM: problemArray.push(row[1] + ': @' + row[2]); break; case TRY: tryArray.push(row[1] + ': @' + row[2]); break; default: break; } }); var date = new Date(); var now = Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy/MM/dd hh:mm'); // 終了時点の時刻を取得 postSlack( // まとめて投稿 '今スプリントの KPT なっしー!みんなお疲れなっしー!\n' + '```\n' + sheet.getName() + ' ~ ' + now + '\n\n' + '# KEEP\n' + keepArray.join('\n') + '\n\n' + '# PROBLEM\n' + problemArray.join('\n') + '\n\n' + '# TRY\n' + tryArray.join('\n') + '\n```' ); ss.insertSheet(0); // 新たな空シートを先頭に追加 } function registerKpt(text, userName) { // KPT登録処理 postSlack('登録してるなっしー....'); var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; var categoryCell = sheet.getRange(sheet.getLastRow() + 1, 1); // 挿入する行の1列目を取得 var contentCell = sheet.getRange(sheet.getLastRow() + 1, 2); // 挿入する行の2列目を取得 var userCell = sheet.getRange(sheet.getLastRow() + 1, 3); // 挿入する行の3列目を取得 var message = processMessage(text); // 投稿を要素と内容に分ける if (message === null) { // 形式違いは排除 postSlack('形式が違うなっしー。 `K:`、 `P:`、 `T:` から始めるなっしー!'); return; } categoryCell.setValue(message.category); // 書き込み contentCell.setValue(message.content); userCell.setValue(userName); postSlack('スプシに書いといたなっしー!'); } function processMessage(text) { // 投稿を要素と内容に分ける var match = text.match(/^([K|P|T]):(.*)$/m); if (match === null) { return null; } var getText = text; switch(match[1]) { case 'K': getText = getText.replace('K:', ''); case 'P': getText = getText.replace('P:', ''); case 'T': getText = getText.replace('T:', ''); default: } return { category: convertCategory(match[1]), content: getText, }; } function convertCategory(category) { // プレフィックスから4要素を判断する switch(category) { case 'K': return KEEP; case 'P': return PROBLEM; case 'T': return TRY; default: return null; } } function postSlack(text){ // Slackへの投稿 // ここに先程コピーしていた Slack Incoming WebHook の URL を指定してください var url = "https://hooks.slack.com/services/~~~~~~"; var options = { "method" : "POST", "headers": {"Content-type": "application/json"}, "payload" : '{"text":"' + text + '"}' }; UrlFetchApp.fetch(url, options); }
コードを設定できたら保存し、メニューの Publish
を選択します。
設定ダイアログの中身を入力していきますが、この時以下のことに注意してください。
Project version:
は毎回保存時は必ずNew
を選択してくださいWho has access to the app:
ですが、必ずAnyone, even anonymous
で設定してください
以上で Google Apps Script の設定は完了です。
3. Slack App と GAS の連携(Slack Outgoing WebHook)
Slack の機能である Slack Outgoing WebHook
を設定することにより、 GAS を発火させることができます。
その手順を説明していきます。
まず、 https://SlackのWorkspace名.slack.com/apps
にアクセスし、検索フォームから Outgoing WebHooks
を検索し、選択してください。
遷移したら Slack に追加
を選択し、 Outgoing Webhook インテグレーションの追加
をクリックしてください。
そうすると設定画面になるので、追加していきます。
今回は KPT
振り返りの内容を投稿できるようにしたいので、以下のようにそれぞれ設定していきます。
チャンネル
: 投稿で使用するチャンネルを設定引き金となる言葉
: 投稿内容に含まれるどの文字を検知して発火させるか指定(今回だと KPT)URL
: GAS の URL を指定
以上で、 GAS と Slack の連携設定は完了です。
4. チームでの運用ルール
以下のルールを元に、チーム内で運用しています。
- スプリントの始まりと終わりにスプレッドシートのシートをスプリント単位で分けたいので、スクラムマスターが
start
end
を投稿 KEEP
の内容はK:
を頭につけて投稿PROBLEM
の内容はP:
を頭につけて投稿TRY
の内容はT:
を頭につけて投稿
上記ルールに沿って投稿すると、bot が返信してくれるので、地味に面白かったりします(笑)。 (参考サイトがふなっしーを使って運用されてたので、そこに引きずられる形でボットの画像や口調も合わせて設定してみてます)
5. まとめ
SlackBot での振り返りを導入したことにより、以下のような感想をチームメンバーからもらうことができました。
- 思いついたタイミングで気軽に投稿できる - 気づきを忘れなくなった - スプリント単位で手軽に管理できる
短い期間のスプリントだと、効率良く少しずつ改善を繰り返していく仕組みが大事なので 少しの自動化も開発の一助になるのならと思い、今回導入してみましたがそれなりの価値があったのではないかと思います。
今後もメンバーの手助けになるような仕組みをどんどん導入していき、 円滑にスプリントを回していけるよう精進したいと思います!
ここまで読んでいただきありがとうございました!!
参考記事
- [GAS]Slackのbotを作る方法を、お節介なほど丁寧に説明
- スクラムの振り返りで【分報式KPT】をSlack導入したら捗った件
- Slack KPT Bot 導入してから1ヶ月経ったので振り返りをしてみた
- Google Apps Scriptを利用したSlack Bot作成の" Hello, World!! "
Holmesはエンジニア・デザイナーを募集しています 興味がある方はぜひこちらからご連絡ください!