Bolt + lambda を使って Slack に通知メッセージを送る API を作る
Boltを利用してWebアプリと連携し、Slackワークスペースに所属するユーザーに応じて通知を出し分けるAPIを作ってみたので知見として書きます。
当記事で書くこと
- Slack Appの設定
- Bolt + serverless によるSlackBotのAPI実装
- emailからユーザーとのDMチャンネルを検索
- 取得したDMチャンネルIDへのメッセージ送信
- lambdaへのデプロイ
当記事で書かないこと
- Boltを利用したOAuth周りの認証設定
- 複数ワークスペースでSlackAppを利用するため
- 今後別記事にて投稿予定
手順
【SlackApp側の設定】
Slack App 作成 ←のリンクから Slack App を作成する
App Home タブにてアプリの DM に表示するタブを設定
- "App Display Name" の Edit ボタンから、好きな display name を設定して保存する
- Messages Tab
- ここのチェックを
true
にすると Messages Tab でユーザーがメッセージを送信できるようになる
Allow users to send Slash commands and messages from the messages tab
- ここのチェックを
OAuth & Permissions タブにて
- Slack API の利用に必要な以下の権限を設定する
"channels:read",
"chat:write:bot",
"groups:read",
"im:read",
"mpim:read",
"users:read",
"users:read.email"
以下のAPIのWorks with
に必要なscopeが書いてあります
- Install to WorkSpace する
【Slack App開発】
サンプルコードは以下のリポジトリで公開しています
まずはサーバーレスアプリケーションを開発、デプロイするためのツールをインストールします
こちらの記事で紹介されている事前準備を行ってください
Lambdaの作成
下記コマンドを実行してNode.js用の作業ディレクトリとLambdaの定義ファイル作成します。
$ serverless create --template aws-nodejs --path myService
以下を実行して初期設定を行います
$ yarn init
myService
├── .npmignore
├── handler.js
├── package.json
└── serverless.yml
以下のコマンドで必要なパッケージをインストールします
$ yarn add @slack/bolt @vendia/serverless-express multiparty
$ yarn add -D serverless serverless-offline
package.jsonに以下のscriptを追記します
これでyarn dev
することでlocalでデバックできるようになりました
{
...
"scripts": {
"dev": "sls offline",
"deploy": "npx serverless deploy"
},
}
Serverlessの設定ファイルを以下の内容に変更します
service: serverless-bolt-js
frameworkVersion: "2"
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-1
environment:
SLACK_SIGNING_SECRET: ${env:SLACK_SIGNING_SECRET}
SLACK_BOT_TOKEN: ${env:SLACK_BOT_TOKEN}
functions:
slack:
handler: app.handler
events:
- http:
method: ANY
path: /{any+}
useDotenv: true
plugins:
- serverless-offline
package:
patterns:
- '!.git/**'
- '!README.md'
あわせて環境変数を追加します
SLACK_SIGNING_SECRET="xxx"
SLACK_BOT_TOKEN="xoxb-xxx"
準備ができたら処理を書いていきます
const { App, ExpressReceiver } = require("@slack/bolt");
const serverlessExpress = require("@vendia/serverless-express");
const multiparty = require("multiparty");
const accessToken = process.env["SLACK_BOT_TOKEN"];
const expressReceiver = new ExpressReceiver({
signingSecret: process.env["SLACK_SIGNING_SECRET"],
processBeforeResponse: true,
});
const app = new App({
token: accessToken,
receiver: expressReceiver,
});
// /slack/events/massegesへのpostリクエストのエンドポイント作成
app.receiver.router.post("/slack/events/masseges", async (req, res) => {
// req から fields を抽出する
const data = await new Promise((resolve, reject) => {
const form = new multiparty.Form();
form.parse(req, (err, fields, files) => {
resolve(fields);
});
});
// validation
if (!data.email) {
res.status(400).send("error: no_email");
return;
}
if (!data.text) {
res.status(400).send("error: no_text");
return;
}
const userEmailList = data.email.find((_, i) => i === 0).split(",");
const massege = data.text.find((_, i) => i === 0);
let userIds = [];
for (const email of userEmailList) {
try {
// reqパラメーターのemilがワークスペースに存在するか確認
const user = await app.client.users.lookupByEmail({
token: accessToken,
email: email,
});
if (user) {
userIds = [...userIds, user.user.id];
}
} catch (error) {
res.status(400).send(`error: user is Not Found. ${email}`);
return;
}
}
// DMチャンネル一覧を取得
const conversationsList = await app.client.conversations.list({
token: accessToken,
types: "im",
});
const channels = conversationsList.channels;
if (!!userIds.length) {
// メールアドレスから取得したユーザーの DM チャンネルのみにフィルター
channels
.filter((x) => {
return userIds.some((y) => {
return y === x.user;
});
})
.forEach((x) => {
// メッセージ送信
app.client.chat.postMessage({
token: accessToken,
channel: x.id,
blocks: massege,
});
});
}
res.status(200).send("success!!");
});
// Handle the Lambda function event
module.exports.handler = serverlessExpress({
app: expressReceiver.app,
});
デプロイ
以下コマンドでlambdaへデプロイします
yarn deploy
これで完成です!
試してみる
APIエンドポイントに対してcurlでリクエストを送ってみる
(email が一致したユーザーは Slack の DM にメッセージが送信される)
通知メッセージを作成する
paramater
- email : カンマ( , )区切りで通知を送信したいユーザーのメールアドレスを渡す
- text : メッセージの block を作成しパラメーターに渡す
- Block Kit Builder にて作成する
curl --location --request POST 'https://xxxxxxxxxx.amazonaws.com/dev/slack/events/masseges' \
--form 'email="taro@hoge.com,jiro@hoge.com"' \
--form 'text="[
{
\"type\": \"section\",
\"text\": {
\"type\": \"mrkdwn\",
\"text\": \"*twitterフォローしてね!*\"
}
},
{
\"type\": \"section\",
\"fields\": [
{
\"type\": \"mrkdwn\",
\"text\": \"https://twitter.com/Area029S\"
}
]
}
]"'
送信できました!
あとがき
以上でSlackワークスペースに所属するユーザーに応じて通知を出し分けるAPIを作成することができました。
今後はBoltの認証機能を利用したマルチワークスペース対応の実装例を紹介できればと思います。
ちなみにシンプルに通知のみを実行したいのであればBoltを使わないでサービス側で直接SlackAPIを叩いてしまう方が低コストに実現できます。
参考にした記事
Discussion
Slack bot を作るのにNext.jsとVercelってどうですかね?
ExpressとLamdaは直接的には使ったことないのですが、そっちの方が楽ですか?
APIエンドポイントを楽に取得できるという意味でNext.jsを使ってやってるのですが、いまいちうまくいかない部分もあり・・
ご質問ありがとうございます!
Express 単体というよりは Bolt が Slack の APIを諸々内包してくれているので型の補完も効くし楽という印象でした。
Next.js + Vercel で Slack の API を直接叩く方法でも全然有りだと思います!
私が使うなら用途に応じてアプリケーションの機能の一部として使うなら API を直接叩く方法にします。逆に Slack bot を作るならBoltを使ってLambdaとかCloud Functionsとかにデプロイするかなぁという個人の感想でした。
なにかしらの参考になれたらうれしいです。