AWS Lambda/API Gateway を用いた Slack との Serverless 連携
[履歴] [最終更新] (2017/11/17 04:12:06)

概要

こちらのページで基本的な使い方を把握した AWS Lambda は API Gateway のバックエンドとして設定することができます。Slack API から実行するためのエンドポイントを API Gateway で用意することによって、Hubot 等を用いない Serverless な Slack 連携が可能になります。AWS コンソールを利用した具体的な設定およびサンプルコードを示します。

関連するドキュメント

Slack 側の設定

利用形態の選択

Slack API には Slack App という箱を作成してその枠組みの中で設定を管理する利用方法と、Slack API 単体で利用する方法の二つがあります。前者は比較的新しい仕様であり、ドキュメントも別々になっています。例えば Slack に外部から通知する Incoming Webhooks の最新のドキュメントはこちらにあり、昔のドキュメントはこちらにあります。Outgoing Webhooks のように、新しい Slack App の枠組みの中では利用できない API も存在しますが、これは Events API で代替可能です。本ページでは Slack App 内で利用する API を考えることにします。

API の選択

AWS Lambda はイベントドリブンな処理を行うサービスであるため、常時接続が必要な WebSocket を利用した Slack API は利用できません。そこで、Slack 内から特定の条件下で AWS Lambda に HTTP POST が発生する Slack API を利用します。2017/11/14 現在のところ Slash Commands, Events API, Outgoing Webhooks が該当しますが、Outgoing Webhooks は前述のとおり Slack App 内で利用できないため今回は考えないことにします。

Slash Commands を利用する場合の設定

  1. ログインした状態で Your Apps の「Create New App」をクリックして新規 Slack App を作成します。
  2. Slack App の画像や説明、開発メンバーの「Collaborators」設定を行います。
  3. 「Basic Information」から Slash Commands を設定します。
    • Request URL には HTTP POST 先となる API Gateway のエンドポイントを設定します。API Gateway は未設定のため、とりあえず仮の URL を入力しておきます。
    • Escape channels, users, and links sent to your app など、必要に応じて設定します。
  4. 後で Lambda に設定する「Basic Information」の App Credentials / Verification Token の値を確認します。
  5. 「Install your app to your workspace」から workspace に Slack App をインストールします。
  6. /{コマンド名} が利用できることを確認します。

Events API を利用する場合の設定

Slash Commands の場合と同様に Slack App を作成してから、Event Subscriptions 項目で HTTP POST 先となる Request URL や送信したいイベント種別を設定します。Slash Commands の場合と異なり適当な URL を設定することはできず、Slack からの challenge リクエストに適切なフォーマットで応答できるエンドポイントを設定しなければなりません。そのため、後述の AWS 設定を先に行う必要があります。本ページでは Slash Commands を扱うことにします。より複雑な処理を行いたい場合は Events API のドキュメントを読んで対応します。その際には以下のようなことに注意します。

  • Event 種別 message.channels, message.groups, message.im, message.mpim を購読すると投稿されたメッセージを取得できます。
  • Bot 自身からの投稿に対する Event を無視するためには subtype bot_message を利用します。
  • Slash Commands の場合と同様に tokenteam_id を認証に利用します。
  • 同じ event_id の HTTP POST が最大 3 回 (直後、1分後、5分後) 送られてくるため、重複して処理しないように工夫する必要があります。
    • X-Slack-Retry-Reason ヘッダーの値を確認してリトライがなされた原因を確認します。解消できない場合 X-Slack-No-Retry: 1 として HTTP POST にレスポンスを返すとリトライを抑制できます。
    • Slack からの HTTP POST における X-Slack-Retry-Num ヘッダーの値が 1,2,3 となっていることを利用して重複処理を回避できます。
    • 簡易的には event_idevent_time で重複処理を回避することもできます。

AWS 側の設定

Slack からのリクエストに含まれる各種情報を文字列化して返すだけの Lambda 関数を作ってみます。

  1. こちらのページと同様に Lambda 実行時に利用する IAM を作成します。「AWS service」一覧から Lambda を選択して、ポリシーは CloudWatch Logs 出力が必要なため AWSLambdaBasicExecutionRole を付与します。
  2. Lambda 関数を Author from scratch から先程の IAM を設定して作成します。
  3. 以下のようなソースコードを index.js に記載して保存します。slack-echo-command という名称の、nodejs に関する Lambda blueprint から引用して簡略化したものです。
  4. Triggers から API Gateway を選択して追加します。Lambda 関数内で認証するため Security は Open にします。
  5. Api Gateway コンソールの Stages → prod から確認できる Invoke URL をもとに、Slack で仮に入力した URL を更新します。
  6. Slack から /コマンド名 を入力してレスポンスが返ることを確認します。
  7. Slack API は 3 秒でタイムアウトします。Lambda 関数がメモリにロードされていない場合は起動に時間がかかります。起動時間によるタイムアウトを回避するためには『AWS Lambda 関数を cron のように定期実行』に記載の CloudWatch イベントで Lambda を 5 分毎に定期実行するのが簡単です。

index.js

'use strict';

const qs = require('querystring');
const token = 'Slack App で確認した値'; // KMS で暗号化した方が安全です。

exports.handler = (event, context, callback) => {

  const params = qs.parse(event.body);
  const requestToken = params.token;
  if (requestToken !== token) {
    console.error(`Request token (${requestToken}) does not match expected`);
    return callback('Invalid request token');
  }

  const user = params.user_name;
  const command = params.command;
  const channel = params.channel_name;
  const commandText = params.text;

  callback(null, {
    statusCode: '200',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(`${user} invoked ${command} in ${channel} with the following text: ${commandText}`)
  });
};
関連ページ
    概要 Mattermost においても Slack のような API が提供されています。動作確認のためのインストール手順および API の利用例を記載します。 インストール手順 CentOS6 の場合は以下のようになります。 Installing Mattermost on RHEL 6.6 Database のインストール