43
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Cloud Functionsで作る選択肢つき Slack BOT

Last updated at Posted at 2018-10-14

次のGIF画像のような、メンションに反応して選択肢を表示、そのあと選択に応じた処理をしてくれる BOT を作ります。

いわゆる ChatOps 的な使い方ができそうなやつです。

choices-bot.gif

全体の流れ

Slack の3つの機能を使っているため、ここをちゃんと分割して考えることができれば、理解がしやすくなります。

  • ユーザーからのメンションを受け取る部分 (Event Subscriptions)
  • チャンネルへメッセージを投稿する部分 (Slack API)
  • 選んだ選択肢を受け取る部分 (Interactive Components)

image.png

Cloud Functions の準備

Slack App から来るイベントを受け取るための HTTP サーバーとして Google Cloud Platform の Cloud Functions を使います。

Event Subscriptions の受け取り先になるには、そのサーバーを所有していることを証明する必要があり、 url_verification と呼ばれてます。

簡単にまとめると、challenge という値がリクエストに含まれて来るので、その challenge をそのまま返せばOKです。

さっそく app.js というファイルを作って、満たす関数をつくってみましょう。

app.js
const onRequest = (req, res) => {
    const payload = req.body;

    if (payload.type === 'url_verification') {
        return res.status(200).json({ 'challenge': payload.challenge });
    }

    res.status(200).send('OK');
}

exports.slackChoicesBot = onRequest;

書き終わったら、gcloud コマンドですぐにデプロイできます。 (最近東京リージョンや Node.js 8 に対応したので嬉しいですね!)

npm init
gcloud beta functions deploy slackChoicesBot --runtime nodejs8 --trigger-http --region asia-northeast1

デプロイ完了したら、https://asia-northeast1... という関数の URL が出るので、これをメモしておきましょう。

Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: slackChoicesBot
httpsTrigger:
  url: https://asia-northeast1-xxxxxxx.cloudfunctions.net/slackChoicesBot
...

デプロイした関数は、Google Cloud Platform のコンソール からも確認できます。

image.png

Slack App の作成

Cloud Functions が準備できたので、次は Slack App を作って BOT ユーザーを作ります。

(1) Slack App と BOT User をつくる

https://api.slack.com/apps にアクセスして、Create New App ボタンから Slack App をつくり、Bot Users というメニューから BOT 用のユーザーを作ります。

image.png

(2) メンションを受け取るサーバーを指定

App 設定の Event Subscriptions をONにして、Request URL に Cloud Functions にデプロイした関数の URL を入れます。

次に、Subscribe to BOT Events のところに app_mention を追加します。

image.png

(3) 選択結果を送るサーバーを指定

App の設定で次のように、Interactivity: ON にして、Request URL に Cloud Functions にデプロイした関数の URL を入れます。

image.png

(4) Slack ワークスペースに App をインストール

App の設定の OAuth & PermissionsScopes で、bot という名前の scope を追加します。

image.png

そこまで完了したら、OAuth & Permissions ページにある Install App to Workspace で Slack ワークスペースにインストールしましょう。

image.png

メンションに反応して選択肢を返す

BOT に対してメンションを飛ばすと、Cloud Functions の関数に app_mention という種類のリクエストが来ます。

{
    "type": "app_mention",
    "user": "U061F7AUR",
    "text": "<@U0LAN0Z89> is it everything a river should be?",
    "ts": "1515449522.000016",
    "channel": "C0LAN2Q65",
    "event_ts": "1515449522000016"
}

この内容に対して、選択肢つきの返信をチャンネルに投稿する処理を書きます。チャンネルへの投稿は chat.postMessage API を普通に使います。

選択肢は Slack Attachments として記述できる ので、ドキュメントを読みながら組み立てていきました。

app.js
const request = require('request-promise-native');

async function postMessage(payload) {
    await request.post('https://slack.com/api/chat.postMessage', {
        headers: { 'Authorization': `Bearer ${process.env.BOT_USER_TOKEN}` },
        json: payload,
    });
}

const onRequest = async (req, res) => {
    let payload = req.body;

    if (payload.type === 'url_verification') {
        return res.status(200).json({ 'challenge': payload.challenge });
    }

    if (payload.event && payload.event.type === 'app_mention') {
        if (payload.event.text.includes('hi')) {
            const slackRes = await postMessage({
                text: `<@${payload.event.user}> hi!`,
                channel: payload.event.channel,
                attachments: [createSlackAttachment('BOT response')],
            });
            return res.status(200).send('OK');
        }
    }

    ...

また、チャンネルに投稿するために BOT User OAuth Access Token が必要ですが、このトークンはソースコードには含めたくないものなので、環境変数ファイルに分離 して関数をデプロイしました。

.env.yml
BOT_USER_TOKEN: xoxp-xxxxx...

デプロイコマンドはこんな感じです。

gcloud beta functions deploy slackChoicesBot --runtime nodejs8 --trigger-http --env-vars-file .env.yml --region asia-northeast1

選択肢を選んだ結果を受け取る

BOT が出した選択肢を選んだとき、Cloud Functions の関数に interactive_message という種類のリクエストが来ます。

リクエストの中に「どの選択肢を選んだか」などの情報がはいっているので、それに応じて関数でいろいろなことができます!

※app_mention のときと payload の形式が異なっているので注意が必要です。 app_mention はそのまま構造化された JSON で来ましたが、interactive_message のときは payload という名前のフィールドに 文字列化されたJSONが入っている 構造なので、payload を一度 JSON.parse する必要がありました。

app.js
...
const onRequest = async (req, res) => {
    let payload = req.body;
    
    ...

    if (typeof payload.payload === 'string') {
        payload = JSON.parse(payload.payload)
    }

    if (payload.type === 'interactive_message') {
        const action = payload.actions[0];
        if (action.name === 'choices') {
            const selectedOption = action.selected_options[0];
            return res.status(200).send(`<@${payload.user.id}> Your select value: "${selectedOption.value}"`);
        }
    }
...

まとめ

全体のソースコードは GitHub にアップロードしておきました。
今回は選んだ値を Slack に出すだけの単純な BOT でしたが、これをベースに様々な BOT が開発できそうです。

最近は Cloud Functions のような関数だけをさくっと公開できるサービスが充実してて、非常に開発がやりやすいですね!
その影響もあって、プログラミングの部分よりは、Slack API の仕様の理解のほうが苦労します :sweat_smile:

43
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?