Help us understand the problem. What is going on with this article?

SlackのSlash commandの処理をAWS Lambdaで実装

More than 3 years have passed since last update.

シナリオ

  • Slack → API Gateway → Lambda

Slack 側では Custom Integrations の Slash Commands を使用します。
AWS 側は blueprint にある slack-echo-command-python を使ってコマンドを実行する Lambda 関数を作成します。
Lambda 関数の処理では 3 秒以内にSlack 側へ応答を返す必要があります。
時間がかかる処理を行うために、Lambda で受信したコマンドは、SNS を経由して別の Lambda 関数をイベント起動します。

  • Lambda → SNS → Lambda → Slack

SNS イベントから起動される Lambda 関数では STS を利用して、Slash Command を発行したユーザのロールを引き受けてAPI コマンドを実行します。

lambda-bot.png

  • 利用したblueprint
    • slack-echo-command-python Slack 側は Custom Integrations の Slash Commands を使用します。
    • cloudwatch-alarm-to-slack-python
      Incoming Webhooks でなく response_url を使って応答を返すように修正しました。

Slack → API Gateway → Lambda

Lambda 関数の作成

blueprint として slack-echo-command-python を選択して、Lambda 関数の登録を行います。

step1.png

step2.png

ロールを作成します。内容はひとまずデフォルトのままでかまいません。

lambda-role1.png

lambda-role2.png

lambda-role3.png

エンドポイント設定では Method を POST にセキュリティ設定は Open で作成します。

step3.png

最初は関数の本体はbuleprint のままでかまいません。

Slash Command App の登録

blueprint の先頭にコメントで設定手順が書かれていますので、これにそって進めます。

This function handles a Slack slash command and echoes the details back to the user.

Follow these steps to configure the slash command in Slack:  

  1. Navigate to https://.slack.com/services/new

  2. Search for and select "Slash Commands".

    slash2.png

  3. Enter a name for your command and click "Add Slash Command Integration".

    shash3.png

  4. Copy the token string from the integration settings and use it in the next section.

    tokenの文字列はこのあとの設定で使いますので控えておいてください。

    shalsh4.png

  5. After you complete this blueprint, enter the provided API endpoint URL in the URL field.

    slash5-1.png

    shash5-2.png

KMSの設定

Follow these steps to encrypt your Slack token for use in this function:

  1. Create a KMS key - http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html.

    IAMのマネジメントコンソールから暗号化キーの設定を行います。

    kms1-1.png

    kms1-2.png

    kms1-3.png

  2. Encrypt the token using the AWS CLI.

    $ aws kms encrypt --key-id alias/ --plaintext ""

    $ aws kms encrypt --key-id alias/slack-token --plaintext="XXXXXXXXXXXXXXXXXXXXXXXX"
    
    {    "KeyId": "arn:aws:kms:ap-northeast-1:XXXXXXXXXXXX:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
         "CiphertextBlob": "BASE64-encoded-string"}
    
  3. Copy the base-64 encoded, encrypted key (CiphertextBlob) to the kmsEncyptedToken variable.

    出力された CiphertextBlob の値を Lambda 関数の ENCRYPTED_EXPECTED_TOKEN 変数の値として設定します。

    ENCRYPTED_EXPECTED_TOKEN = "BASE64-encoded-string" # Enter the base-64 encoded, encrypted Slack command token (CiphertextBlob)
    
  4. Give your function's role permission for the kms:Decrypt action.

    Example:

       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Action": [
               "kms:Decrypt"
             ],
             "Resource": [
               "<your KMS key ARN>"
             ]
           }
         ]
       }
    

    この例を参考に先ほど作成したポリシーを修正します。

    lambda_basic_execution

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "arn:aws:logs:*:*:*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "kms:Decrypt"
                ],
                "Resource": [
                    "arn:aws:kms:us-east-1:XXXXXXXXXXXX:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
                ]
            }
        ]
    }
    

エンドポイントの設定

