Cocoon-Engine

甘党インフラエンジニアの技術ブログ

Chocolat Chocolat Chocolat Chocolat

IT技術とスイーツに興味がある方、ちょっと見ていきませんか?

Slack API + Python + Linux でスタンプの有無によるリマインドメッセージ投稿を実現

Slack で特定のメッセージに対してスタンプで反応があったら~する、無ければ~する。

というように、リアクションの有無による処理を実現します。

仕様

Slack API + Python + Linux(cron)で定時作業を忘れていた場合にリマインドメッセージが飛ぶようにします。

具体的には、

・Slackbot で毎日9:00 に「ファイルアップロード!」というリマインドメッセージを飛ばす

・作業を忘れずに実施したときは上記のメッセージにスタンプをつける。(手動)

・リマインドメッセージを API によって取得し、メッセージの内容とスタンプの有無をチェックする

・スタンプがついていない(=作業を忘れている)場合、「ファイルのアップロードを忘れてますよ!」というメッセージを飛ばす

Slack アプリ作成

参考

https://zenn.dev/kou_pg_0131/articles/slack-api-post-message

1. ブラウザで Slack(https://slack.com/intl/ja-jp/) にログイン

2. Slack api の Your Apps ページ(https://api.slack.com/apps)にて「Create New App」を選択

3. 「From scratch」を選択し、App Name と対象ワークスペースを指定して「Create App」

4. 作成したアプリの詳細ページにて、「OAuth&Permissions」から「Scopes」を探す

5. Bot Token Scopes に以下の 3つの Scope を追加する。

    - channels:history → パブリックチャンネルの情報取得用
    - chat:write → アプリによるメッセージ投稿用

6. 同ページ内の上の方にある「OAuth Tokens for Your Workspace」から「Install to Workspace」でアプリをインストール

7. 「Bot User OAuth Token」にトークンが発行されているので控えておく

 

チャンネルにアプリを登録

1. Workspace 内の App に作成したアプリが追加されていることを確認する

  追加されない場合はブラウザで一度 Slack を開いてみると良いかも

2. 追加したいチャンネルを右クリックし、「チャンネル詳細」>「インテグレーション」タブから

「アプリを追加する」を選択

3. 作成したアプリを追加

Slack API + Python 処理

まず公式SDKをインストールします。

pip install slack_sdk

細かい説明は後回しで先にプログラムを載せます。

import datetime, time
from slack_sdk import WebClient

# Slack API
SLACK_TOKEN = '1-7で確認したトークン'

#チャンネルをブラウザで開いたときの https://app.slack.com/client/hoge/fuga ← fugaの部分がID'
CHANNEL_ID = 'fugafuga' 

client = WebClient(token=SLACK_TOKEN)

#Slackbotのチェック対象メッセージのみを取得
tgt_msg = 'リマインダー : ファイルアップロード!'

try:
    response = client.conversations_history(channel=CHANNEL_ID,limit=10)
    current_ts = time.time()

    bot_msg = ""
    for msg in response['messages']:
        if msg.get('user') == 'USLACKBOT' and msg.get('text') == tgt_msg and (current_ts - float(msg.get('ts'))) < 300:
            bot_msg = msg

    #print(bot_msg)

    send_msg = '<!here> ファイルのアップロードを忘れてますよ!'


    if not bot_msg:
        pass
    elif 'reactions' in bot_msg.keys():
        pass
    else:
        client.chat_postMessage(channel=CHANNEL_ID,text=send_msg)

except SlackApiError as e:
    # Slack API呼び出しがエラーとなった場合の処理
    print(f"Error: {e.response['error']}")

では紹介。

このあたりが必須の部分です。トークンとチャンネルIDからWebClientをつくります。

from slack_sdk import WebClient

# Slack API
SLACK_TOKEN = '1-7で確認したトークン'

#チャンネルをブラウザで開いたときの https://app.slack.com/client/hoge/fuga ← fugaの部分がID'
CHANNEL_ID = 'fuga' 

client = WebClient(token=SLACK_TOKEN)

slackbot によるリマインダーメッセージには リマインダー :  がつくので、取得対象のメッセージは下記にする。

tgt_msg = 'リマインダー : ファイルアップロード!'

メッセージを取得するには client.conversations_history を使う。

取得するメッセージは最新から10通のみに絞る(デフォルトは100)。

response = client.conversations_history(channel=CHANNEL_ID,limit=10)

中身を見てみる(ID等はマスクして改行も加えてます。)

>>> print(response)
{'ok': True, 'messages': [
	{'client_msg_id': 'XXXX', 'type': 'message', 'text': 'test', 'user': 'XXXX', 'ts': '1705842699.105359', 'blocks': [{'type': 'rich_text', 'block_id': 'XXXX', 'elements':[{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'test'}]}]}], 'reactions': [{'name': 'eyes', 'users': ['XXXX'], 'count': 1}]}, 
	{'bot_id': 'B01', 'type': 'message', 'text': 'リマインダー : ファイルアップロード!', 'user': 'USLACKBOT', 'ts': '1705842141.709709', 'blocks': [{'type': 'rich_text', 'block_id': 'XXXX', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'リマインダー : ファイルアップロード!'}]}]}], 'team': 'XXXX'}, 
	{'type': 'message', 'subtype': 'reminder_add', 'text': '  今日 22:02 , 日本標準時 にこのチャンネルでこれをリマインドするよう設定しました : “ファイルアップロー ド!” ', 'user': 'XXXX', 'ts': '1705842081.644479'}, 
	{'type': 'message', 'subtype': 'channel_join', 'ts': '1705841261.346399', 'user': 'XXXX', 'text': '<@XXXX>さんがチャンネルに参加しました', 'inviter': 'XXXX'}, 
	{'type': 'message', 'subtype': 'channel_join', 'ts': '1705839286.939959', 'user': 'XXXX', 'text': '<@XXXX>さんがチャンネルに参加しました'}], 
'has_more': False, 'pin_count': 0, 'channel_actions_ts': None, 'channel_actions_count': 0}
>>>

各要素はリストに辞書型で格納されているので、for 文で取得したい要素を指定すれば任意のフィールドで条件を組めます。

ただし、例えばスタンプがついていないと reactions は出てきません。

 

user が slackbot, text が取得対象メッセージ, かつ 5分以内のメッセージに一致するものを取得します。

for msg in response['messages']:
        if msg.get('user') == 'USLACKBOT' and msg.get('text') == tgt_msg and (current_ts - float(msg.get('ts'))) < 300:
            bot_msg = msg

メッセージの送信は client.chat_postMessage で行います。

bot_msg が空の場合と、対象メッセージにスタンプがついている場合は処理をスキップ。

send_msg = '<!here> スタンプに反応がありません!'
if not bot_msg:
    pass
elif 'reactions' in bot_msg.keys():
    pass
else:
    client.chat_postMessage(channel=CHANNEL_ID,text=send_msg)

メンションをつける場合は以下のように書くらしい。

・<@ユーザーID>

・<#チャンネルID>

・<!here>

 

最後のexcept 文は例外処理なので省略。

 

Linux(cron)設定

定時実行させるために Linux の cron を用います。

Cloud Scheduler なり GAS の定期実行(GASだと定時にはならないが…)でもできそう。

[test@test ~]$ crontab -l
3 9 * * * /usr/bin/python3.9 /home/hoge/work/check_work.py
[test@test ~]$

これで9:00のリマインダーに対して3分以内にスタンプの反応がなかったら、注意メッセージが飛んできます。

取得条件さえいろいろ変えれば汎用的に役に立ちそう。

気に入ったらぜひ共有してください。

TOP