この記事は弥生 Advent Calendar 2020の1日目の記事です。
こんにちは。@KawamataRyoです。
最近嬉しかったことは、6 年ぶりにリアルの新刊を読めたことです。
さて、Misoca開発チーム では DocBase に日報を書くのですが、みんなの日報を読むために毎回ブラウザで DocBase を開くのが地味に面倒でした。
なので、日報の内容を Slack に流す Bot を作ってみました。
この記事ではその実装方法を紹介します。
🍿 何を作った?
DocBase に日報を投稿すると、こんな感じにフォーマットされたメッセージを Slack の専用チャネルに通知する Bot です。DocBase 上でのコメントの投稿にも対応しています。
一応 DocBase 自体で Slack 連携に対応しているのですが、以下のように投稿の全文が表示されず、毎回 DocBase を見に行く手間が発生するので今回は独自で作りました。
🛠 実装方法
サクッと作りたかったので、DocBase からの Webhook を受ける用の AWS Lambda を作り、レスポンスを Slack 投稿用に整形したうえで Slack の Incomming Webhook に投げる方式としました。
構成はこんなイメージです。
次項から簡単に実装を説明します。
1. Slack Appの作成
最初に Slack API から Slack App を作成して Incoming Webhooks を設定をします。
ここで発行した Incoming Webhook の URL を次項で作る Lambda の送信用URLとして使います。
2. Serverless Framework で Webhook 応答用の Lambda を作成
AWS の Lambda の実装です。
Lambda は Serverless Framework を使って開発しました。
Serverless Framework は Lambda や cloud functions の開発環境構築・デプロイを楽に実現するためのライブラリです。
今回は以下コマンドで、TypeScript の Lambda 環境を作りました。
$ npx sls create -t aws-nodejs-typescript -n docbase-nippou-notifier -p docbase-nippou-notifier
これだけで指定のパスに Lambda の関連コードが生成されます。
あとは生成されたディレクトリに移動してコードを編集します。
今回は以下のような関数を作りました。
DocBase の Webhook からの POST リクエストを受け取り、Slack の Incoming Webhook に投げるだけです。
import { APIGatewayProxyHandler } from 'aws-lambda'; import 'source-map-support/register'; import { DocBaseWebhookPayload } from "./lib/types"; import { createPostBlock } from "./lib/createPostBlock"; import { createCommentBlock } from "./lib/createCommentBlock"; import fetch from 'node-fetch'; // DocBaseからのイベントを受け取り、整形したデータをSlackにPOSTする export const webhook: APIGatewayProxyHandler = async (event, _context) => { try { const payload = JSON.parse(event.body) as DocBaseWebhookPayload; if (isTargetTeamPost(payload)) { await postToSlack(payload); } } catch (error) { console.error(error); } return { statusCode: 200, body: JSON.stringify({ message: 'ok', }, null, 2), }; } // 対象チームの投稿かどうかの判定 const isTargetTeamPost = (payload: DocBaseWebhookPayload) => { return payload.post.tags.some((t) => t.name === process.env.TEAM_TAG_NAME); } // Slack Incoming WebhookへのPOST const postToSlack = async (payload: DocBaseWebhookPayload) => { const messageBody = "comment" in payload ? createCommentBlock(payload) : createPostBlock(payload); const options = { method: "POST", headers: { "Content-type": "application/json" }, body: JSON.stringify(messageBody), }; await fetch(process.env.SLACK_WEBHOOK_URL, options) }
Slack への投稿の整形は以下で行っています。
レイアウト指定の JSON は、Slack Block Kit Builder を使って構築するのがおすすめです。
import { DocBaseWebhookPayload } from "./types"; export function createPostBlock(payload: DocBaseWebhookPayload) { const post = payload.post; return { blocks: [ { type: "header", text: { type: "plain_text", text: `:pencil2: 日報 posted by ${post.user.name}`, }, }, { type: "section", text: { type: "mrkdwn", text: `<${post.url}|${post.title}>`, }, }, { type: "divider", }, { type: "section", text: { type: "mrkdwn", text: post.body, }, accessory: { type: "image", image_url: post.user.profile_image_url, alt_text: "user thumbnail", }, }, { type: "divider", }, ], }; }
あとは、serverless.ts
を編集して Lambda、API Gateway、環境変数、デプロイ先を設定して以下コマンドを実行すれば指定の Lambda にデプロイされ、API Gatewayまで作成されます。
$ npx sls deploy
Serverless Framework 便利!
3. DocBase の Webhook 設定
最後に DocBase の Webhook の設定を行います。 以下記事の通りに設定すればOKです。設定するURLは Serverless Framework でデプロイした Lambda のエンドポイントとなります。 これで完成です 🎉
終わりに
Lambda はちょっとした Bot を作るのにも使いやすいですね。
Misoca の slack には他にも Lambda で作られた Bot が多数生息しています。
今後も業務・個人で色々作っていきたいなと思いました。
🎺 宣伝
Misoca 開発チームでは日常の些細なことでも技術で解決していくエンジニアを募集しています。