🐈

Amazon APIGatewayからSlackにメッセージ投稿する

2023/01/13に公開

概要

Amazon APIGatewayで受けとったリクエストにトークン等の情報を付け加えてSlackAPIから投稿(リンクテキスト+メンション)を行う手順のメモ

今回はWebページからAPIGatewayにリクエスト→APIGatewayのパラメータマッピングで変換を加えてSlackAPIへ、という想定での構成

イメージは以下(APIをカスタムドメイン名で公開するときにエッジ最適化を選択し、CloudFront経由でAPIGatewayにアクセスさせる様にする。これによってWAFによるIPアドレス制限などが容易となる)

Slack側にアプリを作成する

まずSlack側にアプリを作成する必要がある。今回はBotによる投稿を想定するため、Bot Token Scopeschat:writeスコープを追加したアプリを作成し、そのトークン(xoxb-から始まる)を取得してくる。

方法は以下のサイトに分かりやすくまとめられているので省略
https://zenn.dev/kou_pg_0131/articles/slack-api-post-message

APIGatewayの設定

東京リージョンでAPIを作成していく。APIを作成からREST API(エンドポイントタイプはエッジ最適化)を選択すると以下の画面になる。まずはリソース(APIエンドポイント)を設定するため、アクション>リソースの作成を行う。

好きなリソース名を設定してリソースの作成を行う(CORSを行いたい場合は後から設定できるのでチェックをつけてもつけなくてもOK)

続いて、先ほど作成したリソースの下でメソッド(どのHTTPメソッドでリクエストを受け取るか)を設定する。アクション>メソッドの作成でPOSTを選択する。すると画面右側にセットアップ画面が表示される。

ここで統合タイプにはHTTPを選択し、エンドポイントにはSlackAPIの受け口となるhttps://slack.com/api/chat.postMessageを指定する

保存すると画面右側が以下の様に通信のフローを表すものにかわる

続いてリンク文字となっている統合リクエストをクリックし、最下部のマッピングテンプレートを設定していく。

JSONパラメータの変換

今回、APIGatewayへ送るPOSTリクエストのJSONパラメータは以下を想定とする

{"link":"https://example.com", "text":"test", "mention":["username1", "username2"]}

上のリクエストをAPIGatewayで受け取り、以下の形に変換してSlackAPIへPOSTする

{"channel":"【チェンネル名もしくはチェンネルID】", "text":"<https://example.com|test> <@【username1のUserID】> @【username2のUserID】>"}

それぞれのパラメータの詳細は以下

  • channel:投稿先Slackのチェンネル名もしくはチェンネルIDを指定する。値はAPIGatewayのステージ変数から取得する
  • text:Slackに投稿する文章。<【URL】|【テキスト】>とすることでリンクテキストとすることができる。また、<@【SlackのUserID(メンバーID)】とすることでそのユーザをメンションすることができる。UserIDはステージ変数から取得する(つまり、APIGatewayでは名前だけを受け取り、マッピングテンプレートでUserIDに変換するということ)

SlackAPIのトークン設定(Authorizationヘッダの追加)

JSON形式のパラメータでは、SlackAPIのtoken(xoxb-から始まる)はAuthorizationヘッダにBearerトークンとして指定する必要がある模様。application/x-www-form-urlencoded形式で送る場合はボディパラメータの中にtoken=xoxb-・・・という形式でもOK

マッピングテンプレートに設定するスクリプト

上記の変換を行うマッピングテンプレート(VTLスクリプト)は以下となる。

#set($link = $input.path('$.link'))
#set($text = $input.path('$.text'))
#set($mention_indexes = $input.path('$.mention'))

#set($concat_text = "<${link}|${text}>")

#foreach($i in $mention_indexes)
    #if($i == "username1")
        #set($concat_text = "$concat_text <@$stageVariables.username1UserID>")
    #elseif($i == "username2")
        #set($concat_text = "$concat_text <@$stageVariables.username2UserID>")
    ・
    ・
    ・
    (複数メンションしたいユーザがいる場合は上と同じ様にここに列挙)
    #end
#end

#set($context.requestOverride.header.Authorization = "Bearer $stageVariables.slackToken")
{
    "channel":"$stageVariables.channelName",
    "text":"$concat_text"
}

(スクリプトの補足)

  • $stageVariables.で始まる部分はステージ変数。後ほど設定する
  • #set($context.requestOverride.header.Authorization = "Bearer $stageVariables.slackToken")でリクエストヘッダにSlackAPI用のトークンを付与する

上記のスクリプトをマッピングテンプレートに設定する
以下の画像のようにContent-Typeをapplication/jsonにし、その下に出現するスクリプト入力欄に先ほどのスクリプトをコピペする

リクエスト本文のパススルーのラジオボタンについて、「なし」に設定することでリクエストのContent-Typeがapplication/jsonではないリクエストをすべて415エラーとする模様。

APIのテスト

マッピングテンプレートを保存したら一度テストしてみる
メソッドの実行という画面に戻るとテストという項目があるのでクリックすると、以下の画面となる

また、少し下へいくとステージ変数という場所がある
ここのステージ変数はテスト用なので、適当な値を設定しておく(本物のステージ変数はAPIのデプロイ後に設定する箇所がある)
※マッピングテンプレート内に記載された$stageVariables.の分だけ出現する。もし表示されていない場合は一度画面をリフレッシュするか別のサービス等に移動してから再度確認すると表示されているはず

リクエスト本文には、APIGatewayに送る想定の以下JSONを設定する

{"link":"https://example.com", "text":"test", "mention":["username1", "username2"]}

最後にテストを押下することで、APIGatewayに想定のJSONが送られ、マッピングテンプレートで変換されてSlackAPIにリクエストが送信される。
右側にリクエストの結果が表示されるが、ここでステータスコード200であればマッピングテンプレートによる変換までは成功している(SlackAPIのトークンの値が不正なので投稿自体は失敗する)

https://requestbin.com/

APIのデプロイとステージ変数の設定

APIのテストまでで問題なければ、APIのデプロイとステージ変数($stageVariables.から参照する値)を設定する。

APIのデプロイはアクション>APIのデプロイを選択する。ステージ名は適当に。

すると、左側メニューのステージから新たにステージエディターが表示される様になる。
タブのステージ変数からマッピングテンプレートで呼び出したい値を設定する。

ここのまでの例と同じ様に設定していた場合、以下の4つが対象となる

  • username1UserID
  • username2UserID
  • channelName ←前述の通りチェンネル名もしくはチャンネルID
  • slackToken

変数の設定が完了したら、URLの呼び出しに書かれたURLに対してAjaxリクエストをしてみる。

今回の場合はhttps://0jyfnox6ak.execute-api.ap-northeast-1.amazonaws.com/test/to-slack-apiに対して、以下のボディをもつAjaxリクエストを行うということ。
※Content-Typeをapplication/jsonにしないとエラーになる様設定したことを忘れずに。

{"link":"https://example.com", "text":"test", "mention":["username1", "username2"]}

無事Slackに投稿ができていればOK!

もしドメイン名を取得している場合、APIGatewayのカスタムドメイン名からドメイン名作成→APIマッピングから作成したステージを紐づければよい。


以上、お疲れ様でした!

Discussion