Slackでスッキリすの結果を見る需要があったので、Slashコマンドを初めて使ってみました。
またAWSも触ってみたかったので、AWS Educateに登録してLambdaに処理させるようにしました。
今回はSlash commandをLambdaランタイム上のPythonで実行する方法を記録します。
はじめに
今回作ったコードはGitHubに保存しているので、併せて参照してください。
ちなみにスッキリすのスクレイピングの話はしません。
構成
Slackでslash commandを実行、API Gatewayを通じてLambdaに処理が渡され、スッキリすのスクレイピングをし、その結果を返す構成です。
準備
Slack slash commandの入出力
Slash commandを打った時にPOSTされるパラメータは以下のドキュメントに掲載されています。
https://api.slack.com/slash-commands#app_command_handling
ちなみに掲載時点ではパラメータ例は以下のようになっています。
token=gIkuvaNzQIHg97ATvDxqgjtO
&team_id=T0001
&team_domain=example
&enterprise_id=E0001
&enterprise_name=Globular%20Construct%20Inc
&channel_id=C2147483705
&channel_name=test
&user_id=U2147483697
&user_name=Steve
&command=/weather
&text=94070
&response_url=https://hooks.slack.com/commands/1234/5678
&trigger_id=13345224609.738474920.8088930838d88f008e0
これらがContent-Type: application/x-www-form-urlencoded
で送られます。
出力はtextあるいはjsonで構成します。
特にこだわりがなければtextだけでいいですが、jsonでリッチなメッセージを送ることもできます。
以下のドキュメントに簡単な説明が書いてあります。
https://api.slack.com/slash-commands#responding_immediate_response
通常slash commandの応答はcommandを打った人にしか表示されないため、全員に見えるように応答する場合は"response_type": "in_channel"
というkey-valueをつけたjsonにする必要があります。
AWS API Gateway
AWSのAPI GatewayとLambdaの結合部分は統合プロキシと呼ばれています。
統合プロキシの入力(API Gateway → Lambda)と出力(API Gateway ← Lambda)は以下のドキュメントに掲載されています。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-create-api-as-simple-proxy
掲載時点では以下のようになっています。
(2019/11/10追記:出力フォーマットが入力フォーマットと同一になっていた誤りを修正。)
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {String containing incoming request headers}
"multiValueHeaders": {List of strings containing incoming request headers}
"queryStringParameters": {query string parameters }
"multiValueQueryStringParameters": {List of query string parameters}
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}
ちなみにこの出力は、Pythonのdictionary型でもよいことを確認しました。
コード作成
今回はPythonを用います。
理由はよく使用しているスクレイピングライブラリのBeautifulSoupが使えたからです。
Lambdaから呼び出された際に実行する関数はハンドラと呼ばれ、2引数関数で実装します。
(特にファイル名や関数名は問わない、自由に設定できる。)
def lambda_handler(event, context):
前述のAPI Gatewayの入力はevent(第1引数)に渡されます。
第2引数のcontextはLambdaを実行している環境の情報が与えられます(今回は触れない)。
詳しいことはドキュメントを参照してください。
PythonのLambdaハンドラ:
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-programming-model-handler-types.html
第2引数のLambdaコンテキスト:
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-context-object.html
Slackのコマンドの引数はtextパラメータに保存されるので、以下のように抜き出します。
from urilib.parse import parse_qs
def lambda_handler(event, context):
input_text = parse_qs(event['body'])['text'][0].rstrip()
ここでrstrip()
を使っているのは、パラメータの末尾に改行が含まれているためです。
また、返り値をAPI Gateway、Slash Commandそれぞれのフォーマットに合わせる必要があるため、最終的に以下のような関数になります。
from urilib.parse import parse_qs
import json
def lambda_handler(event, context):
input_text = parse_qs(event['body'])['text'][0].rstrip()
...
return {
"isBase64Encoded": false,
"statusCode": 200,
"headers": {},
"body": json.dumps({
"response_type": "in_channel",
"text": ***OUTPUT***
})
}
あとは好きな処理を加えて煮るなり焼くなり好きにします。
コードをzip保存
LambdaのコードはWebコンソール上のエディタで作成することもできますが、zipアップロードすることもできます。
ここではpipでインストールしたモジュールも入れたいため、zipアップロードを使用します。
まずはzipを作る前に、ソースと同じディレクトリにモジュールを落とします。
pip install -r requirements.txt -t .
#あるいは
pip install <package> -t .
続いて、ソースとモジュールを同時にzipで固めます。
zip -r source.zip *
これでpipモジュールを使っていても、全く同じコードで走らせることができます。
デプロイ
AWS
Lambdaのコンソールを開き、「関数の作成」を選択。
編集画面に移ったら、「関数コード」ペインにスクロールし、以下のように設定して保存。
- コードエントリタイプ:.zipファイルをアップロード
- ランタイム:指定の言語
- ハンドラ:ファイル名.関数名(
sukkirisu.py
のlambda_handler()
ならsukkirisu.lambda_handler
)
続いて上の「Designer」ペインにスクロールし、「+トリガーを追加」を選びます。
APIは「新規APIの作成」を選びます。
セキュリティは「オープン」とします。本当は設定したいのですが、Slash Commandのリクエストがユーザ指定のリクエストに対応していないので、どうしてもセキュリティ設定をしたい場合はPOSTリクエスト内容を見るしか現状ないです。
これで出来上がるエンドポイントURLを控えておきます。これでAWS側の設定は完了です。
Slack
Slack APIのapps(https://api.slack.com/apps)でアプリを作成します。
作成したら、「Features」メニューの「Slash Commands」を選びます。
「Create New Command」を選び、新規コマンド作成画面へ。
ここでRequest URLに先ほど控えたAPI GatewayのURLを入れます。
これにて完成です。コマンドを叩いたら、レスポンスが返ってくるか確認します。
コメント