オンライン雑談会の「開催日は今日だよ!」をSlackに自動投稿する仕組みをサーバーレスで作ってみた
定期開催しているオンライン雑談会があります。日時の調整に調整さんが大活躍しています。
- 月初に「調整さんを作ってね」のリマインダーがSlackに自動投稿される(以前に作成した仕組み)
- 人間が調整さんを作成する
- 人間がSlackのチャンネルで案内する(URLと締切)
- Botが締切日に「今日が締切だよ!」と案内する(以前に作成した仕組み)
- 人間が当日に「今日が開催日だよ!」と案内する
そこそこ自動化してきましたが、ツメが甘かったです。5.
も自動化できることに気づきました。やりました!!!
全体概要
前回の仕組みを利用し、Lambda内で処理を分けています。
Slackのワークフローを作成する(開催日の入力)
ワークフローの新規作成
Slackの左上を選択し、ワークフロービルダー
を起動します。
適当に名前を付けます。
人間が起動するためショートカット
を選択します。
チャンネルと短い名前を入力します。
ワークフローのステップを追加(フォーム入力)
ステップを追加
を選択します。
フォームを作成します。
ワークフローのステップを追加(メッセージ送信)
さらにステップを追加
します。今度はメッセージを送信
を選択し、保存します。
ワークフローを公開する
右上の公開する
ボタンを選択すればOKです!
Lambdaの変更
対象日をDynamoDBに保存するLambdaを変更する
前回作成したsrc/save_deadline/app.py
を下記に変更します。DynamoDBに保存するときtype
を設けて区別しています。
deadline
: 調整さんの回答締切日announce
: 同期会の開催日
import boto3 import json import logging import os import re from datetime import datetime logger = logging.getLogger() logger.setLevel(logging.INFO) dynamodb = boto3.resource('dynamodb') SLACK_WORKFLOW_USER_DEADLINE = 'reminder_misc_join_201901_workflow' SLACK_WORKFLOW_USER_ANNOUNCE = '同期会のお知らせ' def lambda_handler(event, context): main(event) return { 'statusCode': 200 } def main(event): logger.info(json.dumps(event)) body = json.loads(event['body']) logger.info(json.dumps(body)) if 'username' not in body['event']: logger.info('No username.') return # 「締切」と「開催日」が同日の場合は考慮しない(運用上なし) if body['event']['username'] == SLACK_WORKFLOW_USER_DEADLINE: # 調整さんの締切とURLを登録する deadline_timestamp = parse_timestamp_for_deadline(body['event']['text']) url = parse_url_for_deadline(body['event']['text']) item = { 'deadline': deadline_timestamp, 'type': 'deadline', 'expiration': deadline_timestamp + 60*60*11, # 当日11時をDynamoDBのTTL期限とする 'url': url } logger.info(f'item for deadline: {json.dumps(item)}') put_item(item) elif body['event']['username'] == SLACK_WORKFLOW_USER_ANNOUNCE: # 開催日を登録する announce_timestamp = parse_timestamp_for_announce(body['event']['text']) item = { 'deadline': announce_timestamp, 'type': 'announce', 'expiration': announce_timestamp + 60*60*11, # 当日11時をDynamoDBのTTL期限とする } logger.info(f'item for announce: {json.dumps(item)}') put_item(item) else: logger.info('No workflow message.') def parse_timestamp_for_deadline(text): pattern = r'.+\n期限は \*(\d{4}/\d{1,2}/\d{1,2})\* です!' res = re.match(pattern, text) if res: # 0時のunixtimeを返す return int(datetime.strptime(res.group(1), '%Y/%m/%d').timestamp()) raise ValueError def parse_timestamp_for_announce(text): pattern = r'同期会の開催日は \*(\d{4}/\d{1,2}/\d{1,2})\* です!' res = re.match(pattern, text) if res: # 0時のunixtimeを返す return int(datetime.strptime(res.group(1), '%Y/%m/%d').timestamp()) raise ValueError def parse_url_for_deadline(text): pattern = r'.+\n.+\n<(.+)>' res = re.match(pattern, text) if res: return res.group(1) raise ValueError def put_item(item): table_name = os.environ['REMINDER_TABLE_NAME'] table = dynamodb.Table(table_name) res = table.put_item(Item=item) logger.info(res)
告知するLambdaを変更する
前回作成したsrc/notify_deadline_message/app.py
を下記に変更します。type
によって通知文面を変更しています。
import boto3 import json import logging import os import requests from botocore.exceptions import ClientError from datetime import date, datetime logger = logging.getLogger() logger.setLevel(logging.INFO) INCOMMING_WEBHOOK_URL = os.environ['INCOMMING_WEBHOOK_URL'] dynamodb = boto3.resource('dynamodb') def lambda_handler(event, context): today = get_today() logger.info(f'today: {today}') remind_data = get_remind_data(today) logger.info(f'get_remind_data(): {remind_data}') if remind_data is None: return message = create_message(remind_data) post_slack(message) def get_today(): today = date.today() # 今日の0時0分0秒のunixtimeを返す return int(datetime(today.year, today.month, today.day).timestamp()) def get_remind_data(deadline): table_name = os.environ['REMINDER_TABLE_NAME'] table = dynamodb.Table(table_name) try: res = table.get_item(Key={ 'deadline': deadline } ) except ClientError as e: logger.error(e.response['Error']['Message']) return None else: return res.get('Item', None) def create_message(remind_data): # https://api.slack.com/incoming-webhooks # https://api.slack.com/docs/message-formatting # https://api.slack.com/docs/messages/builder # https://www.webfx.com/tools/emoji-cheat-sheet/ if remind_data['type'] == 'deadline': return { 'text': '<!here> 今日が締切です!! 記入お願いします!\n', 'attachments': [ { 'text': remind_data['url'] } ] } if remind_data['type'] == 'announce': return { 'text': '<!here> 今日が開催日です!!\n', } raise AttributeError('unsupport type') def post_slack(message): url = f'https://{INCOMMING_WEBHOOK_URL}' # http://requests-docs-ja.readthedocs.io/en/latest/user/quickstart/ try: response = requests.post(url, data=json.dumps(message)) except requests.exceptions.RequestException as e: logger.error(e) else: logger.info(response.status_code)
ビルド&デプロイ
sam build
sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-chouseisan-reminder-deploy-bucket
sam deploy \ --template-file packaged.yaml \ --stack-name Chouseisan-Reminder-Stack \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset \ --parameter-overrides ChouseisanNotifySlackUrl=/Slack/INCOMING_WEBHOOK_URL/channel_name/choseisan_reminder
動作確認
1. Slackのワークフローで調整さんのURLと締切日を登録する
調整さんを作成し、締切日とURLをSlackのワークフローで入力します。
Slakでは下記のメッセージが自動投稿されます。
DynamooDBには下記が格納されました。
2. 調整さんの入力締切日に通知がくる
3. 開催日を登録する
開催日をSlackのワークフローで入力します。
Slakでは下記のメッセージが自動投稿されます。
DynamooDBには下記が格納されました。
4. 開催日に通知がくる!!!
さいごに
一通りの自動化ができました。よい調整さんライフをお過ごしください!