dely engineering blog

レシピ動画サービス「kurashiru」を運営するdelyのテックブログ

サーバーレス+Go言語で作るインタラクティブな哲学slackBot

本記事はdely Advent Calendar 2018の19日目の記事です。

Qiita : dely Advent Calendar 2018 - Qiita
Adventar : dely Advent Calendar 2018 - Adventar

前日は、弊社でSREをしている井上がkurashiruのデプロイについて記事を書きましたので是非読んでみてください! tech.dely.jp

はじめに

こんにちは。サーバーサイドエンジニア兼、新米slackBot整備士のjoe (@joooee0000) です。 前回の11日目の記事では、くだらないslackBotを作るモチベーションやBotアプリの作成方法について書かせていただきました。

tech.dely.jp

本記事では、InteractiveMessages(InteractiveComponents)を用いた哲学slackBotの実装面について紹介させていただきます。
また、slackでは

をつけることができますが、今回はボタンを使っています。
(こちらのような見た目です)

f:id:joe0000:20181217151059p:plain

(深い。。。)

機能としては、

  • 数百種類の哲学名言の中から毎朝9:30に1つをつぶやく
  • メンバーがボタンを押して哲学を評価する
  • 誰がどのように評価をしたかがslackにポストされる

というシンプルなBotです。これだけでも、みんなが参加してくれて盛り上がったりします! 1つの哲学に対してみんなの受け取り方が全く違ったりするので、今やとても興味深いです。

構成図

f:id:joe0000:20181218194353p:plain

インタラクティブなBotを構成する要素は二種類あります。

  1. ボタン付きのメッセージをチャンネルにポストする
  2. ボタン付きのメッセージへのユーザーのリアクションを受け取り、処理する

こちらの二種それぞれについて書いていきます。

また、slackBotの実装を始めるには、slackのアプリを作成します。アプリの作成に関しては、11日目の記事に書いてあるので、参照してください。

ボタン付きメッセージをポストする

最初に、ボタン付きメッセージを決まった時間にチャンネルにポストする部分について書きます。 組み合わせは、下記のシンプルな構成です。

  • Go言語
  • DynamoDB
  • AWS SAM
    • AWS Lambda
    • AWS CloudWatch

哲学名言はクロールしてDynamoDBにデータを保持しています。クロールの部分やDynamoDBへのデータ保持について話すと長くなってしまうので、あらかじめデータが入ったDynamoDBが用意されている前提で話します。

1. Go言語でメッセージをポストするコードを書く

コードは、Lambdaで実行する用に書いていきます。 こちらが簡略化したサンプルコードです。長くなりすぎないように、DynamoDBから哲学用語を取得する部分などは省略しています。

package main

import (
    "errors"
    "log"
    "math/rand"
    "strconv"
    "syscall"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/dynamodb"
    "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
    "github.com/nlopes/slack"
)

type PhilosophicalWord struct {
    PhilosophicalWordId int    `dynamodbav:"philosophical_word_id"`
    FormedText          string `dynamodbav:"formed_text"`
    PlainText           string `dynamodbav:"plain_text"`
    ScholarName         string `dynamodbav:"scholar_name"`
}

func getRandomPhilosophyWordByDynamoDB() (result *PhilosophicalWord, err error) {
   // DynamoDBから哲学名言を取得する処理
    return
}

