この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Slackで特定のキーワードを含むメッセージに対して、自動でリアクションを付けたいことってありますよね。というわけで、実際にやってみました。
おすすめの方
- Slackで特定チャンネルのメッセージで動くSlackアプリ(Bot)を作成したい方
- Slackで自動でリアクションを付与したい方
- 上記の仕組みをサーバーレスで作成したい方
ざっくり構成
SlackのEvents APIで特定チャンネル(Slackアプリが追加されたチャンネル)のメッセージを受信し、reactions.addでリアクションを付与します。
自動リアクションBotを作成する(準備編)
Slackアプリ作成時にWebAPIを指定するため、最初にVerify用のAPIを作成します。
SAM Init
sam init \
--runtime python3.8 \
--name Slack-Team-IoT-Reaction-Bot \
--app-template hello-world \
--package-type Zip
SAMテンプレート
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Slack-Team-IoT-Reaction-Bot
Resources:
SlackChannelSubscribeFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/slack_channel_subscribe/
Handler: app.lambda_handler
Runtime: python3.8
Timeout: 10
Events:
Message:
Type: Api
Properties:
Path: /message
Method: post
SlackChannelSubscribeFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${SlackChannelSubscribeFunction}
Outputs:
SlackMessageApi:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/message/"
Lambdaコード
APIのURL検証が必要なので、Event APIのドキュメントに従って、受け取ったパラメータのchallengeを返しています。
app.py
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
logger.info(event['body'])
body = json.loads(event['body'])
return {
'statusCode': 200,
'body': json.dumps(
{'challenge': body['challenge']},
),
}
デプロイ
sam build --use-container
sam package \
--output-template-file packaged.yaml \
--s3-bucket cm-fujii.genki-deploy
sam deploy \
--template-file packaged.yaml \
--stack-name Slack-Team-IoT-Reaction-Bot-Stack \
--s3-bucket cm-fujii.genki-deploy \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset
APIのエンドポイントを取得
下記コマンドでAPIのエンドポイントを取得します。Slackアプリ作成時に利用するため、メモしておきます。
aws cloudformation describe-stacks \
--stack-name Slack-Team-IoT-Reaction-Bot-Stack \
--query 'Stacks[].Outputs'
Slackアプリの作成
新規作成
Slack Appにアクセスして、アプリを新規作成します。
Slackアプリの設定
Event Subscriptionsの設定
Basic Informationにある「Event Subscriptions」を選択します。
「Request URL」にさきほどデプロイしたURLを入力し、Verify OK
になることを確認します。
続けて、Event APIにメッセージのRead権限として、message.channels
を付与します。
忘れずにSaveします。
OAuth & Permissionsの設定
「Scopes」でreactions:write
を付与します。
Slackアプリをワークスペースにインストールする
「Install to Workspace」を選択してインストールします。
インストール後、Bot User OAuth Token
が表示されるので、メモしておきます。これは、リアクション付与時に使用します。
任意のチャンネルにSlackアプリを追加する
Slackで任意のチャンネルを選択し、さきほどインストールしたSlackアプリを追加します。
「Slackアプリを追加したチャンネルのメッセージ」がデプロイしたAPIに渡されます。
自動リアクションBotを作成する(リアクション編)
SSMパラメータストアにトークンを保存する
SecureStringとして保存しています。
aws ssm put-parameter \
--type 'SecureString' \
--name '/Slack/Toekn/Team-IoT-Reaction-Bot' \
--value 'xoxb-xxx-yyy-zzz'
SAMテンプレート
SSMパラメータストアのRead権限やSSMパラメータストアに保存したトークンのKey名を環境変に設定しています。
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Slack-Team-IoT-Reaction-Bot
Parameters:
SlackAppTokenKey:
Type: String
Resources:
SlackChannelSubscribeFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/slack_channel_subscribe/
Handler: app.lambda_handler
Runtime: python3.8
Policies:
- arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
Environment:
Variables:
SLACK_APP_TOKEN_KEY: !Ref SlackAppTokenKey
Timeout: 10
Events:
Message:
Type: Api
Properties:
Path: /message
Method: post
SlackChannelSubscribeFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${SlackChannelSubscribeFunction}
Outputs:
SlackMessageApi:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/message/"
Lambdaコード
Lambdaコードを下記に変更します。メッセージの内容を確認し、いいね
が含まれている場合に、:good:
のリアクションを付与します。
Verifying requests from Slack | Slack
Lambda上で動くPythonにてSlack Appからのリクエストを検証する
slackapi bolt-python: A framework to build Slack apps using Python
app.py
import json
import logging
import os
import boto3
import requests
logger = logging.getLogger()
logger.setLevel(logging.INFO)
SLACK_APP_TOKEN_KEY = os.environ['SLACK_APP_TOKEN_KEY']
ssm = boto3.client('ssm')
def lambda_handler(event: dict, context: dict):
logger.info(event['body'])
body = json.loads(event['body'])
if is_reaction_message(body) is False:
return
token = get_token()
reaction_slack(body, token, 'good')
return {
'statusCode': 200,
}
def is_reaction_message(body: dict) -> bool:
return 'いいね' in body['event']['text']
def get_token() -> str:
res = ssm.get_parameter(
Name=SLACK_APP_TOKEN_KEY,
WithDecryption=True
)
return res['Parameter']['Value']
def reaction_slack(body: dict, token: str, name: str) -> None:
channel = body['event']['channel']
timestamp = body['event']['event_ts']
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
url = 'https://slack.com/api/reactions.add'
data = {
'channel': channel,
'name': name,
'timestamp': timestamp
}
try:
response = requests.post(url, headers=headers, data=json.dumps(data))
except requests.exceptions.RequestException as e:
logger.error(e)
else:
logger.info(response.status_code)
logger.info(response.text)
デプロイ
--parameter-overrides
を追加して、SSMパラメータストアのKey名を渡しています。
sam build --use-container
sam package \
--output-template-file packaged.yaml \
--s3-bucket cm-fujii.genki-deploy
sam deploy \
--template-file packaged.yaml \
--stack-name Slack-Team-IoT-Reaction-Bot-Stack \
--s3-bucket cm-fujii.genki-deploy \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides SlackAppTokenKey=/Slack/Toekn/Team-IoT-Reaction-Bot \
--no-fail-on-empty-changeset
動作確認
Slackで「いいね」を含むメッセージを投稿すると、:good:
リアクションが付与されました!!
さいごに
この内容を応用すれば、NGワードゲーム(日替わり)が作れそうですね。