Slackに絵文字が追加されたときに自動通知するBotをAWSで復旧させた話
こんにちは、ハンズラボ シニアテックリードのfjwrです。今日はSlackの話をします。
私がハンズラボに入社した当時、Slackの #random チャンネルには多種多様のおもしろbotが動いていました。その中の一つが EmojiBot です。
このbotはGAS(Google Apps Script)で作成されており、その日に新規に登録された絵文字を自動的に検知して通知してくれるというものでした。Slackでのコミュニケーションの活性化に一役買っていて大好きなbotでした。
しかし、SlackのAPIの仕様が変更されたためか、またはGASの管理者の退職の影響か、ある日動かなくなってしまいました。
とてもとても寂しかったので、AWS+Pythonで書き直しました。名付けて「帰ってきたEmojibot」です。
事前準備(Slack)
SlackのApp管理画面を開き、Appを新規作成してOAuth token を入手してください。必要な権限は以下の通りです。
- chat:write
- chat:write.public
- emoji:read
OAuth token の詳細な入手手順は本記事では説明しませんので、各自調べて設定してください。
トークンだけでなく、アイコンや表示名も必要に応じてここで設定します。
コード(Python)
Botの本体となるPythonコードは以下の通りです。このコードをAWS Lambdaにデプロイします。
import os
from datetime import datetime, timezone
import boto3
from slack_sdk.web import WebClient
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('SlackEmojiList')
slack = WebClient(token=os.environ["SLACK_API_TOKEN"])
def getEmojiList():
# emoji一覧の入手
response = slack.emoji_list()
if not response['ok']:
print(f'emoji_list failed: {response}')
return
# DynamoDBに登録済みかチェック
for emoji, url in response['emoji'].items():
result = table.get_item(
Key={
'emoji': emoji
}
)
# 新規絵文字の場合はSlackへ投稿
if 'Item' not in result:
postSlack(emoji, url)
return
def postSlack(emoji, url):
# エイリアスの追加は通知しない
if 'alias' not in url:
text = "Here comes new emoji!!\n" + "`:" + emoji + ":` " + url
response = slack.chat_postMessage(text=text, channel="#random")
if not response['ok']:
print(f'{emoji} chat_postMessage failed: {response}')
return
# DynamoDBに保存
now = datetime.now(timezone.utc)
table.put_item(
Item={
'emoji': emoji,
'url': url,
'create_at': now.isoformat()
}
)
return
def handler(event, context):
getEmojiList()
return
if __name__ == "__main__":
getEmojiList()
解説
- SlackSDKを利用しての絵文字一覧APIを実行して絵文字一覧を入手
- DynamoDBに保存してある前日分との差分との比較
- 新規絵文字の場合はSlackへ投稿
- 投稿したらDynamoDBに保存
という、非常にシンプルなコードです。絵文字一覧APIには画像URLが含まれていますので、IDと合わせてSlackに投稿しています。絵文字の画像URLはパブリック(!)のため、URLを投稿すればSlack上でプレビューされます。
コード(AWS)
AWSにデプロイを行うためのテンプレートをserverless.yml形式で記述します。
service: emojibot
frameworkVersion: ">=3.0.0 <4.0.0"
plugins:
- serverless-python-requirements
custom:
defaultStage: common
pythonRequirements:
dockerizePip: true
layer: true
slackAPIToken: ${ssm:/emojibot/slackAPIToken}
provider:
name: aws
runtime: python3.8
region: ap-northeast-1
stage: ${opt:stage, self:custom.defaultStage}
environment:
TZ: Asia/Tokyo
SLACK_API_TOKEN: ${self:custom.slackAPIToken}
logRetentionInDays: 7
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:DescribeTable
- dynamodb:Scan
Resource:
- !GetAtt EmojiListDynamoDBTable.Arn
package:
patterns:
- '!**'
- 'emojibot.py'
functions:
EmojiBot:
handler: emojibot.handler
layers:
- !Ref PythonRequirementsLambdaLayer
timeout: 900
events:
# 毎日18:00に起動
- schedule: cron(0 9 * * ? *)
resources:
Resources:
# -----
# DynamoDB作成
# -----
EmojiListDynamoDBTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: SlackEmojiList
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: emoji
AttributeType: S
KeySchema:
- AttributeName: emoji
KeyType: HASH
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
解説
毎度おなじみのServerless Frameworkです。serverless-python-requirementsを利用してPythonコードをLambdaとしてデプロイするほか、DynamoDBも新規作成しています。slackAPITokenにセットする OAuth token は秘密情報にあたるため、ssmパラメタストアに格納してデプロイ時に参照させています。
※ Serverless Framework の詳細な使い方はこの記事では説明しません
上記コードをデプロイ後、リリース当日までに登録済みの絵文字データをDynamoDBに初回データ移行すれば準備は完了です。DynamoDBの項目は以下の通りです。
- emoji:Slack上の絵文字のユニークIDをDynamoDBのハッシュキーとして保持
- url:絵文字画像のURL
- create_at:追加日時 特に利用しないが念の為保持
初回データ移行ではSlackAPIをコールして取得した絵文字一覧をCSVに加工してインポートしました。
動作確認
Slackに新規絵文字を登録後、18時まで待つと……
やったね。
おわりに
絵文字の追加が通知されるようになると、絵文字の追加を利用者の皆に気づいてもらえるようになりコミュニケーションが活性化します。
ネタ絵文字をこっそり追加して、通知されるのを黙って待つなどの奥ゆかしい使い方もできます笑。
また、個人の権限に依存しがちなGASからAWSでの管理に切り替えたこともあり、複数人でのメンテナンスが容易になったこともメリットの一つです。
今後も止まることなくずっと動き続けて欲しいものです。弊社Slackでは他にも愉快なbotが稼働中ですので、また別の機会にご紹介させていただきたいと思います。
※本記事はハンズラボアドベントカレンダー2022に執筆した記事を一部更新して転記したものです。