Bot(ボット)を作ってみたので、その作業メモです。Lambdaを使うとサーバー周りのことを気にしなくて済みます。Frameworkを使うと、面倒なAWSコンソールでの作業が不要になるので、割と手軽に作成できてオススメです。Botが全く初めての場合は、先にこちらを読んだ方が分かりやすいかと思います。www.kwbtblog.com本記事は主に、実装方法についてのメモになります。Botを作るには、いくつか満たさないといけない要件があるので、その要件と解決方法を中心に説明していきます。Botとはざっくり言うと、Slackのワークスペース上にいるBotユーザーに対して、メッセージ送信などの何らかのアクションを起こすと、Slackから事前に登録していたURLに、そのアクション内容を通知してくれる仕組みです。Botとは、BotがSlackでのイベント収集窓口となり、Slackで起こったイベントを、指定URLへWebhookする仕組みです。URLはBot開発者が用意する必要があるのですが、ドメイン取得やWebサーバーの手配等は結構面倒です。Lambdaだと、URLはAmazonが発行してくれるし、サーバーの用意も不要です。Botを作ることにしました。URL確認を実装するSlackにURLを登録する際、そのURLが本当にそのBot用のURLなのか調べるため、SlackがURLに確認メッセージを送ってきます。そのメッセージに対して正しいレスポンスが返せて初めて、そのURLがSlackに登録されます。Slackが送ってくるメッセージは下記のようなものです。"url_verification"}サーバー側では、下記のような処理を行います。URLは外部にさらされることになるので、まず「token」がBotのものかをチェックして認証します。SlackからはURLに対して送ってくるのは、確認のメッセージだけでなく、Botからのイベントも送られてきます。URL確認メッセージは「type」が「url_verification」となるので、チェックします。それらが正しければ、「challenge」の値を応答します。実装は下記のようになります。}};注意:必ずコード200を返す途中処理がエラーになった場合でも、catch()で受けてthrowはせず、必ずコード200で成功応答するようにしています。SlackがURLにアクセスしてきて、コード200以外で応答した場合、Slackはアクセスに失敗したと認識し、リトライを行います。これはURL確認メッセージの場合のみでなく、イベント送信の場合も同様です。リトライが一定回数以上になると、それ以降はSlackからの送信が行われなくなります。URLにアクセスしてきた後の処理内容に関わらず、Slackには必ずコード200で返答するようにして、Slackからのイベント送信が停止しないようにします。イベント処理を実装する無事URL登録が済むと、BotからURLにイベントが送られるようになります。送られてきた情報がイベントかどうかは「type」が「event_callback」かを見て判断します。イベントには、どのアプリのイベントかを識別する「api_app_id」が付与されているので、「api_app_id」が正しいものかをチェックします。実装は下記のようになります。}};ここではreturnだけ返して、イベントに対して何もしていません。どんなメッセージが届こうとも、前述のとおりコード200の応答をするので、リトライは発生せず、Slackから同じイベントが2回以上届くことはないのです。3秒以内に応答するSlackはイベントを送って、3秒以内に応答がなかった場合、Slackはアクセスに失敗したと認識し、リトライを行います。前述のとおり、リトライが一定以上続くとイベント送信を停止してしまいます。3秒なんて、ちょっとした処理を書いただけでもすぐに超えてしまいます...。Slackからイベントが届いたら、まずSlackに返答だけしてしまって、本来のやりたかった処理は、返答した後でゆっくりやるようにする必要があります。Service(SQS)を使うことにしました。SQSとは、メッセージを一時的に保存してくれるサービスです。メッセージをSQSに保存して、後からそのメッセージを取りにいくことができます。SQSとLambdaは簡単に連携して使うことができます。SQSとLambdaの関数を連携させると、Lambdaは定期的にSQSにメッセージが届いていないかチェックし、メッセージが届いていたら、そのメッセージを引数に入れて、連携しているLambda関数を呼びだしてくれます。Slackからイベントが送られたら、イベント内容をSQSに保存して、まずSlackに応答して関数を終わります。その後、Lambdaは定期的にSQSをチェックしているので、先程保存したイベントを見つけて、Lambdaを呼び出だします。これにより、Slackには応答して終了しつつ、処理を次のLambdaに非同期的に渡すことができるようになります。実装は下記のようになります。}};イベント内容をSQSに送ってそのままコード200で終了しています。すると、SQSからLambda関数「handlerSqs()」が呼び出されるようになります。ここでは何もせず、returnを返して正常終了させています。SQSから呼び出されたLambda関数が正常終了した場合は、SQSからメッセージが削除されるので、同じメッセージが何度も届くことはありません。Botに最低限必要な要件は満たせました。後はSQSから呼び出されたLambda(handlerSqs())の中で、好きな処理を書くだけです。APIを使って行います。APIを呼び出すとそれっぽくなります。Botにメッセージを送って、Botからオウム返しさせるのは下記のようになります。}}せっかくなのでBotアプリっぽく、Googleの翻訳APIを使って、話しかけると英語に翻訳して返事するようにしてみました。}}以上、もろもろを踏まえた実装は下記のようになります。認証に必要な情報はLambdaの環境変数にセットして渡します。Gatewayを設定して、SQSを設定して、Lambdaにマッピングするだけです......。言いたいのですが、これがかなり面倒です......。Frameworkを使うことにしました。Serverlessの詳しい使い方は省略しますが、今回のLambdaをデプロイする方法は、ざっくりとは下記のとおりです。deployでデプロイ設定ファイルは下記になります。1Severlessの設定ファイルはシンプルなので、見れば何となくやっていることが分かると思うのですが、簡単に補足説明します。Lambdaの環境変数は「env:~」を使って、ローカルの環境変数の値がセットされるようにしています。SQSを使うので、Lambdaの実行ポリシーにSQSアクセス権を追加しています。SQSはServerlessは自動では作ってくれないので「resources」セクションで明示的に作成しています。CloudFormationのテンプレート書式がそのまま使えます。SQSはCloudFormationを介して作成されます。SQSのURLはSQSが作成された時に決まるので、プログラムには環境変数「AWS_SQS_URL」を通じて渡しています。SQSのCloudFormationの論理名「slackBotTestSqs」をRefで参照すると、SQSのURLを取得できます。https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.htmlSQSからメッセージを読み取る際、複数メッセージをまとめて読めるのですが、「batchSize」で読み取り数を1だけにしています。Google・GCPの箇所は翻訳API利用のために使用しているだけなので、削除して構いません。Botの雛形ができました。Gateway・SQSが絡み合うので、AWSのコンソールで同じことやろうとすると大変です。Frameworkを使うと、AWSのコンソールは一切触らずに、設定ファイルの記述だけで済んでしまうので本当に便利です。Frameworkが無かったら確実に挫折してました...。はじめ、エラー処理と応答時間は気にせず、適当に作っててハマりました...。問題が発生したらエラーコード返すようにしていると(例外をキャッチしないでほっとくとか)、「エラー発生->Slackリトライ->エラー発生...」のループとなり、最後に止まるということになります。イベントを受けた時に、横着してそこで全ての処理を書いてしまうと、時間内に処理が終わらず、「処理中にタイムオーバー->Slackリトライ->処理中にタイムオーバー...」のループとなり、何度も同じ処理(応答)を行って、最後に止まるということになります。あと、SQSに送れるメッセージサイズは最大256kbなので、本格運用するのであれば、投稿本文はSQSには送らず、SQSメッセージを処理するLambda内で取得し直すような仕組みにした方がいいですね。(1)スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。