現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、
「え、今日・・・司会担当・・・だれ・・・?」
とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと
一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、
「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか
いちいち面倒臭い。
よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、自分が選ばれない)に決めるToolを作ろう。
はじめに
コミュニケーションToolはSlack
プラットフォームはAWSとする。
構成
朝会の担当者をランダムに選択し、Slackに通知するTool。
実行トリガーは2種類存在する。
1. CloudWathEventsから定期的(朝会の始まる5分前)に実行
2. Slack→AWS ChatBotから実行
DynamoDBではファシリテーター担当履歴を管理。
(現時点では直近の担当者しか保存していない。)
構築
AWS
ChatBot以外はCloudFormationで構築。
IAM等のユーザーは事前に発行しておくこと。
ソースコード
Lambda
Node.jsで実装。
コード全体とは以下の通り。
'use strict'constrequestPromise=require('request-promise')constAWS=require('aws-sdk')constdynamodb=newAWS.DynamoDB.DocumentClient({region:'ap-northeast-1'})letslackPostOption={url:'https://slack.com/api/chat.postMessage',method:'POST',qs:{token:process.env.SLACK_TOKEN,channel:process.env.SLACK_CHANNEL,text:'',username:'ぼくのいうことはぜったい'},json:true}exports.handler=async()=>{returnnewPromise((resolve,reject)=>{Promise.resolve().then(()=>{// 直近のファシリテーターを取得returngetLatestFacilitator()}).then((latestFacilitator)=>{// ランダムに取得(直近を除く)returngetFacilitator(latestFacilitator)}).then((facilitator)=>{// 直近のファシリテーターを登録returnputLatestFacilitator(facilitator)}).then((facilitator)=>{// Slackに通知returnpostSlack(facilitator)}).then(()=>{resolve('Finish')}).catch(reject)})}constgetLatestFacilitator=()=>{constparam={TableName:'FacilitatorHistory',Key:{status:'latest'}}returnnewPromise((resolve,reject)=>{dynamodb.get(param,(err,data)=>{if(err)reject(err)resolve(data.Item?data.Item.member:'')})})}constgetFacilitator=(latestFacilitator)=>{returnnewPromise((resolve,reject)=>{constmemberList=process.env.MEMBER.split(',')constget=()=>{constfacilitator=memberList[Math.floor(Math.random()*memberList.length)]if(latestFacilitator==facilitator){get()return}resolve(facilitator)}get()})}constputLatestFacilitator=(facilitator)=>{returnnewPromise((resolve,reject)=>{varparam={TableName:'FacilitatorHistory',Item:{status:'latest',member:facilitator}}dynamodb.put(param,(err,data)=>{if(err)reject(err)resolve(facilitator)})})}constpostSlack=(facilitator)=>{returnnewPromise((resolve,reject)=>{slackPostOption.qs.text=`きょうのあさかいは <${facilitator}> だ。`requestPromise(slackPostOption).then(resolve).catch(reject)})}
内容
本来なら並列にできる部分もあるが、わかりやすく直列化している。
流れとしては
1. 直近のファシリテーターを取得
2. ファシリテーターをランダムに取得(直近のファシリテーターは除く)
3. Slackに担当者を通知
4. 2.で選ばれたファシリテーターを登録
といったシンプルなもの。
2.のファシリテーター取得ロジック部分で担当者ごとに重みをつけて、選ばれやすさを制御したり、自身だけ選ばれづらくするなどのイカサマはできそう。
CloudFormation
一部内容がベタが記されている部分があるので、使用する際には修正する必要がある。
AWSTemplateFormatVersion:2010-09-09Resources:MorningFacilitatorFunction:Type:AWS::Lambda::FunctionProperties:Code:./release/app.zipFunctionName:MorningFacilitatorFunctionHandler:index.handlerRuntime:nodejs12.x# Lambdaの実行ロールRole:{lambda arn}MemorySize:128Timeout:30Environment:Variables:TZ:Asia/Tokyo# カンマ区切りでファシリテーター候補のSlackユーザーIDを定義MEMBER:'@yamada,@yamamoto,@yamashita,@yamagami,@yamsuda'# 各種Slackの情報SLACK_TOKEN:{slack api token}SLACK_CHANNEL:{slack channel}FacilitatorHistory:Type:AWS::DynamoDB::TableProperties:TableName:FacilitatorHistoryAttributeDefinitions:-AttributeName:statusAttributeType:SKeySchema:-AttributeName:statusKeyType:HASHProvisionedThroughput:ReadCapacityUnits:3WriteCapacityUnits:3FacilitatorEventsRule:Type:AWS::Events::RuleProperties:Name:FacilitatorEventsRule# 朝会開始時間5分前(GMTなのでJST変換すると+9時間)ScheduleExpression:cron(55 0 * * ? *)State:ENABLEDTargets:-Arn:!GetAttMorningFacilitatorFunction.ArnId:lambdaMorningFacilitatorPermission:Type:AWS::Lambda::PermissionProperties:Action:lambda:InvokeFunctionFunctionName:!RefMorningFacilitatorFunctionPrincipal:events.amazonaws.comSourceArn:!GetAttFacilitatorEventsRule.Arn
デプロイする際は、AWS CLIを使用。
毎回入力するのは面倒なため、shを作成
#!/bin/sh# ソースコードをアーカイブrm-fr release &&mkdir release
zip -r app.zip index.js node_modules > /dev/null 2>&1
mv app.zip release/
# 必要な情報はベタがきするか、shの引数で対応AWS_IAM_USER_NAME=$1AWS_ACCESS_KEY_ID=$2AWS_SECRET_ACCESS_KEY=$3AWS_DEFAULT_REGION=$4AWS_S3_BUCKET=$5AWS_STACK=$6
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID--profile$AWS_IAM_USER_NAME
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY--profile$AWS_IAM_USER_NAME
aws configure set region $AWS_DEFAULT_REGION--profile$AWS_IAM_USER_NAME
aws cloudformation package --template-file template.yml --s3-bucket$AWS_S3_BUCKET--s3-prefix`date'+%Y%m%d%H%M%S'`--output-template-file output.yml --profile$AWS_IAM_USER_NAME
aws cloudformation deploy --region$AWS_DEFAULT_REGION--template-file output.yml --stack-name$AWS_STACK--profile$AWS_IAM_USER_NAME
AWS Chatbot
AWS Chatbotのコンソールを開き、チャットクライアント「Slack」を選択し、クライアントを設定を押下。
以上でAWS側の設定は完了。
Slack
メンバーが揃っているチャンネルで、@awsさんをインバイトする。
/invite @aws
このように表示されればOK。
SlackからLambdaの実行。
@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
すると、ファシリテーターが指名される。
ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。
Slackワークフロー
作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1
を入力。
これでワークフロー登録完了。
運用してみよう
カタカタカタ...
「...ふぅ。」
「おっとそろそろ朝会だなぁ。」
「今日の担当は・・・?」
「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」
「しょうがない。選ぶか。」
「ショートカットから...えい」
まとめ
正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、
- 朝会前のTheWordが少なくなる(かも?)
- 興味のあったAWS Chatbotをさわれた
のでよかったかと。
AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。