Follow these steps to complete the configuration of your command API endpoint

  1. When completing the blueprint configuration select "POST" for method and

    "Open" for security on the Endpoint Configuration page.

    すでに、この内容で作成しているはずです。設定が合っていないときは変更してください。

  2. After completing the function creation, open the newly created API in the

    API Gateway console.

  3. Add a mapping template for the application/x-www-form-urlencoded content type with the following body: { "body": \$input.json("\$") }

    API Gateway の Resource で、Integration Request の Body Mapping Templates を追加します。

    api-gw-1.png

    api-gw-2.png

  4. Deploy the API to the prod stage.

    修正後、Deply API で反映しておきます。

  5. Update the URL for your Slack slash command with the invocation URL for the created API resource in the prod stage.  

    エンドポイントの設定ができましたので、Slack 側の Slash Commands の Integration Settings でエンドポイントのURLを設定しておきます。

    api-gw-3.png

最初のコード

この時点で、Lambda 関数は ENCRYPTED_EXPECTED_TOKEN の値を設定しただけで、blueprint のままになっているはずです。

lambda-botコード

import boto3
from base64 import b64decode
from urlparse import parse_qs
import logging

ENCRYPTED_EXPECTED_TOKEN = "<KMS出力のCiphertextBlobのRVALUEであるBASE64文字列">" # Enter the base-64 encoded, encrypted Slack command token (CiphertextBlob)