func getIconEmoji(scholarName string) (emojiStr string) {
   // 絵文字の文字列を学者の名前から取得する処理
    return 

func handleSendPayload(client *slack.Client, channelId string, sendText string, scholarName string) error {

    actionName := "philosophical_value"
    attachment := slack.Attachment{
        Color:      "#42cbf4",
        CallbackID: "philosophical",
        Fields: []slack.AttachmentField{
            {
                Title: "Please evaluate the word!! :smirk_cat:",
            },
        },
        Actions: []slack.AttachmentAction{
            {
                Name:  actionName,
                Text:  "すごく微妙",
                Type:  "button",
                Style: "danger",
                Value: "minus2",
            },
            {
                Name:  actionName,
                Text:  "微妙",
                Type:  "button",
                Style: "danger",
                Value: "minus1",
            },
            {
                Name:  actionName,
                Text:  "普通",
                Type:  "button",
                Value: "zero",
            },
            {
                Name:  actionName,
                Text:  "良い",
                Type:  "button",
                Style: "primary",
                Value: "plus1",
            },
            {
                Name:  actionName,
                Text:  "すごく良い",
                Type:  "button",
                Style: "primary",
                Value: "plus2",
            },
        },
    }

    params := slack.PostMessageParameters{
        IconEmoji: getIconEmoji(scholarName),
        Username:  scholarName,
    }

   // メッセージの本文を定義する処理
    msgOptText := slack.MsgOptionText(sendText, true)
   // 必須項目を定義する処理
    msgOptParams := slack.MsgOptionPostMessageParameters(params)
   // ボタンを定義する処理
    msgOptAttachment := slack.MsgOptionAttachments(attachment)

   // メッセージ送信処理
    if _, _, err := client.PostMessage(channelId, msgOptText, msgOptParams, msgOptAttachment); err != nil {
        log.Println("Slack PostMessage Error")
        return err
    }
    return nil
}

func LambdaHandler() error {
    oauthAccessToken, found := syscall.Getenv("OAUTH_ACCESS_TOKEN")
    if !found {
        log.Print("OAuth Access Token Not Found")
        return errors.New("OAuth Access Token Not Found")
    }

    channelId, found := syscall.Getenv("CHANNEL_ID")
    if !found {
        log.Print("Channel Id Not Found")
        return errors.New("Channel Id Not Found")
    }

    result, err := getRandomPhilosophyWordByDynamoDB()
    if err != nil {
        log.Print("DynamoDB Error")
        return err
    }

    sendText := result.FormedText
    scholarName := result.ScholarName

    client := slack.New(oauthAccessToken)

    handleSendPayload(client, channelId, sendText, scholarName)
    return nil
}

func main() {
    lambda.Start(LambdaHandler())
}

slackのAPI Clientには、nlopes/slackというpackageを使用しています。

今回はメセージをチャンネルに送ることが目的なので、こちらのpackageの中でもchat.goPostMessage関数を使ってslackチャンネルにメッセージを送信しています。

nlopes/slackPostMessageは、要件をslackメッセージの要素ごとに分解して設定できるようになっています。

ざっくり言うと、

  • PostMessageParameters: メッセージ送信時の必須パラメータ部分(ピンク部分)
    • ユーザー名やアイコンの設定など
  • MsgOptionText: メッセージ本文(黄色部分)
  • MsgOptionAttachments: アタッチメント部分(青部分)
    • ボタンの設定

f:id:joe0000:20181217152218p:plain

という構成です。 nlopes/slackでは、上記であげたそれぞれの要素が構造体として定義されています。PostMessageの引数は可変引数となっており、必要な型だけを引数として指定することができます。
例えば、ユーザー名やアイコンの設定はデフォルトでよく、ボタンも不必要なメッセージのみを送る場合は、

msgOptText := "適当"
client.PostMessage("your-channel-name",  msgOptText)

だけでシンプルなメッセージを送信することができます。

また、アタッチメント部分(今回だとボタンを定義している部分)もいくつかの構造体の入れ子になっており、AttachmentField(アタッチメント部分に記載できるテキストなど)やAttachmentAction(ボタンやプルダウンメニューなどのアクションの具体的な内容を指定するところ)を指定できるようになっています。ここら辺の値をやりたいことに対して柔軟に変えることでカスタマイズしていきます。

ここに書いたこと以外にも、nlopes/slack をつかって様々なことができるので、詳しくはコードを読むか、docsを参照してください。

2. AWS SAMで定期実行するLambdaを構築する

AWS SAMの設定ファイルを作成する

AWS SAMとは、サーバーレスアプリケーションモデルの略で、AWSでサーバーレスアプリケーションを構築するために使用することができるオープンソースフレームワークです。テンプレートに必要な情報を記入することで、サーバーレスアプリケーションの構築を手軽に行うことができます。(本当に手軽にできます)

下記のサンプルは、

  • Lambdaを動かすためのIAM Roleの作成
  • 毎日朝9:30に稼働するメッセージポスト用Lambdaの設定(1で作成したLambdaコード)

が記述されています。 哲学Botは、DynamoDBに哲学用語をためているため、DynamoDBへのアクセス権限もつけています。

また、ソースコードに載せられないセキュアな情報は環境変数にしてLambdaから呼び出すようにしています。
Systems Manager パラメータを使って設定した変数をLambdaのコード内から呼び出せるように、Parametersという項目を指定します。
今回は、

  • チャンネルID (slackのチャンネル名)
  • OAuth Access Token (slackのAPIを呼び出すためのトークン)

をパラメータ化して環境変数として呼び出せるようにしています。 Systems Manager パラメータの設定の仕方は、こちらを参照してください。

# template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Create Lambda function by using AWS SAM.
Parameters:
  ChannelId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /philosophy_bot/channel_id
  OauthAccessToken:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /philosophy_bot/oauth_access_token
Resources:
  LambdaIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        -
          PolicyName: "philosophy-slack-bot"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: "dynamodb:*"
                Resource: "*"
              -
                Effect: "Allow"
                Action: "cloudwatch:*"
                Resource: "*"
  PhilosophySlackBot:
    Type: AWS::Serverless::Function
    Properties:
      Handler: philosophy-slack-bot
      Runtime: go1.x
      Role: !GetAtt LambdaIamRole.Arn
      Environment:
        Variables:
          CHANNEL_ID: !Ref ChannelId
          OAUTH_ACCESS_TOKEN: !Ref OauthAccessToken
      CodeUri: build
      Description: 'Post philosophical word to slack'
      Timeout: 30
      Events:
        Timer:
          Type: Schedule
          Properties:
            Schedule: cron(30 0 * * ? *) # JST 09:30

デプロイする

デプロイするにあたって、下記を済ませておく必要があります。

  • IAM Roleの設定
    • こちらで言う所のIAM Roleの設定は、AWS SAMを動かすための権限付与
    • (正しく解説できる自信がな記事が長くなりすぎるため詳細の説明を省略)
  • AWS CLIのセットアップ

先ほど作成したAWS SAMのtemplate.ymlを使って、aws-cliのコマンドでデプロイすることができます。

# deploy.sh
#!/usr/bin/env bash
cd ./slack_bot
GOARCH=amd64 GOOS=linux go build -o ../build/philosophy-slack-bot
cd ../
zip build/philosophy-slack-bot.zip build/philosophy-slack-bot

aws cloudformation package --profile yourprofile \
    --template-file template.yml \
    --s3-bucket serverless \
    --s3-prefix philosophy-slack-bot \
    --output-template-file .template.yml
aws cloudformation deploy --profile yourprofile \
    --template-file .template.yml \
    --stack-name philosophy-slack-bot \
    --capabilities CAPABILITY_IAM

こちらのスクリプトでは、前半の部分でGoのコードのデプロイパッケージ(コードと依存関係で構成される .zip ファイル)を作成しています。

windowsにおけるGoのデプロイパッケージの作り方が少し違うようなので、デプロイを実行するOSによって書き方を変える必要があります。こちらのサンプルコードは、MacOSで作成する場合のサンプルとなっています。詳しくは、こちらをご覧ください。

また、ここで紹介しているサンプルでは、下記のようなファイルの階層を想定しています。

.
├── build
├── deploy.sh
├── slack_bot
│   └── main.go
└── template.yml

これで、deploy.shを実行するだけでLambdaが指定したevent通りの時間に定期実行されるようになります。

ここまでで、メッセージの見た目はslackチャンネルにポストできるようになりました!!

f:id:joe0000:20181217233108p:plain

(この時点では、ボタンを押してもなにも起こらない)

ボタン付きメッセージへのユーザーのリアクションを受け取る

ボタンをおしたらアクションが起こるようにしていきます。

1. Go言語でユーザーのリアクション情報を受け取るコードを書く

ユーザーがボタンを押すと、設定したURLにPOSTリクエストが届きます。 なので、常にリクエストを待ち受けるAPIにしておく必要があります。

構成は、下記のようなシンプルなものです。

  • Go言語
  • DynamoDB
  • AWS SAM
    • AWS Lambda
    • AWS API Gateway

最初に、API Gatewayにslackからリクエストがきた時のLambdaの処理を書いていきます。 処理は、下記のような手順で行います。

  1. callbackレスポンスをParseする
  2. VerificationTokenをチェックする
  3. 結果を確認し、所望の処理をする
  4. リクエストのレスポンスとして、callbackレスポンスの中のoriginal_messageと同じ形式のレスポンスを返す

1. callbackレスポンスをParseする

リクエストをJSON形式にParseすると、このような形式のレスポンスが返ってきます。 返ってきたレスポンスを、nlopes/slack packageが定義してくれている slack.InteractionCallback 型にマッピングします。

{  
   "type":"interactive_message",
   "actions":[  
      {  
         "name":"philosophical_value",
         "type":"button",
         "value":"plus1"
      }
   ],
   "callback_id":"philosophical",
   "team":{  
      "id":"xxx",
      "domain":"xxx"
   },
   "channel":{  
      "id":"xxx",
      "name":"xxx"
   },
   "user":{  
      "id":"xxx",
      "name":"joe"
   },
   "action_ts":"1544247183.026560",
   "message_ts":"1544247178.002000",
   "attachment_id":"1",
   "token":"DUMMYDUMMYDUMMY",    # VerificationToken
   "is_app_unfurl":false,
   "original_message":{  
      "type":"message",
      "subtype":"bot_message",
      "text":"言い違い、聞き違い、読み違い、書き違いは受ける側の願望を表わしてる。 - ジークムント・フロイト- ",
      "ts":"1544247178.002000",
      "username":"ジークムント・フロイト",
      "icons":{  
         "emoji":":tetsu_freud:"
      },
      "bot_id":"xxxxxx",
      "attachments":[  
         {  
            "callback_id":"philosophical",
            "id":1,
            "color":"42cbf4",
            "fields":[  
               {  
                  "title":"please score the word!! :smirk_cat:",
                  "value":"",
                  "short":false
               }
            ],
            "actions":[  
               {  
                  "id":"1",
                  "name":"philosophical_value",
                  "text":"すごく微妙",
                  "type":"button",
                  "value":"minus2",
                  "style":"danger"
               },
               {  
                  "id":"2",
                  "name":"philosophical_value",
                  "text":"微妙",
                  "type":"button",
                  "value":"minus1",
                  "style":"danger"
               },
               {  
                  "id":"3",
                  "name":"philosophical_value",
                  "text":"普通",
                  "type":"button",
                  "value":"zero",
                  "style":""
               },
               {  
                  "id":"4",
                  "name":"philosophical_value",
                  "text":"良い",
                  "type":"button",
                  "value":"plus1",
                  "style":"primary"
               },
               {  
                  "id":"5",
                  "name":"philosophical_value",
                  "text":"すごく良い",
                  "type":"button",
                  "value":"plus2",
                  "style":"primary"
               }
            ]
         }
      ]
   },
   "response_url":"https:\/\/hooks.slack.com\/actions\/DUMMYDUMMY\/DUMMYDUMMY\/DUMMYDUMMY",
   "trigger_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

2. VerificationTokenをチェックする

さきほどParseしたリクエストに、token というkeyがはいっています。こちらのトークンを使って、不正なリクエストでないかを判定することができます。
こちらのtokenと作成したアプリの管理画面で参照できるVerificationTokenが一致するかをチェックします。

3. 結果を確認し、所望の処理をする

哲学Botは、DynamoDBにcallbackとして送られてきた評価を記録していますが、割愛します。

4. リクエストのレスポンスとして、callbackレスポンスの中のoriginal_messageと同じ形式のレスポンスを返す

ParseしたJSONを見ると、original_messageという項目が返ってきているのがわかります。 こちらのJSONを希望のレスポンスに加工して返すことで、slackに反映させることができます。

また、response_type/replace_originalというkeyをoriginal_messageのJSONに追加して返すことで、下記のような様々な見た目のメッセージの反映ができます。

  • アクションしたユーザーだけが見れる(Only visible to you)

    • response_type: ephemeral (default)

f:id:joe0000:20181217181135p:plain

  • メッセージの上書き

    • response_type: in_channel
    • replace_original: true

f:id:joe0000:20181217175616p:plain

  • 新しいメッセージのポスト

    • response_type: in_channel

f:id:joe0000:20181217175644p:plain

レスポンスの選択肢に関しては こちらを参照しました。

サンプルコード

本記事で紹介している哲学Botのレスポンス形式は、ユーザーのアクションを検知したら、前のメッセージをうわ書かずに新しいメッセージをチャンネルにポストする方式です。

package main

import (
    "context"
    "encoding/json"
    "net/url"
    "strings"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/nlopes/slack"
)

func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
   //  1. callbackレスポンスをParseする
    str, _ := url.QueryUnescape(request.Body)
    str = strings.Replace(str, "payload=", "", 1)

    var message slack.InteractionCallback
    if err := json.Unmarshal([]byte(str), &message); err != nil {
        return events.APIGatewayProxyResponse{Body: "json error", StatusCode: 500}, nil
    }
   //  2. VerificationTokenをチェックする
    verificationToken, found := syscall.Getenv("VERIFICATION_TOKEN")
    if !found {
        return events.APIGatewayProxyResponse{Body: "NoVerificationTokenError", StatusCode: 500}, nil
    }
    if message.Token != verificationToken {
        return events.APIGatewayProxyResponse{Body: "InvalidVerificationTokenError", StatusCode: 401}, nil
    }

   //  3. callbackの中身をみて所望の処理をする
    var score string
    value := message.ActionCallback.Actions[0].Value
    switch value {
    case "plus2":
        score = "すごく良い"
    case "plus1":
        score = "良い"
    case "zero":
        score = "普通"
    case "minus1":
        score = "微妙"
    case "minus2":
        score = "すごく微妙"
    default:
        score = "0"
    }
   
   // 4. リクエストのレスポンスとして、callbackレスポンスと同じ形式のレスポンスを返す
    userName := message.User.Name
    resMsg := userName + "さんが" + "「" + score + "」" + "と評価しました"

    orgMsg := message.OriginalMessage
    orgMsg.Text = ""
   // 今回はメッセージを上書きせず、チャンネル全体に投稿する
    orgMsg.ResponseType = "in_channel"
    orgMsg.Attachments[0].Color = "#f4426e"
   // ボタンを空にする
    orgMsg.Attachments[0].Actions = []slack.AttachmentAction{}
   // 返したいレスポンスを定義する
    orgMsg.Attachments[0].Fields = []slack.AttachmentField{
        {
            Title: resMsg,
            Value: "",
            Short: false,
        },
    }

    resJson, err := json.Marshal(&orgMsg)
    if err != nil {
        return events.APIGatewayProxyResponse{Body: "JsonError", StatusCode: 500}, nil
    }

    return events.APIGatewayProxyResponse{Body: string(resJson), StatusCode: 200}, nil
}

func main() {
    lambda.Start(handleRequest)
}

2. AWS SAMでslackからのリクエストを受け取るAPI Gatewayを構築する

AWS SAMの設定ファイルを作成する

こちらの設定ファイルは、先ほどのボタン付きメッセージをslackにポストする部分も一緒に含まれています。

前回と同様に、VerificationTokenなどのセキュアな情報はSystems Manager パラメータで設定したものを呼び出しています。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Create Lambda function by using AWS SAM.
Parameters:
  ChannelId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /philosophy_bot/channel_id
  OauthAccessToken:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /philosophy_bot/oauth_access_token
  VerificationToken:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /philosophy_bot/verification_token
Resources:
  LambdaIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        -
          PolicyName: "philosophy-slack-bot"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: "dynamodb:*"
                Resource: "*"
              -
                Effect: "Allow"
                Action: "cloudwatch:*"
                Resource: "*"
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:DescribeLogGroups"
                  - "logs:DescribeLogStreams"
                  - "logs:PutLogEvents"
                  - "logs:GetLogEvents"
                  - "logs:FilterLogEvents"
                Resource: "*"
  # ボタンつきメッセージのポスト
  PhilosophySlackBot:
    Type: AWS::Serverless::Function
    Properties:
      Handler: philosophy-slack-bot
      Runtime: go1.x
      Role: !GetAtt LambdaIamRole.Arn
      Environment:
        Variables:
          CHANNEL_ID: !Ref ChannelId
          OAUTH_ACCESS_TOKEN: !Ref OauthAccessToken
      CodeUri: build
      Description: 'Post philosophical word to slack incoming webhooks'
      Timeout: 30
      Events:
        Timer:
          Type: Schedule
          Properties:
            Schedule: cron(30 0 * * ? *) # JST 09:30
  # ボタン付きメッセージのレスポンスAPI
  PhilosophySlackBotInteractiveApi:
    Type: AWS::Serverless::Function
    Properties:
      Handler: philosophy-slack-bot-interactive-api
      Runtime: go1.x
      CodeUri: build
      Timeout: 300
      Role: !GetAtt LambdaIamRole.Arn
      Environment:
        Variables:
          VERIFICATION_TOKEN: !Ref VerificationToken
      Events:
        Post:
          Type: Api
          Properties:
            Path: /slack
            Method: post

Lambdaを定期的に動かすタイプの設定と違うところは、EventsのところをAPI Gatewayの設定に変更するだけです。

      Events:
        Post:
          Type: Api
          Properties:
            Path: /slack
            Method: post

それだけで、API Gatewayが立ち上がり、pathに指定したエンドポイントにアクセスすることができます。

https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/ このようなエンドポイントが、/Stage(ステージング用)と/Prod(プロダクション用)それぞれ用意されます。

エンドポイントはデプロイ後、AWSコンソールのAPI Gatewayの画面で確認することができます。

デプロイする

こちらのデプロイスクリプトに関しても、ボタン付きメッセージをポストする部分が含まれています。 説明はボタン付きメッセージの時とほぼ一緒なので割愛しますが、2つのデプロイパッケージをつくって同時にデプロイすることが可能です。

#!/usr/bin/env bash
cd ./slack_bot
GOARCH=amd64 GOOS=linux go build -o ../build/philosophy-slack-bot
cd ../

cd ./slack_interactive_api
GOARCH=amd64 GOOS=linux go build -o ../build/philosophy-slack-bot-interactive-api
cd ../

zip build/philosophy-slack-bot.zip build/philosophy-slack-bot
zip build/philosophy-slack-bot-interactive-api.zip build/philosophy-slack-bot-interactive-api

aws cloudformation package --profile yourprofile \
    --template-file template.yml \
    --s3-bucket serverless \
    --s3-prefix philosophy-slack-bot \
    --output-template-file .template.yml
aws cloudformation deploy --profile youprofile \
    --template-file .template.yml \
    --stack-name philosophy-slack-bot \
    --capabilities CAPABILITY_IAM

想定する階層構造

.
├── build
│   ├── philosophy-slack-bot
│   ├── philosophy-slack-bot-interactive-api
│   ├── philosophy-slack-bot-interactive-api.zip
│   └── philosophy-slack-bot.zip
├── deploy.sh
├── slack_bot
│   └── main.go
├── slack_interactive_api
│   └── main.go
└── template.yml

./deploy.shをしていただければAPI Gateway/Lambdaに先ほど書いたコードがデプロイされます。

これで、晴れて、インタラクティブなslackBotが完成しました!!

f:id:joe0000:20181217232046p:plain

まとめ

初めてのことが多かったので色々な記事を参考にさせていただきました。 こちらの記事も、少しでもslackBotの運用をするきっかけとなれば幸いです。

今後の展望ですが、ランキング機能をつけるなどの拡張を考えるとRDBの方が使い勝手がいいので、近々Aurora Serverlessに載せ替えたいと思っています!

明日はデザインiOSエンジニアのJohnが「デザインについてエンジニアなりに意識していること」というタイトルで投稿します!お楽しみに!