Function ComputeでサーバレスなSlack Botを作る

作るもの

Alibaba CloudのFunction ComputeでサーバレスなSlack Botを作ります。

/my_echo [TEXT]と入力すると作ったBot[TEXT]を返すechoのコマンドを作ります。

f:id:asmsuechan:20180712212230p:plain

LambdaとSlack Botの連携をする公式ドキュメントを参考にしています。Slack側の設定はこちらを参照してください。

Handling events from the Events API using AWS Lambda | Slack

botの追加

App Deirectoryで「bots」と検索してbotを追加します。 Add Apps to Slack | Apps and Integrations | Slack App Directory

BotsのページでAdd Configurationを押します f:id:asmsuechan:20180712143814p:plain

適当なUsernameを入力します。私は安直にtest_botにしました。 f:id:asmsuechan:20180712143819p:plain

API Tokenをコピーします。 f:id:asmsuechan:20180712143822p:plain

ここの中程にあるcurlを実行して正常にbotが喋ったら成功です。

Slack Web API | Slack

$ curl -X POST -H 'Authorization: Bearer xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-type: application/json' \
--data '{"channel":"random","text":"I hope the tour went well, Mr. Wonka.","attachments": [{"text":"Who wins the lifetime supply of chocolate?","fallback":"You could be telling the computer exactly what it can do with a lifetime supply of chocolate.","color":"#3AA3E3","attachment_type":"default","callback_id":"select_simple_1234","actions":[{"name":"winners_list","text":"Who should win?","type":"select","data_source":"users"}]}]}' \
https://slack.com/api/chat.postMessage

シャァベッタァァァァァァァ!! f:id:asmsuechan:20180712144622p:plain

Function Computeで関数の追加

一つ適当に作っておきます。以下を参照のこと。

qiita.com

Slash Commandsの追加

f:id:asmsuechan:20180712151039p:plain

my_echoってコマンドにします。 f:id:asmsuechan:20180712212639p:plain

実装

nodejsでソースコードを書きます。

こんな感じの構成にします。

$  tree
.
├── node_modules
....()
├── package.json
├── slack.js
└── yarn.lock

npmのライブラリであるrequestを使うので、/node_modulesも一緒にアップロードします。

※zipでアップロードしようとしましたがなんか壊れてるっぽい(2018/7/12)のでコンソール画面から「フォルダのアップロード」でアップロードしました。

const https = require('https')
const req = require('request')
const getRawBody = require('raw-body')

const VERIFICATION_TOKEN = "xxxxxxxxxxxxxxxxxxxxx"
const ACCESS_TOKEN = "xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

function post(query, request, response) {
  getRawBody(request, function(err, data){
    const params = {
      form: {
        token: ACCESS_TOKEN,
        channel: query.channel,
        text: query.text
      }
    }
    req.post('https://slack.com/api/chat.postMessage', params)
      .on('response', (res) => {
        const body = data
        const status = 200
        respBody = new Buffer(body)
        response.setStatusCode(status)
        response.setHeader('content-type', 'application/json')
        response.send(respBody)
      })
  })
}

function process(request, response) {
  const data = request.queries
  const message = {
    channel: data.channel,
    text: data.text
  }

  post(message, request, response)
}

function verify(request, response) {
  let status, body
  if (request.queries.token === VERIFICATION_TOKEN) {
    status = 200
    body = `challenge:${VERIFICATION_TOKEN}`
  } else {
    status = 500
    body = 'error'
  }
  getRawBody(request, function(err, data){
    respBody = new Buffer(body)
    response.setStatusCode(status)
    response.setHeader('content-type', 'application/json')
    response.send(respBody)
  })
}

exports.handler = (request, response, context) => {
  switch (request.queries.type) {
    case "url_verification": verify(request, response, '', ''); break
    case "event_callback": process(request, response); break
    default: process(request, response)
  }
}

verify()はSlack APIのサービス追加時のEvent Subscriptionsで使います。

url_verification event | Slack

handlerの第一引数であるrequestには以下のオブジェクトが入っています。

{
    "domain": null,
    "_events": {},
    "_eventsCount": 0,
    "method": "GET",
    "clientIP": "54.234.37.47",
    "url": "/2016-08-15/proxy/test/slack/?token=xxxxxxxxxxxxxxxxx&team_id=TBPAQJLHK&team_domain=asmsuechan&channel_id=CBNF2BSQG&channel_name=random&user_id=UBNF2BPB2&user_name=suenagaryoutaabc&command=%2Fmy_echo&text=asaaaaa&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands",
    "path": "/",
    "queries": {
        "channel_id": "CBNF2BSQG",
        "channel_name": "random",
        "command": "/my_echo",
        "response_url": "https://hooks.slack.com/commands/TBPAQJLHK/397188022723/xxxxxxxxxxxxxxxxxxxxxxx",
        "team_domain": "asmsuechan",
        "team_id": "TBPAQJLHK",
        "text": "asaaaaa",
        "token": "xxxxxxxxxxxxxxxxxxxxxxx",
        "user_id": "UBNF2BPB2",
        "user_name": "suenagaryoutaabc"
    },
    "headers": {
        "accept": "application/json,*/*",
        "user-agent": "Slackbot 1.0 (+https://api.slack.com/robots)"
    }
}

デバッグむずい。SlackへのPOSTリクエスト送信部とFunctionComputeのhandler部分を分けて実装したら作りやすいです。

特にBotsのIntegration Settingsのchallenge。

検証

Image from Gyazo

よっしゃできた。

まとめ

Slackさん、諸々のキーがどこにあるか分かりにくい。