デイリースクラムのファシリテーターをランダムで決めるSlack Botをサーバーレスで作った

キミにきめた!
2021.11.26

私のいるチームでは、毎日10時30分からデイリースクラムをしています。 最近、「ファシリテーターを変えてみよう」という事になりました。 当初はデイリースクラムの最後に「次の人を指名する」という運用を数日していましたが、忘れたり時間切れで決めれない場合がありました。

そこで、ランダムでファシリテーターを決める仕組みを作ってみました。

ファシリテーターが通知された!

おすすめの方

  • Slack Botを作りたい方(通知動作)
  • AWS SAMでAWS Lambdaをデプロイしたい方

Slackアプリの作成と通知先URLの取得

Slackアプリの作成

下記にアクセスして、Slackアプリを作成します。

Slackアプリを作成する

Incoming Webhooksの設定

Incoming Webhooksを選択し、ONにします。

incoming webhookを選択する

incoming webhookをONにする

ページの下側にある「Add New Webhook to Workspace」を選択し、任意のチャンネルを許可します。

incoming webhookを作成する

Slackワークスペースにincoming webhookを追加する

追加完了したあとは、URLをメモしておきます。

incoming webhook設定が完了する

AWS Systems ManagerのパラメータストアにSlack通知先を追加

下記コマンドでAWS Systems Managerのパラメータストアに追加します。 URLの先頭にhttps://があると、AWS CLIコマンド実行に失敗するため除去しています。

aws ssm put-parameter \
    --type 'String' \
    --name '/Slack/DailyScrumChannelUrl' \
    --value 'hooks.slack.com/services/xxxxx/yyyyy/zzzzz'

Slack Botを作成する

sam init

sam init \
    --runtime python3.9 \
    --name Daily-Scrum-Facilitator-Bot \
    --app-template hello-world \
    --package-type Zip

SAMテンプレート

通知先のURLは、AWS Systems Managerのパラメータストアから直接取得しています。 また、10時30分からデイリースクラムを開始するので、1分前の10時29分に起動させます。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Daily-Scrum-Facilitator-Bot

Parameters:
  DailyScrumChannelUrl:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /Slack/DailyScrumChannelUrl

Resources:
  NotifyDailyScrumFacilitatorFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.9
      Timeout: 10
      Architectures:
        - x86_64
      Environment:
        Variables:
          DAILY_SCRUM_CHANNEL_URL: !Ref DailyScrumChannelUrl
      Events:
        Notify:
          Type: Schedule
          Properties:
            Schedule: "cron(29 1 ? * MON-FRI *)"  # 月-金の10時29分(JST)

  NotifyDailyScrumFacilitatorFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub /aws/lambda/${NotifyDailyScrumFacilitatorFunction}

Lambdaコード

メンションをしたいので、メッセージのtypemrkdwnにしています。plain_textだとメンションされませんでした。

app.py

import json
import os
import urllib.request
import random

NOTIFY_SLACK_URL = os.environ['DAILY_SCRUM_CHANNEL_URL']
HIT_USERS_NUMBER = 4

USERS = [
    {'userId': 'aaa', 'name': 'Aさん'},
    {'userId': 'bbb', 'name': 'Bさん'},
    {'userId': 'ccc', 'name': 'Cさん'},
    {'userId': 'ddd', 'name': 'Dさん'},
    {'userId': 'eee', 'name': 'Eさん'},
    {'userId': 'fff', 'name': 'Fさん'},
]


def lambda_handler(event, context):
    payload = make_payload()
    post_slack(payload)


def make_payload():
    hit_users = random.sample(USERS, HIT_USERS_NUMBER)
    main_user_id = hit_users.pop()['userId']
    sub_user_names = '\n'.join([x.get('name') for x in hit_users])
    return {
        'blocks': [
            {
                'type': 'section',
                'text': {
                    'type': 'mrkdwn',
                    'text': f'今日のファシリテーターは、 <@{main_user_id}> です!'
                }
            },
            {
                'type': 'section',
                'text': {
                    'type': 'mrkdwn',
                    'text': f'不在の場合は、次の人にお任せしましょう。 ```{sub_user_names}```'
                }
            }
        ]
    }

def post_slack(payload):
    try:
        res = post(f'https://{NOTIFY_SLACK_URL}', data=payload)
    except requests.exceptions.RequestException as e:
        print(e)
        raise
    else:
        print(res.status)

def post(url: str, data: dict, headers: dict = {}):
    req = urllib.request.Request(url,
                                 data=json.dumps(data).encode('utf-8'),
                                 headers=headers,
                                 method='POST')
    return urllib.request.urlopen(req)

メンバーIDは、Slackのプロフィールで取得できます。

ユーザIDを取得する

デプロイ

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-deploy

sam deploy \
    --template-file packaged.yaml \
    --stack-name daily-scrum-facilitator-bot-stack \
    --s3-bucket cm-fujii.genki-deploy \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

動作確認

朝10時29分になると、無事に通知が来ました!

ファシリテーターが通知された!

なお、「開始ギリギリすぎて近況トークの準備に困る」とのフィードバックをもらったので、10時に通知するように変更したいと思います。

参考