kms = boto3.client('kms')
expected_token = kms.decrypt(CiphertextBlob = b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    req_body = event['body']
    params = parse_qs(req_body)
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match exptected", token)
        raise Exception("Invalid request token")

    user = params['user_name'][0]
    command = params['command'][0]
    channel = params['channel_name'][0]
    command_text = params['text'][0]

    return "%s invoked %s in %s with the following text: %s" % (user, command, channel, command_text)

テスト

マネジメントコンソールからテストするときのテストイベントは次のようになります。

tokenの値は Slash Commands の設定で控えた値を指定してください。

{
"body":
"token=XXXXXXXXXXXXXXXXXXXXXXXX&command=/lambda&text=ec2&user_name=Steve&channel_name=test&response_url=https://hooks.slack.com/commands/1234/5678"
}

ここまでエラーなく動くようになったら、Slack からテストしてみます。

test1.png

1回目はタイムアウトでエラーになるかもしれません。そのときはもう一度コマンドを発行してみてください。

応答が返ってくれば成功です。

test2.png

blueprint のサンプルでは return の引数は文字列となっていますが、これでは応答の前後に二重引用符がついて表示されます。Slash Commands の解説にあるように、JSON 形式で返せば二重引用符はつきません。

return { "text": "%s invoked %s in %s with the following text: %s" % (user, command, channel, command_text) }

あとは自由にコードを足して、好みの振る舞いをするボットを作成しましょう。

Lambda → SNS → Lambda → Slack

blueprint にある cloudwatch-alarm-to-slack-python を利用して notify-to-lambda を作成します。

Incoming Webhooks は使用しないため、blueprint の先頭に書かれている手順は実行しなくてもかまいません。

Slash Commands の説明で response_url を利用した応答の返しかたが記載されています。

この内容をもとに Incoming Webhooks でなく response_url を使って応答を返すように修正します。

Slash Commands | Slackより引用

Delayed responses and multiple responses
If you want to provide additional command response messages, or if you're unable to immediately respond to a command within 3000 milliseconds, use the specific response_url we send with our initial execution of your URL to respond to a command at your leisure. With this approach, you can respond to a user commands up to 5 times within 30 minutes of the user's invocation.
Sending HTTP requests to this response_url is easy. Just build a JSON POST body in the same format as used when responding to our command invocation request to your registered URL. It supports all of the same fields (response_type, text, and attachments). Then, send that data as an HTTP POST with a content-type of application/json to the destination specified as the response_url.
The only user-facing difference between immediate responses and delayed responses is that "in channel" delayed responses will not include the initial command sent by the user. To echo the command back to the channel, you'll still need to provide a response to Slack's original visit to your invocation URL.

SNS Topic の作成

SNS の受信イベントで Lamba 関数を起動するために、SNS Topic を作成し、Subscription を Protocol lambda で作成します。

pic16.png

Lambda関数の作成

pic17.png

pic18.png

pic19.png

pic20.png

blueprintのコードの修正

lambda-bot 側では、パラメータを JSON 形式で SNS メッセージとして送ります。

Publish - Amazon Simple Notification Service より引用 (太字部分は筆者による強調)

MessageStructure
Set MessageStructure to json if you want to send a different message for each protocol. For example, using one publish action, you can send a short message to your SMS subscribers and a longer message to your email subscribers. If you setMessageStructure to json, the value of the Message parameter must:
be a syntactically valid JSON object; and
contain at least a top-level JSON key of "default" with a value that is a string.
You can define other top-level keys that define the message you want to send to a specific transport protocol (e.g., "http").
For information about sending different messages for each protocol using the AWS Management Console, go to Create Different Messages for Each Protocol in the*Amazon Simple Notification Service Getting Started Guide*.
Valid value: json
Type: String
Required: No

このために関数の本体を次のように変更します。

lambda-botコード

def lambda_handler(event, context):
    req_body = event['body']
    params = parse_qs(req_body)
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match exptected", token)
        raise Exception("Invalid request token")

    user = params['user_name'][0]
    command = params['command'][0]
    channel = params['channel_name'][0]
    if params.has_key('text'):
        command_text = params['text'][0]
    else:
        command_text = ''
    response_url = params['response_url'][0]
    arg = command_text.split(' ')

    sns = boto3.client('sns')
    topic_arn = sns.create_topic(Name='sns-lambda')['TopicArn']
    message={"user_name": user, "command": command, "channel": channel, "command_text": command_text, "response_url": response_url}
    message=json.dumps(message)
    message=json.dumps({'default': message, 'lambda': message})
    response = sns.publish(
        TopicArn=topic_arn,
        Subject='/lambda',
        MessageStructure='json',
        Message=message
    )
    return { "text": "%s %s\nroger" % (command, command_text) }

受信側は blueprint のコードを次のように修正します。
HOOK_URL のかわりに response_url を使用し、JSON 形式で応答を返すようにしています。

notify-to-slackコード

def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = event['Records'][0]['Sns']['Message']
    try:
        message = json.loads(message)
        user_name = message['user_name']
        command = message['command']
        command_text = message['command_text']
        response_url = message['response_url']
        arg = command_text.split(' ')
        # if response_type is not specified, act as the same as ephemeral
        # ephemeral, response message will be visible only to the user
        slack_message = {
            'channel': '@%s' % user_name,
            #'response_type': 'in_channel',
            'response_type': 'ephemeral',
            'isDelayedResponse': 'true',
            'text': "response for: %s %s" % (command, command_text)
        }
        logger.info("Send message to %s %s", response_url, slack_message)
        req = Request(response_url)
        req.add_header('Content-Type', 'application/json')
        response = urlopen(req, json.dumps(slack_message))
        response.read()
        logger.info("Message posted to %s", slack_message['channel'])
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)

Lambda関数のロールの修正

SNS トピックへの送信を許可します。

lambda_basic_execution

        {
            "Effect": "Allow",
            "Action": [
                "sns:*"
            ],
            "Resource": [
                "arn:aws:sns:us-east-1:XXXXXXXXXXXX:sns-lambda"
            ]
        },

テスト

マネジメントコンソールからテストするときのテストイベントは次のようになります。

response_url の値は、Slash Commands で送信されてきたものをログに残すなどして入手してください。

{
  "Records": [
    {
      "Sns": {
        "Message": "{\"command\": \"/lambda\", \"command_text\": \"ec2 console i-XXXXXXXX\", \"user_name\": \"Steve\", \"channel_name\": \"test\", \"response_url\": \"https://hooks.slack.com/commands/1234/5678\"}"
      }
    }
  ]
}

テストで問題がなければ、Lamba 関数で Event source にSNSトピックを指定し State を Enable にしておきます。

Slack からテストしてみます。

pic14.png

このような応答が得られれば大丈夫です。

pic29.png

もう少し実用的なコード

