【Slack自動保存】サーバーレスでCronのように定期的にプログラムを実行しよう

どうもこんにちは。zonoと申します。
普段は coconala.com の開発をしています。

おや、ブログの順番回ってきた。さて何書こうかな。

・・・よし、1人Slackとサーバーレスネタにしよう。

1人Slack

ここ数ヶ月、自分用のメモとしてSlackを使っています。 ・・・と言っても会社のSlackではなく、自分1人だけのSlack。

Slackって個人でも使えるんですよ。知ってました?しかも無料。

他に誰もいないので好きなこと書き放題。スレッド機能があるのでTodoリストや日報的に使うもよし。色々なアプリケーションとも連動できるし、Twitterや記事のURLを渡せば記事やツイートの内容を自動で取ってきてくれるし。

f:id:coconalainc:20180710200237p:plain

とにかく適当につらつらーーっと何か書きとどめておけるのが大変便利なのです。

例:日報的に使ってみる

大体始業前に「今日はこれやるぞ」と決めているので、Slackにざらざらとリストを書きます。 1タスク1発言がオススメ。

f:id:coconalainc:20180710200429p:plain

で、1日が終わる時はこれにリアクションをつけます。NGとかWIPとかDoneとかとか。

f:id:coconalainc:20180710200453p:plain

各タスクについて何か書きたい時はスレッドにしちゃえばOK。 というわけで、意外と使えて便利なんです。

落とし穴

1. メッセージ保存数

便利なんですが、「無料」には落とし穴があるわけで。 無料版のSlackには下記の制限があります。

slack.com

一番大きいのは「チームの直近のコミュニケーション10,000件」ですかね。 直近1万件を超えたメッセージは閲覧できなくなります。

2. 検索がつらい

チャットツールにありがちなのですが、検索が大変使いにくい。

解決策を探してみる

上記2点を解決すべく、思いついたのがEvernote。SlackとEvernoteを連携すると

  • 「/clip today」
  • 「/clip yesterday」

といったコマンドで一日のSlackの投稿をEvernoteに保存できます。

f:id:coconalainc:20180710200700p:plain

メッセージ数の上限は気にしなくてよくなるし、Evernoteの検索は少なくともSlackより良い。

というわけで、1日に1度「/clip」コマンドを実行すればOKになりました。

やっぱりそれでも面倒くさい

手動めんどくさい。
やだ。
忘れる。
だるい。
自動で毎日実行させたい。 (ものぐさの極地)

やっと本題

そんなこんなで本題です。外からSlackのコマンドを実行させるようにします。 ちなみに素直に「/clip」とAPIに投げると...

f:id:coconalainc:20180710200734p:plain

「/clip」とつぶやくだけ。寂しい!

Slack API

(表だって公開はされていない?) チャットコマンド実行用のSlack APIがあるようです。

github.com

こいつをたたくと実行できるのかな?

定期でコマンド実行させるには

下記を使います。

この2つを使えばサーバーレスで、かつ定期でコマンド実行できちゃう!しかも無料枠で十分補える!

え?AWSの契約?個人で契約していますが何か。 (セキュリティはしっかりね!)

Lambda用のコードを書く

というわけでまずはさくっとLambda用のコードを書きましょう。 Node.jsは得意じゃないのでPythonで。

slack_command.py

Slack APIをたたくための処理です。

# -*- coding: utf-8 -*-
import urllib.request
import json

API_URL = 'https://slack.com/api/chat.command'

def execute_command(token, channel_id, command, text = ''):
    params = {
        'token' : token,
        'channel' : channel_id,
        'command' : command,
        'text'  : text
    }

    req = urllib.request.Request('{}?{}'.format(API_URL, urllib.parse.urlencode(params)))
    with urllib.request.urlopen(req) as res:
        body = res.read()

    return json.loads(body)

handler.py

Lambda起動用メソッドをさくっと書きます。 環境変数から各種値を受け取り、さっき書いた処理に渡しているだけですね。

# -*- coding: utf-8 -*-
import os
import json
from slack_command import execute_command

def handler(event, context):
    slack_command = os.environ['COMMAND']
    slack_text = os.environ['TEXT']
    token = os.environ['TOKEN']
    channel_id = os.environ['CHANNEL_ID']

    result = execute_command(token, channel_id, slack_command, slack_text)
    if result['ok']:
        print('Command "%s %s" OK' % (slack_command, slack_text))
    else:
        print('Command "%s %s" NG' % (slack_command, slack_text))

この2ファイルあればOK。

Lambdaの設定

コードをアップロード

2ファイルをzipに固めてアップロードします。

f:id:coconalainc:20180710201022p:plain

f:id:coconalainc:20180710200915p:plain

  • コードエントリタイプ ... zipファイルをアップロード
  • ランタイム ... Python3.6
  • ハンドラ ... 起動ファイル名と起動メソッドをピリオドでくっつけたもの。今回は「handler.py」の中の「handler」メソッドなので「handler.handler」とします。

環境変数設定

handler.py内で取得している4つの環境変数をセットします。

f:id:coconalainc:20180710201042p:plain

これで「保存」を押せば完了。

CloudWatch Eventsの設定

今回のLambda関数は登録しただけでは動きません。外部から何かトリガーをひいてあげる必要があります。

Cronのように定期的に関数を実行したいのでCloudWatch Eventsを設定します。

「ルールの作成」からイベントを作成します。

f:id:coconalainc:20180710201108p:plain

「イベントソース」は「スケジュール」にします。 決まった時間に実行したいならば「Cron式」を選びましょう。

f:id:coconalainc:20180710201246p:plain

式の内容は cron(* * * * * *) です。Cron式を書かないといけないので注意。

参考: ルールのスケジュール式 - Amazon CloudWatch Events

ターゲットに先ほど作ったLambdaを選択し、決定すれば完了です。

f:id:coconalainc:20180710201547p:plain

一日一回、0時30分に実行するように設定しました。 (時刻はUTCで設定しないといけないので注意)

テストで時間を「数分後」に設定して待っていると...

f:id:coconalainc:20180710202202p:plain

動いた!clipされてる!!

というわけでCloudWatch EventとLambdaを組み合わせたら、サーバー管理無し+好きなタイミングでプログラムを走らせることができました。あらステキ。

AWS Lambdaは無料枠の容量が大変多いので、個人でもおおいに活用しましょう!オススメ!

最後に

ココナラでは一緒に事業を成長させてくれるエンジニア、デザイナーの仲間を探し中でございます。 少しでも興味がありましたら、カジュアルにお話しましょう!

www.wantedly.com

www.wantedly.com