LoginSignup
2
0

More than 3 years have passed since last update.

[AWS]Slack + AWS Chatbot + Lambdaで朝会のファシリテーター指名してみた

Last updated at Posted at 2020-07-22

現職場では、デイリースタンドアップとして、朝会を実施している。
そして、いつも開始日時になると、

「え、今日・・・司会担当・・・だれ・・・?」

とTheWordになる。
※TheWord ・・・ リモート会議において、 無音が続き、最初に話した人がファシリテーターをしなければいけない空間のこと

一応、SlackBotを使用して、それとなく機械的にファシリテーターを指名しているのだが、

「この人お休みだよ〜」 や 「昨日もやったんですけど〜」 とか

いちいち面倒臭い。

よし。朝会のファシリテーターをいい感じ(前回当たった人は当たらない、スムーズに再抽選ができる、自分が選ばれない)に決めるToolを作ろう。

はじめに

コミュニケーションToolはSlack
プラットフォームはAWSとする。

構成

全体構成はこんな感じ。
構成.png

朝会の担当者をランダムに選択し、Slackに通知するTool。
実行トリガーは2種類存在する。
1. CloudWathEventsから定期的(朝会の始まる5分前)に実行
2. Slack→AWS ChatBotから実行

DynamoDBではファシリテーター担当履歴を管理。
(現時点では直近の担当者しか保存していない。)

構築

AWS

ChatBot以外はCloudFormationで構築。
IAM等のユーザーは事前に発行しておくこと。
ソースコード

Lambda

ソース

Node.jsで実装。
コード全体とは以下の通り。