EC2インスタンスの一覧やコンソールの出力の取得ができるようにします。

またS3バケットの一覧も取得してみます。

notify-to-slack 側 slack_message = { … } の後につぎのコードを追加します。

        if arg[0] == 'ec2':
            if len(arg) == 3:
                if  arg[1] == 'console':
                    ec2 = boto3.resource('ec2')
                    instance = ec2.Instance(arg[2])
                    response = instance.console_output()
                    output = '\n'.join([x.rstrip() for x in response['Output'].split('\n')[-20:]])
                    slack_message['text'] = output
            else:
                ec2 = boto3.client('ec2')
                response = ec2.describe_instances()
                if response.has_key('Reservations') and len(response['Reservations']) >= 1:
                    status = [(lambda x: (x[u'InstanceId'], ', '.join([t[u'Value'] for t in x[u'Tags']]), x[u'State'][u'Name']))(i) for i in response[u'Reservations'][0][u'Instances']]
                    slack_message['text'] = '%s %s\n%s' % (command, command_text, '\n'.join(['%s: (%s) %s' % x for x in status]))
                else:
                    slack_message['text'] = "%s %s\nno instance found" % (command, command_text)
        elif arg[0] == 's3':
            s3 = boto3.client('s3')
            buckets = s3.list_buckets()
            slack_message['text'] = "%s %s\n%s" % (command, command_text, ' '.join([x[u'Name'] for x in buckets[u'Buckets']]))

Lambda関数のロールの修正

EC2 インスタンス、S3 バケットの情報を参照できるようにします。

lambda_basic_execution

        {
            "Effect": "Allow",
            "Action": [
                "ec2:Describe*",
                "ec2:Get*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:Get*"
            ],
            "Resource": [
                "*"
            ]
        },

pic30.png

pic31.png

pic32.png

pic33.png

pic34.png

pic35.png

STSの利用

ボットを呼び出すユーザに応じて、コマンドを実行できる権限を変えることを考えます。

クロスアカウントアクセスのロール virginia-ec2-delegate を作成して、信頼関係で Lambda サービスを追加しました。

STS の設定についての理解が十分でないため、これで必要十分性なのかよくわかりませんが、動いているのでおそらく大丈夫だと思います。

ユーザ名をそのまま利用しているため、AWS の IAM ユーザ名と Slack のユーザ名は合わせておく必要があります。

ロールvirginia-ec2-delegate

pic25.png

pic26.png

      "Principal": {
        "Service": "lambda.amazonaws.com"
      },

ポリシーec2-full-access

pic28.png

ロールassume-role

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "sts:AssumeRole"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Lambda関数のロールの修正

lambda_basic_execution

pic27.png

IAMの必要な参照ができるようにロールのポリシーを修正します。

        {
            "Effect": "Allow",
            "Action": [
                "iam:GetUser",
                "iam:GetRole",
                "iam:UserPolicy"
            ],
            "Resource": [
                "*"
            ]
        }

notify-to-slackコード

    iam = boto3.client('iam')
    response = iam.get_user(UserName=user_name)
    if response.has_key('User'):
        uid = response['User']['Arn'].split(':')[4]
        if uid and boto3.resource('iam').UserPolicy(user_name, 'ec2-full-access'):
            response = iam.get_role(RoleName='virginia-ec2-delegate')
            if response.has_key('Role'):
                sts = boto3.client('sts')
                assumedRoleObject = sts.assume_role(RoleArn="arn:aws:iam::%s:role/%s" % (uid, 'virginia-ec2-delegate'), RoleSessionName='session')
                credentials = assumedRoleObject['Credentials']
                ec2 = boto3.resource(
                    'ec2',
                    aws_access_key_id=credentials['AccessKeyId'],                                    aws_secret_access_key=credentials['SecretAccessKey'],
                    aws_session_token = credentials['SessionToken'],
                    )
                instance = ec2.Instance(arg[2])
                state = 'unknown'
                if arg[1] == 'start':
                    response = instance.start()
                    if response.has_key('StartingInstances'):
                        state = response['StartingInstances'][0]['CurrentState']['Name']
                        slack_message['text'] = '%s %s' % (arg[2], state)
                elif arg[1] == 'stop':
                    response = instance.stop()
                    if response.has_key('StoppingInstances'):
                        state = response['StoppingInstances'][0]['CurrentState']['Name']
                slack_message['text'] = '%s %s' % (arg[2], state)

