作るもの
背景
- とあるチームで、「週に1件以上ビジネスアイデアを Slack に投稿しよう!」という取り組みを作った
- しばらくは真面目に投稿していたが、忙しさにかまけて徐々に投稿が減る事態に・・・
- 「ボットに催促してもらおう!」ということに
機能&構成
手順
Slack アプリの準備
基本的には、Bolt 入門ガイド に従って準備しますが、いくつか記載のない手順も必要だったので、改めてまとめておきます😃
1. アプリを作成する
slack api ダッシュボードの Your Apps で Create New App をクリックします。
画像のようなダイアログが開くので、ここでは From scratch を選択しておきましょう。

アプリ名を入力し、テストに使うワークスペースを選択します。
アプリ名は後から変更可能なので、適当に入力しておきましょう。

2. トークンとアプリのインストール
トークンには ユーザートークン ボットトークン アプリレベルトークン の3種類が存在するようですが、今回はボットを作りたいのでボットトークンを作成します。
2-1. ボット名の設定
作成したアプリの管理画面サイドメニューの Features > App Home にある Your App’s Presence in Slack 欄の Edit をクリックします。
アプリ名とは別にボットの表示名とユーザー名が必要なようなので、設定しておきます。
この手順を抜かすと、後続手順のワークスペース追加ができませんでした。

2-2. Slash コマンドの作成
メニューの Features > Slash Commands から、 Create New Command を押してコマンド登録します。
デプロイ前でまだ URL が決まっていないので、 Request URL は example.com にしておきました。

2-3. 権限設定
サイドメニューの Features > OAuth & Permissions から必要な権限を追加します。
2-2の手順でで commands は登録されていたので、 chat:write を追加しておきました。

最後に、同じ画面上部の Install to Workspace をクリックして、ワークスペース側からアプリへの認可を与えます。

3. Bolt on Serverless Framework の環境作り
以下の手順で構築しましたが、利用したテンプレートの依存バージョンが古く、解決するのに苦労しました。。
諸々解決した状態でシンプルなサンプルアプリを公開しているで、面倒な人はよろしければ→bolt-serverless-node-example をクローンして使ってください。
(これもいずれ古くなると思いますが😅)
3-1. テンプレートから Serverless アプリを新規作成する
npm i -g serverless
# Serveless 用のテンプレートを利用して新規作成(色々と古いので要注意)
serverless create \
--template-url https://github.com/seratch/serverless-slack-bolt-aws/tree/master \
--path idea-reminder
その後、諸々のパッケージのバージョンを上げ、結果としてエラーになった箇所を修正したり、 TypeScript を入れたり `Serverless Offline が使えるようにしました。(詳細略)
3-2. Slash コマンドをとりあえず動かしてみる
2-2 で登録したコマンドの Edit を押し、 Request URL を ngrok で公開した以下の内容に変更します。
https://xxxxxxxx.ngrok.io/dev/slack/events
app を追加したチャンネルで /idea ほげほげ と打つと、 ボットが ほげほげ と返してくれるやまびこアプリができました。
3-3. イベント購読設定
チャンネルにメッセージが投稿されたら受け取れるようにしておきます。
サイドメニューの Event Subscription > Enable Events を On にし、 Request URL に https://<ngrok のドメイン>/dev/slack/events を設定します。

Subscribe to bot events に message.channels を追加します。

4. 機能を実装
4-1. 特定の文字列が含まれるメッセージに対して返信をする
「【アイデア】」という文字列が含まれる投稿を受信し、スレッドでリプライします。
(TS の型エラーが出てしまうので、本来はよくない書き方なのかもしれない・・・)
boltApp.message('【アイデア】', async ({
message, logger, say,
}) => {
try {
// @ts-ignore
await say({ text: `<@${message.user}>さん、 アイデアの投稿ありがとう!`, thread_ts: message.ts });
} catch (e) {
logger.error(e);
}
});
4-2. 今週のアイデア投稿をカウントする
/idea というスラッシュコマンドを入力したら、そのユーザーの「【アイデア】」という文字列が含まれる今週の投稿をカウントします。
TS の型推論が効かない箇所を補いつつ。
boltApp.command('/idea', async ({
command, logger, ack, say,
}) => {
const thisMonday = dayjs().day(1).startOf('day');
try {
const res = await boltApp.client.conversations.history({
token: process.env.SLACK_BOT_TOKEN,
channel: command.channel_id,
oldest: `${String(thisMonday.unix())}.000000`,
limit: 100, // 週に100件を超えるアイデアがない前提
});
const messages = res.messages as { [key: string]: unknown }[];
const count = messages
.filter((message) => message.user === command.user_id)
.filter((message) => (message.text as string).includes('【アイデア】'))
.length;
let text = `<@${command.user_id}>さんの今週のアイデアはまだ投稿されていません。`
+ `${thisMonday.add(6, 'day').format('M月D日(日)')}までに1件のアイデアを投稿しましょう!`;
if (count > 0) {
text = `<@${command.user_id}>さんは、${thisMonday.format('M月D日(月)')}から今日までの間に${count}件のアイデアを投稿しました!この調子!`;
}
await say(text);
await ack();
} catch (e) {
logger.error(e);
await ack(`:x: Failed to post a message (error: ${e})`);
}
});
/idea を送ると以下のようにメッセージが返ってくるようになりました。
5. AWS にデプロイ
5-1. デプロイ
- AWS のダッシュボードから、デプロイ用の IAM ユーザーを作成し、
AdministratorAccess権限を付与します -
~/.aws/credentialsに上記の IAM ユーザーを設定します
[servelessUser]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXXXXXXXXXXXXXX
aws_secret_access_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ひとまず dev stage にデプロイしてみます
sls deploy --verbose --stage dev --aws-profile serverlessUser
デプロイが成功すると以下のようなログが表示され、自動的に生成された API Gateway 上のエンドポイントも表示されます
✔ Service deployed to stack idea-reminder-dev (164s)
endpoint: POST - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/slack/events
functions:
app: idea-reminder-dev-app (3.5 MB)
5-2. エンドポイントを変更
- 先ほど一時的に設定していた ngrok のエンドポイントを、5-1. で表示された URL に変更しておきます
- Slash コマンドの Request URL
- Event Subscriptions の Request URL
まとめ
- 後で書く


