みなさん、在宅勤務やってますか?そして在宅勤務の打刻忘れてませんか?僕は忘れっぽいんですが、在宅勤務の打刻だけは忘れてません。が、打刻をしたあとslackのスレッドにも業務を開始する旨を開始時間と一緒に投稿する必要があります。そして退勤するときも同様に打刻した後に同じスレッドに退勤時間を投稿する必要があります。**これがまあ面倒くさいんですよ。**面倒くさいことはなんとかして解決する、それがエンジニアってもんじゃないですか(偏見)
というわけで、今回はこのSlackへの投稿を自動化してみたいと思います。
※今回はプライベートのテスト環境でためしてます。
今回使うもの
さて何をつかってこれを自動化しようか考えたときに机の上にあるものを見つけました。

そうです、以前Soracom UGのハンズオンで買ったあのボタンです。ハンズオンが終わった後には、ラズパイと連携するネタに使いその流れで夏場はこれを使って遠隔でエアコンの電源をつけるリモコンとして使っていました。そして今年も稼働させようとしてたところでした(いや、本当ですよ)。ですが、今はそのシステムよりも打刻をこれで楽にさせるほうが大事だと思ったので、これを使っていきたいと思います。
イメージ
3種類のトリガーを生かしてこう割り振りたいと思います
- シングルクリックで出勤の連絡をする
- ダブルタップで退勤の連絡をする
- ロングタップは直前の投稿を消す(できたら実装したい)
Slackのtokenを取得
まずはslackにメッセージを投稿させるためにtokenを取得します。こちらのサイトから、Create an Appをクリックして、新規のSlack appを作成します。

このようなダイアログが表示されるので、ここでApp Nameと今回使用するワークスペースを指定して、下のCreate Appをクリックします。App Nameはわかりやすい名前にしておきましょう。後で変更することもできます。

作成すると、以下の画面が出力されるので、右下のPermissionsを選択しておきます。

次の画面をスクロールしたときにScopesという項目がありますが、こちらはUser Token Scopesの中からchat:write、そしてchannel:historyを選択します。

上にスクロールして、Install App to Workspaceをクリックします。

その後、以下のようにOAuth Access Tokenが発行されるので、コピーしておきましょう。

lambdaの実装
その前に今回のシチュエーション
今回はslackに勤怠チャンネルがあって、下図のようなメッセージが毎朝投稿されるからこれに出勤時刻、退勤時刻を投稿するというシチュエーションを考えます。(念のために言いますが、あくまで架空のものです)

今回の実行環境
- AWS lambda(RuntimeはPython3.6)
- SlackのPython SDK
環境構築(lambdaを作成)
- AWSにログインして検索窓からlambdaと入力しlambdaにアクセス
- 関数名はお好みに設定し、ランタイムはPython3系(今回はPython3.6)を選択
- あとは特にいじらずに関数の作成をクリック
- SlackのSDKを使うので、Cloud9で開発環境を構築する(特に何も設定をいじらずに順に設定してOK)
- Web IDEを立ち上げて右側のlambdaマークをクリックして、先程作成したlambdaを選択し、IDE環境にインポートする
   
- IDEの下のターミナルでcd YOUR_LAMBDAと入力し、実行環境にディレクトリを移動したら、以下のコマンドを実行し、SlackのSDKをインストールする
python -m pip install --target=./ slackclient
ソースコード
デフォルトの関数を消して、以下のソースコードに書き換えます。channel IDには皆さんのお使いのワークスペースのアドレスのhttps://WORKSPACE_URL/archives/channelIdの形式のとき、channelIdの文字列をコピペします。
from slack import WebClient
from slack.errors import SlackApiError
from datetime import datetime
import datetime as dt
import json
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
token = "YOUR_TOKEN"
client = WebClient(token=token)
def lambda_handler(event, context):
    logger.info('request event: \n' + json.dumps(event))
    
    # クリックイベントを取得する
    click_type = event['deviceEvent']['buttonClicked'].get('clickType')
    logger.info("click_type: " + click_type)
    
    try:
        # 返信するスレッドのtsを取得
        channelId = 'YOUR_CHANNEL_ID'   # https://WORKSPACE_URL/archives/channelId
        response = client.conversations_history(channel=channelId)
        ts = response["messages"][0]["ts"]
        
        # 投稿時刻を設定し、スレッドに送信
        nowDate = datetime.utcnow() + dt.timedelta(hours=9)
        nowTime = nowDate.strftime("%H:%M")
        if click_type == "SINGLE":
            message = "業務開始します {}".format(nowTime)
        elif click_type == "DOUBLE":
            message = "業務終了します {}".format(nowTime)
        response = client.chat_postMessage(
            channel=channelId,
            thread_ts=ts,
            text=message)
        logger.info(response)
        return{
            'statusCode': 200,
            'body': 'Succeed'
        }
    except SlackApiError as e:
        logger.info(e.response["ok"] is False)
        logger.info(e.response["error"])
        return {
            'statusCode': 500,
            'body': f"Got an error {e.response['error']}"
        }
テスト
Cloud9上でlambdaを開発したときには、その場で関数をテストすることができます。右側のlambdaのタブの上部にある再生ボタンの隣の下矢印をクリックして、Run Localを選択して再生ボタンを押します。

これでローカル環境で実行できる状態ですが、実際にLTE-Mボタンを押したときにlambdaが受け取るデータが無いとテストができません。そこで以下のjsonをコピーして、test payloadに貼り付けます。なお、使うJSONは以前使用したものと全く一緒ですが、今回のコードの動作に影響はないのでそのまま使います。
{
    "deviceInfo": {
        "deviceId": "G030PMXXXXXXXXXX",
        "type": "button",
        "remainingLife": 94.2,
        "attributes": {
            "projectRegion": "us-west-2",
            "projectName": "One-click-Raspi",
            "placementName": "button1",
            "deviceTemplateName": "RequestCmd"
        }
    },
    "deviceEvent": {
        "buttonClicked": {
            "clickType": "SINGLE",
            "reportedTime": "2019-01-24T18:49:36.813Z"
        }
    },
    "placementInfo": {
        "projectName": "One-click-Raspi",
        "placementName": "button1",
        "attributes": {
            "SINGLE": "{\"cmd\":\"green\"}",
            "DOUBLE": "{\"cmd\":\"blue\"}",
            "LONG": "{\"cmd\":\"red\"}"
        },
        "devices": {
            "RequestCmd": "G030PMXXXXXXXXXX"
        }
    }
}
貼り付けたら左上にあるRunボタンをクリックして、テストしてみます。以下のように指定したチャンネルに最後に投稿されたスレッドにメッセージが送信されれば、成功です。

デプロイ
それでは、最後に作成した関数をlambdaのリソースにデプロイします。IDEの右側にあるlambdaタブで先程作成したlocal lambdaを右クリックしてdeployを選択して関数をデプロイをします。デプロイを終えて、lambdaのリソースに移動し先程作成したlambdaにCloud9からデプロイした環境が反映されていたら成功です。

今回はここまで
長くなったので、今回はここまでにします。次回いよいよボタンからこのlambdaを実行していきます。
後編はこちら
ソラコムの【あのボタン】で勤怠打刻してみた(その2)