テスト

Lambda 関数には EC2 インスタンスの起動/停止を行う権限は持たせていませんが、STS を利用してユーザ権限を引き受けて実行します。

pic36.png

pic37.png

pic38.png

pic39.png

サンプルコード

lambda-bot

import boto3
from base64 import b64decode
from urlparse import parse_qs
from datetime import datetime, timedelta
import json
import logging

ENCRYPTED_EXPECTED_TOKEN = "<KMS出力のCiphertextBlobのRVALUEであるBASE64文字列">" # Enter the base-64 encoded, encrypted Slack command token (CiphertextBlob)

kms = boto3.client('kms')
expected_token = kms.decrypt(CiphertextBlob = b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    req_body = event['body']
    params = parse_qs(req_body)
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match exptected", token)
        raise Exception("Invalid request token")

    user = params['user_name'][0]
    command = params['command'][0]
    channel = params['channel_name'][0]
    if params.has_key('text'):
        command_text = params['text'][0]
    else:
        command_text = ''
    response_url = params['response_url'][0]
    arg = command_text.split(' ')

    if arg[0] in ['ec2', 's3']:
        sns = boto3.client('sns')
        topic_arn = sns.create_topic(Name='sns-lambda')['TopicArn']
        message={"user_name": user, "command": command, "channel": channel, "command_text": command_text, "response_url": response_url}
        message=json.dumps(message)
        message=json.dumps({'default': message, 'lambda': message})
        response = sns.publish(
            TopicArn=topic_arn,
            Subject='/lambda',
            MessageStructure='json',
            Message=message
        )
        return { "text": "%s %s\nroger" % (command, command_text) }
    elif arg[0] == 'help':
        return { "text": "ec2 [console|start|stop instance-id]\ns3 [usage bucket]" }
    else:
        return { "text": "%s invoked %s in %s with the following text: %s" % (user, command, channel, command_text) }

notify-to-slack

from __future__ import print_function

import boto3
import json
import logging

from base64 import b64decode
from urllib2 import Request, urlopen, URLError, HTTPError
from time import sleep

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def send_response(response_url, message):
    req = Request(response_url)
    req.add_header('Content-Type', 'application/json')
    try:
        response = urlopen(req, json.dumps(message))
        response.read()
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)

