きっかけ
- 昨今の事情もあり在宅勤務を行っている方は多いと思います
- そんな中でチームのSlackでこんなやりとりがありました
- 在宅でリモートだと曜日感覚なくなってきますよね
- 明日が休みだと思うことで生まれる生産性爆上げ日を逃してしまうのはもったいない!ということでStayHomeの暇つぶしも兼ねて次の日が休みの時にSlackに通知してくれるBotを作ってみました
登場人物
- AWS Lambda(Node)
- Holidays JP API
- Slack Web API
- ServerlessFramework
できたもの
- 当日が平日で、翌日が土日祝日の場合、昼の12時にSlackに以下の投稿がされるようにしてみました
実装内容
Lambdaの実装
- Lambdaにデプロイするベースとなる
handler.ts
はこんな感じです- 今日と明日が平日かどうか判定し、今日が平日で明日が土日祝日の場合はSlackにメッセージを送ります
handler.ts
import * as dayjs from 'dayjs';
import { APIGatewayProxyHandler } from 'aws-lambda';
import { isHoliday } from './holidayUtil';
import { pushMessage } from './slackApi';
export const noticeDayBeforeHoliday: APIGatewayProxyHandler = async () => {
const today = dayjs();
const tomorrow = today.add(1, 'day');
if (await isHoliday(today)) {
return { statusCode: 200, body: 'Today is Holiday!!!' };
}
if (!(await isHoliday(tomorrow))) {
return { statusCode: 200, body: "Let's work tomorrow" };
}
await pushMessage({ text: '明日は休みだよ!残りの仕事も頑張ろう!' });
return { statusCode: 200, body: 'Tomorrow is Holiday!!!' };
};
土日祝日判定
- 日本の祝日情報を取得するAPIがないかと検索したらHolidays JP APIを見つけたので使ってみました
- 以下のような形式で祝日一覧を返してくれるAPIです
{
"2017-01-01": "元日",
"2017-01-02": "元日 振替休日",
"2017-01-09": "成人の日"
}
- 祝日判定をしている
holidayUtil.ts
のコードはこんな感じです- 引数でもらった日付を土曜/日曜か判定し、どちらでもなければ Holidays JP API から取得した祝日に該当するかチェックしています
holidayUtil.ts
import fetch from 'node-fetch';
import * as dayjs from 'dayjs';
const getHolidays = async () => {
const res = await fetch('https://holidays-jp.github.io/api/v1/date.json');
return res.json();
};
export const isHoliday = async (date: dayjs.Dayjs) => {
const formattedDate = date.format('YYYY-MM-DD');
if (date.day() === 0) {
console.log(`${formattedDate} is Sunday`);
return true;
}
if (date.day() === 6) {
console.log(`${formattedDate} is Saturday`);
return true;
}
const holidays = await getHolidays();
if (Object.keys(holidays).includes(formattedDate)) {
console.log(`${formattedDate} is Holiday`);
return true;
}
console.log(`${formattedDate} is Weekday`);
return false;
};
Slackへの通知
- Slackへの通知は公式が提供する@slack/web-apiを使いました
- Slackへ通知を送る
slackApi.ts
はこんな感じです- Tokenはリポジトリに含めたくない、投稿先チャンネルはdevとprodで差し替えたいといった事情から、それらの情報は環境変数から取得しています
- 詳しいやり方はこちらの記事で
- Tokenはリポジトリに含めたくない、投稿先チャンネルはdevとprodで差し替えたいといった事情から、それらの情報は環境変数から取得しています
slackApi.ts
import { WebClient } from '@slack/web-api';
const { SLACK_API_TOKEN, SLACK_CHANNEL, SLACK_USERNAME } = process.env;
const slackClient = new WebClient(SLACK_API_TOKEN);
export const pushMessage = async ({ text }) => {
const result = await slackClient.chat.postMessage({
text,
channel: SLACK_CHANNEL,
username: SLACK_USERNAME,
unfurl_links: true,
});
console.log({ result });
};
ServerlessFrameworkの設定
- ServerlessFrameworkの設定ファイルである
serverless.yml
はこんな感じです- 上の説明でも出てきた環境変数周りの設定やLambdaの定期実行のためのschedule設定などをしています
serverless.yml
service:
name: day-before-holiday
custom:
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
defaultStage: dev
slack:
dev:
token: ${env:SLACK_API_TOKEN_DEV}
channel: ${env:SLACK_CHANNEL_DEV}
username: ${env:SLACK_USERNAME_DEV}
production:
token: ${env:SLACK_API_TOKEN_PROD}
channel: ${env:SLACK_CHANNEL_PROD}
username: ${env:SLACK_USERNAME_PROD}
schedule:
noticeDayBeforeHoliday:
dev: # スケジュールなし
production: cron(0 3 * * ? *) # 日本時間で毎日昼12時
plugins:
- serverless-webpack
- serverless-dotenv-plugin
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, self:custom.defaultStage}
region: ap-northeast-1
environment:
AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1
TZ: Asia/Tokyo
SLACK_API_TOKEN: ${self:custom.slack.${self:provider.stage}.token}
SLACK_CHANNEL: ${self:custom.slack.${self:provider.stage}.channel}
SLACK_USERNAME: ${self:custom.slack.${self:provider.stage}.username}
functions:
noticeDayBeforeHoliday:
handler: src/handler.noticeDayBeforeHoliday
events:
- schedule: ${self:custom.schedule.noticeDayBeforeHoliday.${self:provider.stage}}
Local/Dev/Prodの使い分け
- ちょっとしたLambdaの開発ですが本番環境Onlyで突っ走るのは抵抗があります
- 今回はローカルとAWS(Dev環境)とAWS(Prod環境)に分けて動作確認をしていきました
- 基本はローカルで開発を進める
- 完成したらDevでテスト
- OKならProdへデプロイ
-
package.json
に定義したsrcipts
はこんな感じです
package.json
"scripts": {
"invoke": "sls invoke local -f noticeDayBeforeHoliday",
"invoke:aws": "sls invoke -f noticeDayBeforeHoliday",
"deploy": "sls deploy",
"deploy:prod": "sls deploy --stage production"
}
Local
- ローカルでは以下のコマンドで実行することができます
yarn invoke
Dev
- AWSにDevモードでデプロイする時は以下のコマンドです
yarn deploy
- AWSにデプロイしたLambdaを実行する時は以下のコマンドです
yarn invoke:aws
Prod
- AWSにProdモードでデプロイする時は以下のコマンドです
yarn deploy:prod
- Prodを暴発させるのは良くないのでProdのLambdaを実行するコマンドは入れませんでした
まとめ
- ちょっとしたLambda関数の開発でしたがServerlessFrameworkが快適なので作ってて楽しかったです
- これで在宅勤務でも祝前日の生産性爆上げ日を確保できそうですね
- StayHomeが続きますが工夫して乗り切りましょう!