LoginSignup
5
4

More than 5 years have passed since last update.

AmazonSNSのHTTPリクエストをHubotで受け取る

Last updated at Posted at 2017-01-31

AmazonSNSとは、Amazonが提供するPUSH型通知サービスです。

多数のユーザーに対してメールやPUSH通知を送る以外に、
他のAWSサービスからの通知を受け取り、特定のエンドポイントに対してHTTPリクエストを送ったりできます。
https://aws.amazon.com/jp/sns/

今回は、他のAWSサービスからの通知をチャット上(slack等)に流すために、
HubotでAmazonSNSの通知を受け取ってみます。

なお、ここではメッセージに含まれる署名の確認を行っていません。
なりすまし攻撃を防ぐために署名の検証を行う場合は以下のヘルプをご参照ください。
http://docs.aws.amazon.com/ja_jp/sns/latest/dg/SendMessageToHttp.verify.signature.html

また、AWSへの知識が乏しいために間違った情報が含まれている可能性が大いにございます。
生暖かい目でご覧いただければと思います:bow:

完成品

さっそくですが、完成したスクリプトはこちらです。
次の項から解説していきます。

util = require 'util'
bodyParser = require 'body-parser'
request = require 'request'

module.exports = (robot) ->
  robot.router.post '/webhook/:room', bodyParser.text(), (req, res) ->
    room = req.params.room
    type = if req.headers['x-amz-sns-message-type'] then req.headers['x-amz-sns-message-type'] else 'x-amz-sns-message-type is not found'
    body = if req.body then JSON.parse req.body else {}
    console.log type
    if type == 'SubscriptionConfirmation'
      requestSubscribe body.SubscribeURL
    else if type == 'Notification'
      sendMessage room, body
    res.send  'OK'

  requestSubscribe = (subscribeUrl) ->
    options =
      url: subscribeUrl
      method: 'GET'
    request options, subscribeCallback

  subscribeCallback = (error, response, body) ->
    console.log util.inspect body

  sendMessage = (room, body) ->
    message = body.Subject
    #message += "\r\n" + body.Message
    robot.messageRoom room, message

HubotでHTTPリクエストを受信

Hubotにはexpressが内蔵されており、robot.router.get、またはrobot.router.postを使うことで、HTTPリクエストを受け取ることができます。
AmazonSNSのリクエストはPOSTで来るので、今回はrobot.router.postを使います。
パスの部分は任意ですが、ここでは:roomという形で、投稿するルームIDを指定できるようにしてみました。
(エンドポイントURLを/webhook/%23generalにすると、#generalに投稿されるようにします)

module.exports = (robot) ->
  robot.router.post '/webhook/:room', bodyParser.text(), (req, res) ->

第二引数にはbodyParser.text()を渡します。

AmazonSNSのリクエストは本文がJSON形式の文字列になっているのですが、Content-Typeがplain/textで送られてきます。
Hubotのexpressはデフォルトでplain/textで来たリクエストボディをパースできないので、ここで指定してあげる必要があります。

body-parserモジュールを使うので、コードの上の方でrequireしておきます。

bodyParser = require 'body-parser'

リクエストタイプを判定

次の行からは、パラメータからroomIDの取得、bodyのパースの他、x-amz-sns-message-typeというヘッダーの情報を取得しています。

    room = req.params.room
    type = if req.headers['x-amz-sns-message-type'] then req.headers['x-amz-sns-message-type'] else 'x-amz-sns-message-type is not found'
    body = if req.body then JSON.parse req.body else {}

AmazonSNSからエンドポイントに対して通知を送るには、一度エンドポイントを承認する必要があります。
承認用のリクエストも、通常の通知と同じURLに送られてきます。
リクエストヘッダーに含まれるx-amz-sns-message-typeを取得することで、
承認用のリクエストか通常の通知かを判別できます。

承認用リクエストであればSubscriptionConfirmation、通常の通知であればNotificationが入ってます。

承認用リクエストを処理

    if type == 'SubscriptionConfirmation'
      requestSubscribe body.SubscribeURL
  requestSubscribe = (subscribeUrl) ->
    options =
      url: subscribeUrl
      method: 'GET'
    request options, subscribeCallback

  subscribeCallback = (error, response, body) ->
    console.log util.inspect body

承認用のリクエストだった場合、リクエストボディに含まれる承認用URLにリクエストを送信する必要があります。
承認用URLはbody.subscribeUrlに入っています。
ここでは、requestモジュールを使ってリクエストを送信した後、受け取った内容をコンソールに出力してみました。(出力しなくても特に問題はありません)

requestモジュールもrequireしておきます。

request = require 'request'

通常の通知を処理

    else if type == 'Notification'
      sendMessage room, body
  sendMessage = (room, body) ->
    message = body.Subject
    #message += "\r\n" + body.Message
    robot.messageRoom room, message

通常の通知の場合は、リクエストボディに件名と本文の情報が含まれていますので、それに沿って適切に処理を行います。
body.Subjectが件名で、body.Messageが本文です。
今回は、指定したルームに対して件名の情報だけ投稿するようにしてみました。

エンドポイントの登録

これでHubot側の準備ができましたので、AmazonSNSにエンドポイントを登録してみます。
(ここはあまりうまく説明できる自信が無いのでさらっと流します。詳細はAWSのヘルプページをご覧ください……:sweat_smile:

まず、AmazonSNSのコンソール画面へ移動し、トピックを作成します。
http://docs.aws.amazon.com/ja_jp/sns/latest/dg/CreateTopic.html

続いて、作成したトピックに対して、エンドポイントをサブスクライブします。
http://docs.aws.amazon.com/ja_jp/sns/latest/dg/SubscribeTopic.html

以下のように設定してください。
ProtocolHTTP
Endpointhttp://<your_hubot_host>:<your_hubot_port>/webhook/<roomID>

Create subscriptionをクリックすると、登録したエンドポイントURLに対して、先ほど説明したSubscriptionConfirmationのリクエストが実行されます。正しくスクリプトが動作すれば、エンドポイントが承認されるはずです。
(トピックの画面で、登録したエンドポイントにSubscription IDが付与されていれば承認成功です)

テスト送信

AmazonSNSのコンソールにおいてトピックを表示した状態で、Publish to topicをクリックすると、
メッセージの作成画面が開きます。

適当な件名と本文を入力し、Publish Messageをクリックしてみてください。
チャット上に、件名に入力した文字列が投稿されれば成功です。

他のAWSサービスと連携

あとは、他のAWSサービスの設定において、通知先に先ほど作成したトピックを指定すれば、
AmazonSNSを通じてHubotにリクエストが送信され、チャット上にメッセージが投稿されるようになります。
必要に応じて件名や本文を解析して見やすい形に整形すると良いと思います。

以上、AmazonSNSのHTTPリクエストをHubotで受け取る手順でした。

5
4
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
5
4