def ec2_command(arg, user_name, response_url):
    if len(arg) == 3:
        if  arg[1] == 'console':
            ec2 = boto3.resource('ec2')
            instance = ec2.Instance(arg[2])
            response = instance.console_output()
            output = '\n'.join([x.rstrip() for x in response['Output'].split('\n')[-20:]])
            return output
        elif arg[1] in ['start', 'stop']:
            iam = boto3.client('iam')
            response = iam.get_user(UserName=user_name)
            if response.has_key('User'):
                uid = response['User']['Arn'].split(':')[4]
                if uid and boto3.resource('iam').UserPolicy(user_name, 'ec2-full-access'):
                    response = iam.get_role(RoleName='virginia-ec2-delegate')
                    if response.has_key('Role'):
                        sts = boto3.client('sts')
                        assumedRoleObject = sts.assume_role(RoleArn="arn:aws:iam::%s:role/%s" % (uid, 'virginia-ec2-delegate'), RoleSessionName='session')
                        credentials = assumedRoleObject['Credentials']
                        ec2 = boto3.resource(
                                'ec2',
                                aws_access_key_id=credentials['AccessKeyId'],
                                aws_secret_access_key=credentials['SecretAccessKey'],
                                aws_session_token = credentials['SessionToken'],
                                )
                        instance = ec2.Instance(arg[2])
                        state = instance.state['Name']
                        if arg[1] == 'start':
                            if state == 'stopped':
                                response = instance.start()
                                if response.has_key('StartingInstances'):
                                    state = response['StartingInstances'][0]['CurrentState']['Name']
                                    if state == 'pending':
                                        if response_url:
                                            send_response(response_url, { 'text': 'start %s' % arg[2] })
                                        ec2client = boto3.client('ec2')
                                        for wait in range(0,20):
                                            response = ec2client.describe_instances(InstanceIds=[arg[2]])
                                            state = response[u'Reservations'][0][u'Instances'][0]['State']['Name']
                                            if state == 'running': break
                                            sleep(3)
                                return '%s become %s' % (arg[2], state)
                            else:
                                return '%s is already %s' % (arg[2], state)
                        elif arg[1] == 'stop':
                            if state == 'running':
                                response = instance.stop()
                                if response.has_key('StoppingInstances'):
                                    state = response['StoppingInstances'][0]['CurrentState']['Name']
                                    if state == 'stopping':
                                        if response_url:
                                            send_response(response_url, { 'text': 'stop %s' % arg[2] })
                                        ec2client = boto3.client('ec2')
                                        for wait in range(0,20):
                                            response = ec2client.describe_instances(InstanceIds=[arg[2]])
                                            state = response[u'Reservations'][0][u'Instances'][0]['State']['Name']
                                            if state == 'stopped': break
                                            sleep(3)
                                    return '%s become %s' % (arg[2], state)
                            else:
                                return '%s is already %s' % (arg[2], state)
                        else:
                            return '%s: unknown' % arg[2]
            else:
                return '%s: user unknown' % user_name
    else:
        ec2 = boto3.client('ec2')
        response = ec2.describe_instances()
        if response.has_key('Reservations') and len(response['Reservations']) >= 1:
            status = [(lambda x: (x[u'InstanceId'], ', '.join([t[u'Value'] for t in x[u'Tags']]), x[u'State'][u'Name']))(i) for i in response[u'Reservations'][0][u'Instances']]
            return '\n'.join(['%s: (%s) %s' % x for x in status])
        else:
            return "no instance found"

def s3_command(arg, user_name, response_url):
    if len(arg) == 3 and arg[1] == 'usage':
        cloudwatch = boto3.client('cloudwatch')
        now = datetime.utcnow()
        response = cloudwatch.get_metric_statistics(
                    Namespace='AWS/S3',
                    MetricName='BucketSizeBytes',
                    Dimensions=[
                        { 'Name': 'BucketName', 'Value': arg[2] },
                        { 'Name': 'StorageType', 'Value': 'StandardStorage' }
                    ],
                    StartTime=now - timedelta(days=1),
                    EndTime=now,
                    Period=86400,
                    Statistics=['Average'],
                    Unit='Bytes'
                    )
        if response.has_key('Datapoints') and len(response[u'Datapoints']) >= 1:
            return response[u'Datapoints'][0][u'Average']
        else:
            return "no metric found"
    else:
        s3 = boto3.client('s3')
        buckets = s3.list_buckets()
        return ' '.join([x[u'Name'] for x in buckets[u'Buckets']])

def lambda_handler(event, context):
    message = event['Records'][0]['Sns']['Message']
    logger.info("Event: " + str(message))
    if True:
        message = json.loads(message)
        user_name = message['user_name']
        command = message['command']
        command_text = message['command_text']
        response_url = message['response_url']
        arg = command_text.split(' ')

        # if response_type is not specified , act as the same as ephemeral
        # ephemeral, response message will be visible only to the user
        slack_message = {
            'channel': '@%s' % user_name,
            #'response_type': 'in_channel',
            'response_type': 'ephemeral',
            'isDelayedResponse': 'true',
            'text': "%s %s" % (command, command_text)
        }

        if arg[0] == 'ec2':
            slack_message['text'] = ec2_command(arg, user_name, response_url)
            send_response(response_url, slack_message)
        elif arg[0] == 's3':
            slack_message['text'] = s3_command(arg, user_name, response_url)
            send_response(response_url, slack_message)

    else:
        slack_message = { 'text': "command failed" }
        send_response(response_url, slack_message)
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away