index.js
'use strict'
const requestPromise = require('request-promise')
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB.DocumentClient({
  region: 'ap-northeast-1'
})
let slackPostOption = {
  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 () => {
  return new Promise((resolve, reject) => {
    Promise.resolve()
    .then(() => {
      // 直近のファシリテーターを取得
      return getLatestFacilitator()
    })
    .then((latestFacilitator) => {
      // ランダムに取得(直近を除く)
      return getFacilitator(latestFacilitator)
    })
    .then((facilitator) => {
      // 直近のファシリテーターを登録
      return putLatestFacilitator(facilitator)
    })
    .then((facilitator) => {
      // Slackに通知
      return postSlack(facilitator)
    })
    .then(() => {
      resolve('Finish')
    })
    .catch(reject)
  })
}

const getLatestFacilitator = () => {
  const param = {
    TableName: 'FacilitatorHistory',
    Key: {
      status: 'latest'
    }
  }
  return new Promise((resolve, reject) => {
    dynamodb.get(param, (err, data) => {
      if (err) reject(err)
      resolve(data.Item ? data.Item.member : '')
    })
  })
}

const getFacilitator = (latestFacilitator) => {
  return new Promise((resolve, reject) => {
    const memberList = process.env.MEMBER.split(',')
    const get = () => {
      const facilitator = memberList[Math.floor(Math.random() * memberList.length)]
      if (latestFacilitator == facilitator) {
        get()
        return
      }
      resolve(facilitator)
    }
    get()
  })
}

const putLatestFacilitator = (facilitator) => {
  return new Promise((resolve, reject) => {
    var param = {
    TableName: 'FacilitatorHistory',
      Item:{
        status: 'latest',
        member: facilitator
      }
    }
    dynamodb.put(param, (err, data) => {
      if (err) reject(err)
      resolve(facilitator)
    })
  })
}

const postSlack = (facilitator) => {
  return new Promise((resolve, reject) => {
    slackPostOption.qs.text = `きょうのあさかいは <${facilitator}> だ。`
    requestPromise(slackPostOption)
    .then(resolve)
    .catch(reject)
  })
}

内容

本来なら並列にできる部分もあるが、わかりやすく直列化している。

流れとしては
1. 直近のファシリテーターを取得
2. ファシリテーターをランダムに取得(直近のファシリテーターは除く)
3. Slackに担当者を通知
4. 2.で選ばれたファシリテーターを登録
といったシンプルなもの。
2.のファシリテーター取得ロジック部分で担当者ごとに重みをつけて、選ばれやすさを制御したり、自身だけ選ばれづらくするなどのイカサマはできそう。

CloudFormation

yml

一部内容がベタが記されている部分があるので、使用する際には修正する必要がある。

template.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  MorningFacilitatorFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code: ./release/app.zip
      FunctionName: MorningFacilitatorFunction
      Handler: index.handler
      Runtime: nodejs12.x
      # Lambdaの実行ロール
      Role: {lambda arn}
      MemorySize: 128
      Timeout: 30
      Environment:
        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::Table
    Properties:
      TableName: FacilitatorHistory
      AttributeDefinitions:
        - AttributeName: status
          AttributeType: S
      KeySchema:
        - AttributeName: status
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 3
        WriteCapacityUnits: 3

  FacilitatorEventsRule:
    Type: AWS::Events::Rule
    Properties:
      Name: FacilitatorEventsRule
      # 朝会開始時間5分前(GMTなのでJST変換すると+9時間)
      ScheduleExpression: cron(55 0 * * ? *)
      State: ENABLED
      Targets:
        - Arn: !GetAtt MorningFacilitatorFunction.Arn
          Id: lambda

  MorningFacilitatorPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref MorningFacilitatorFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt FacilitatorEventsRule.Arn

CLI

デプロイする際は、AWS CLIを使用。
毎回入力するのは面倒なため、shを作成

deploy.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=$1
AWS_ACCESS_KEY_ID=$2
AWS_SECRET_ACCESS_KEY=$3
AWS_DEFAULT_REGION=$4
AWS_S3_BUCKET=$5
AWS_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」を選択し、クライアントを設定を押下。
スクリーンショット 2020-07-22 14.55.16.png

権限を求められるので、許可する。
スクリーンショット 2020-07-22 14.56.05.png

チャンネルの設定

新しいチャンネルを設定。
スクリーンショット 2020-07-22 14.58.40.png

各種任意の値を入力し登録。
screencapture-us-east-2-console-aws-amazon-chatbot-home-2020-07-22-15_02_56.png

以上でAWS側の設定は完了。

Slack

SlackからLambda実行

メンバーが揃っているチャンネルで、@awsさんをインバイトする。

/invite @aws

このように表示されればOK。
スクリーンショット 2020-07-22 15.22.48.png

SlackからLambdaの実行。

@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1

すると、ファシリテーターが指名される。
スクリーンショット 2020-07-22 15.27.03.png

ただ、毎回このようなコマンドを入力するのは効率が悪いので、Slackワークフロー登録する。

Slackワークフロー

ワークフロービルダーを開く。
スクリーンショット 2020-07-22 15.31.16.png

作成を押下して、適当な名前を入力。
※ 伸ばし棒が入らない・・・
スクリーンショット 2020-07-22 15.32.36.png

ショートカットを選択、作成。
チャンネル名はメンバーが揃っているチャンネルを選択。
スクリーンショット 2020-07-22 15.32.49.png
スクリーンショット 2020-07-22 15.33.25.png

ステップを追加し、メッセージを送信を選択。
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330303433302f62646265323338632d656633302d373332392d306630352d3337386435383331643434662e706e67.png
スクリーンショット 2020-07-22 15.33.46.png

メッセージの送信先に「ワークフローを開始したチャンネル」
メッセージテキストに@aws lambda invoke --function-name MorningFacilitatorFunction --region ap-northeast-1を入力。
スクリーンショット 2020-07-22 15.34.16.png

保存、そして公開。
スクリーンショット 2020-07-22 15.34.23.png

これでワークフロー登録完了。

ショートカットから起動することが可能。
スクリーンショット 2020-07-22 15.45.44.png

運用してみよう

カタカタカタ...

「...ふぅ。」

「おっとそろそろ朝会だなぁ。」

「今日の担当は・・・?」

スクリーンショット 2020-07-22 15.51.15.png

「病弱太郎か。あれ?あいつ今日病気で休みだったよな・・」

「しょうがない。選ぶか。」

「ショートカットから...えい」

スクリーンショット 2020-07-22 15.55.32.png

「ほうほう。朝会大好き子か。よしよし。今日の仕事を頑張ろう」

まとめ

正直、AWSでやるにはオーバースペック感が否めない。笑
とわ言え、

  • 朝会前のTheWordが少なくなる(かも?)
  • 興味のあったAWS Chatbotをさわれた

のでよかったかと。

AWS Chatbotは触ってみて、無限の可能性を感じた。
SlackトリガーでAWSのServiceを使用できるので、CI/CDを全てAWS上で構築することができれば、SlackOnlyで業務が回りそう。

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0