お役立ち Twitter Bot を作りながら学ぶ AWS ドリル

~第 6 回 AWS Step Functions を使って Well-being Bot を作ろう ! (前編)

2022-06-01
How to be a Developer

Author : 金澤 圭

ソリューションアーキテクト(SA)の金澤 (@ketancho) です。6 月になりましたが、皆さまいかがお過ごしでしょうか ? 私は通勤や退勤のときに 1 駅分歩いて気分をリフレッシュするようにしているのですが、この時期から雨と暑さでしばらくそれができなくなってしまうのが辛い今日この頃です。そして、6 月は祝日がないというハードモード.. はやく夏休みが来てほしいものです。皆さまも健康に気をつけてお過ごしください。

この連載記事のその他の記事はこちら

選択
  • 選択
  • 第 1 回 おはよう Bot 編
  • 第 2 回 昔書いた記事の宣伝 Bot 編
  • 第 3 回 リファクタリング & 曜日ごとのツイート 編
  • 第 4 回 新章突入 ! 気になるワード検索 & 通知 Bot 編
  • 第 5 回 皆さまの代わりに英語でツイートしておくよ Bot 編
  • 第 6 回 AWS Step Functions を使って Well-being Bot を作ろう ! (前編)
  • 第 7 回 AWS Step Functions を使って Well-being Bot を作ろう ! (後編)
  • 第 8 回 - 総集編 & 夏休みの自由研究のすすめ
  • 第 9 回 - IaC 入門しながら作るリマインダー Bot (前編)
  • 第 10 回 - IaC 入門しながら作るリマインダー Bot (中編)
  • 完結回 - IaC & CI/CD 入門しながら作るリマインダー Bot (後編)

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 


1. この記事で作る Bot の機能と、とりあげる AWS サービス・機能

さて、この記事ではそんな季節にぴったりな (?) 皆さまの健康を気にかける Well-being Bot を作成していきます。前回のドリル で皆さまの普段づかいのツイートを検索する実装を行いましたが、今回も同じように直近のツイートを検索し、元気がなさそうであればお声がけする、そんな Twitter Bot を作りたいと思います。「この記事では」と書いたのですが、ひとつの記事にするには実装量が多くなってしったので、ふたつの記事に分け、

  • 今回 : 日中のツイートがない = 仕事に忙殺されて辛そう => 「大丈夫 ?」的なメンションを送る
  • 次回 : 日中のツイートの内容を見てネガティブなことを言ってる = 辛そう =>「元気だして !」的なメンションを送る

という 2 段階で、Well-being Bot を完成させます。

これまでの AWS ドリルをやってきた方からすると「あれ ? 今回のドリル簡単すぎませんか ? 今の私の実力からするとお茶の子さいさいですよ ?」と思われた方もいるかもしれません。そう思われた方の感覚は正しく、今までのドリルで学んだことを横展開することで、今回の記事分は十分実装可能な内容です。(力試しとしてご自身で取り組んでみるのも面白いと思います。)

実は今回、新たに AWS Step Functions というサービスを使って Bot を作成していこうと考えています。これまでのドリルでは、ひとつの Lambda 関数の中に全ての実装を詰め込んでいたので、関数がやや肥大化してしまう傾向がありました。そこで今回、AWS Step Functions を使い、複数の細かい Lambda 関数をワークフローから順に呼び出す形で Bot を構築していきます。このような形にすることで、ひとつひとつの Lambda 関数をコンパクトに、かつ再利用しやすい形にすることができます。

今回は、この Step Functions に慣れていただきたいという想いもあり、お題をシンプルなものにしています。

AWS Step Functions は、サーバーのプロビジョニングや管理なしで、ワークフローを定義・実行できるサービスです。条件によって呼び出す Lambda 関数を変えたり、並列に処理を行ったりといったワークフローを作成できるサービスです。Step Functions では、下記のような状態 (ステート) を組み合わせてワークフローを定義します。

  • Task : AWS サービスの呼び出し
  • Choice : 条件による分岐
  • Parallel : 並列処理

詳細は こちらのドキュメント を確認してみてください。AWS Step Functions では、このワークフローをステートマシンという単位で作成・管理します。

さて、いつも通り今回作る Bot の構成図も示します。Step Functions のワークフローとして、「ツイート検索し、もし直近のツイートがなければ心配するツイートをする」という流れを定義します。そして Amazon EventBridge で定期的に呼び出すことで、Twitter Bot としての機能を実現します。

このドリルにおける作業のざっくりとした流れは下記の通りです。

  • 引数で受け取った文字列をツイートする Lambda 関数を作成する
  • 上の Lambda 関数を呼び出し、ツイートするだけの Step Functions ステートマシンを作成する
  • 引数で受け取った条件でツイート検索する Lambda 関数を作成する
  • Step Functions ステートマシンを修正し、条件に応じて心配ツイートをするステートマシンにアップデートする
  • 毎日 21 時になったらステートマシンが起動するように EventBridge の設定を行う

それでは始めていきましょう ! 🙌


2. 今回から AWS ドリルを始める方の作業

さて、具体的な作業を始める前に前提を揃えたいと思います。今回も、この記事で完結するので、この記事から読み始めていただいても問題なくドリルを進められるかと思います。ただ、この記事では取り扱わない前提作業を AWS ドリル #4 の「2. 今回から AWS ドリルを始める方の作業」 にまとめています。今回から手を動かすぞ ! という方は、こちらを先にご確認いただければと思います。

また、ツイートする部分やツイートを検索する部分の実装は、これまでのドリルの内容をそのまま使ったり、ベースにしたりしています。実装の意図などは過去の記事をあわせてご参照いただくことで、より理解が深まると思います。


3. 引数で受け取った文字列をツイートする Lambda 関数を作成する

それでは今回の AWS ドリルを進めていきましょう。まずはいつも通り、お試しツイートするまでを最初の作業としています。

3-1. Lambda 関数を新規で作成する

いつも通り AWS Lambda のサービス画面 に遷移し、右上の「関数の作成」をクリックしてください。

関数の基本的な情報には、

  • 関数名 : twitter-bot-post-tweet-module-function
  • ランタイム : Python 3.9
  • アーキテクチャ : x86_64

を設定し、「関数の作成」をクリックしてください。

クリックすると拡大します

続いて、requests_oauthlib という Python モジュールをインストールします。こちらはこれまでのドリルで何度もやっている作業ですね。Twitter API を呼び出すときにこのモジュールを利用するのですが、標準の Lambda 関数には含まれておりません。そのため、requests_oauthlib をインストールし、それを .zip ファイルにまとめ、Lambda 関数にソースコードとしてアップロードします。

今回も AWS CloudShell 上で、作業をします。マネジメントコンソールの右上のコンソールマークをクリックしてください。

クリックすると拡大します

CloudShell のコンソールが起動したら、下記のコマンドを実行してください。(最後のコマンドの後にもエンターを押すのを忘れないように気をつけてください。)

mkdir twitter-bot-post-tweet-module-function
cd twitter-bot-post-tweet-module-function/
pip3 install -U pip
pip install requests_oauthlib -t ./
touch lambda_function.py
zip -r twitter-bot-post-tweet-module-function.zip ./
aws lambda update-function-code --function-name twitter-bot-post-tweet-module-function --zip-file fileb://twitter-bot-post-tweet-module-function.zip

無事にコマンドが流れ終わったら、先ほど作成した Lambda 関数 twitter-bot-post-tweet-module-function を再度表示 (既に表示している人はブラウザをリロード) してください。

エディターの左側に依存パッケージ群が配置され、一番下には lambda_function.py があるはずです。こちらの Python ファイルをダブルクリックして開いてみると、中身が空のファイルが右側に表示されます (現時点で中身は空で大丈夫です)。

クリックすると拡大します

3-2. Lambda 関数に紐づく IAM ロールの修正

続いて、Lambda 関数に紐づく IAM ロールの修正を行います。

この作業も前回のドリルと同じ作業で、デフォルトで用意されている IAM ポリシー「AmazonSSMReadOnlyAccess」を Lambda 関数の IAM ロールにアタッチします。ドリル #4 の「3-2. Lambda 関数に紐づく IAM ロールの修正」を参照していただき、ご実施をお願いします。

3-3. Lambda 関数のコードを修正する

さて、ここから Lambda 関数のコードを修正していくのですが、先ほど少し触れましたが汎用的に使える Lambda 関数にしたく、「引数で受け取った文字列をツイートする」関数にしていきます。

まずは、先ほど開いた labmda_function.py に下記のプログラムをコピー & ペーストしてみてください。初期化処理を行う init メソッド、Twitter のツイート API を呼び出す tweet メソッドは実装済みです (こちらの詳細な解説は AWS ドリル #3 をご覧ください)。後ほど、TODO の部分を実装していきます。

import json
from requests_oauthlib import OAuth1Session

import boto3
ssm_client = boto3.client('ssm')

oauth = None

def lambda_handler(event, context):
    init() 
    # TODO: ここを修正する
    
def init():
    response = ssm_client.get_parameter(
        Name='/credentials/twitter',
        WithDecryption=True
    )
    twitter_parameters = json.loads(response['Parameter']['Value'])

    consumer_key = twitter_parameters['consumer_key']
    client_secret = twitter_parameters['client_secret']
    access_token = twitter_parameters['access_token']
    access_token_secret = twitter_parameters['access_token_secret']
    
    global oauth
    oauth = OAuth1Session(consumer_key, client_secret, access_token, access_token_secret)
        
def tweet(text):
    payload = {'text': text}
    response = oauth.post(
        'https://api.twitter.com/2/tweets',
        json=payload,
    )
    if response.status_code != 201:
        raise Exception(
            '[Error] {} {}'.format(response.status_code, response.text)
        )

lambda_handler メソッドの event がこれまで「引数」と呼んでいたものになります。

Lambda 関数は、他のサービスから呼び出される形で使われることが多いのですが、その際、この event に情報が詰め込まれてきます。event から情報を取り出し、Lambda で煮るなり焼くなりするわけです。では、こちらの TODO 部分の実装を進めていきましょう。

def lambda_handler(event, context):
    init() 
    # TODO: ここを修正する

ここでは、event (JSON 形式) の tweet_text というキーに、ツイートしたい文字列が詰め込まれてくる、という前提とし、tweet_text に格納された文字列をそのままツイートする実装をします。

私はこのような実装にしてみました。ソースコードを修正したら、忘れずにデプロイしてください。

def lambda_handler(event, context): 
    init()

    tweet_text = event['tweet_text']
    tweet(tweet_text)

3-4. Lambda 関数のテスト実行をする

Lambda 関数のテスト実行をします。これまでのドリルでは、Lambda 関数のテスト機能を簡単にしか使ってきませんでした (名前だけ付けてすぐに実行していました) が、今回はちょうどいい題材ですので、もう少しこの機能を活用していきます。

まず、Lambda 関数の画面の Test ボタンをクリックしてください。テストイベントを設定する画面が出てきます。この画面で、Lambda 関数の event に引数としてどのような JSON  (以後、イベント JSON と記載します) を渡してテスト実行するかを指定できます。

クリックすると拡大します

Test ボタンをクリックすると、(今回は初回実行時なので)「テストイベントを設定」という画面が出てきます。こちらでいつもどおりイベント名を記入してください。私は tweet-test としました。

(なお、一度決めたイベント JSON の内容を変更したい場合は、Test ボタン右側の矢印から修正することができます。)

クリックすると拡大します

続いて、下部にスクロールしていただくと、イベント JSON を入力する欄が出てきます。こちらに

{
  "tweet_text": "こんにちは!AWS ドリル #6 を進めています"
}

という JSON を入力してください。tweet_text の内容はよしなに変えていただいて構わないのですが、こちらに入力した内容が後ほどツイートされる点にご注意ください。

入力が終わりましたら、右下の「保存」ボタンをクリックしてください。

クリックすると拡大します

もう一度、Test ボタンを押すと、先ほどのイベント JSON を引数にして、Lambda 関数が実行されます。このようにツイートされれば成功です🎉

クリックすると拡大します


4. ツイートするだけの Step Functions のステートマシンを作成する

それではここから Step Functions を使って実装を進めます。冒頭で述べた通り、まずはシンプルに「ツイートするだけ」の流れを実装します。

4-1. Step Functions ステートマシンを作成する

まずは、AWS Step Functions の画面 にアクセスしましょう。

こちらの左上のハンバーガーメニューをクリックし、表示されるメニューから「ステートマシン」を選択してください。

クリックすると拡大します

ステートマシンの一覧画面に遷移します。右上にある、「ステートマシンの作成」をクリックし、作成を進めましょう。

クリックすると拡大します

ステートマシンを作る方法、そしてステートマシンのタイプを選択する画面に遷移します。今回はこちらは変更せずに、そのまま「次へ」をクリックしてください。

クリックすると拡大します

続いて、GUI でワークフローを作る画面 (Workflow Studio といいます) が表示されます。ワークフローは Amazon State Language (ASL) というコードの形でも定義できるのですが、今回はこの GUI ツールでワークフローを定義します。左側のメニューにアクションやフローが並んでいるので、こちらから右側にドラッグ & ドロップしてワークフローを作成します。

まずはじめに、ワークフローのタイムアウト値を設定します。右側の TimeoutSeconds の値を 30 としてください。

クリックすると拡大します

続いて、左側のアクションから「AWS Lambda Invoke」を選び、「最初の状態をここにドラッグ」の部分にドラッグ & ドロップしてみてください。

クリックすると拡大します

このように Start と End の間に Lambda Invoke アクションが入りましたでしょうか?

続いて、この Lambda Invoke アクションに関する詳細設定を進めます。Lambda Invoke アクションをクリックし、選択された状態 (選択されているとアクションの背景色が水色になります) にしてください。

クリックすると拡大します

アクションの詳細が右側に出てくるので、こちらで設定を進めます。

まず、「状態名」には「Tweet Lambda Invoke」と入力してください。こちらは、必須の設定ではないのですが、今後 Lambda 関数を呼び出すアクションが増えたときに、各アクションでどの Lambda 関数を呼び出しているかを明確にするために名付けます。

続いて、API パラメータ内の「Function name」のプルダウンから、先ほど作成した「twitter-bot-post-tweet-module-function:$LATEST」を選択してください。このプルダウンには過去のドリルで作ってきた Lambda 関数も表示されるので、選ぶ関数を間違えないように気をつけてください。

2 箇所設定が終わりましたら、最初の Step Functions ワークフローの設定は全て完了です。右上にある「次へ」をクリックし、ステートマシンとして保存していきましょう。次の画面で「生成されたコードを確認」という画面が出ますが、こちらの画面では何もせずに右下に出ている「次へ」をクリックしてください。

クリックすると拡大します

最後にステートマシンの設定をする画面が出てきます。ここでは名前だけ決めていただきます。名前の欄に「TwitterBotWellBeingMyStateMachine」と入力してください。

クリックすると拡大します

その後、右下の「ステートマシンの作成」をクリックすれば、はじめての Step Functions ステートマシンの完成です。

クリックすると拡大します

4-2. Step Functions ステートマシンをテスト実行する

ステートマシンが完成すると、このような画面に遷移します。今後、ステートマシンの変更をする場合は、この画面から編集します。また、ステートマシン用に必要な権限を含む IAM ロールが自動生成されます。先々、この IAM ロールを変更するのですが、その際もこの画面に戻ってきますので、覚えておいていただくとよいでしょう。

それではうまく実装できているか、テストしてみましょう。下部にある「実行」タブから「実行の開始」をクリックしてください。

クリックすると拡大します

実行する際は、ワークフローへのに渡す入力値を指定できます (最初のアクションにそのまま渡されます)。最初の Tweet Lambda Invoke アクションは、ツイートする文字列が tweet_text に入力されることを期待しているので、この入力値は

{
    "tweet_text": "AWS ドリル #6 では、AWS Step Functions を使っていきます!"
}

としていただき、右下の「実行の開始」をクリックしてください。

クリックすると拡大します

実行の状況や結果は、グラフインスペクターという画面から確認ができます。このように、アクションが緑色になっていれば処理がうまくいっていることになります。

該当アクションをクリックすると、そのアクションの入出力が確認できます。この絵では、先ほどの入力値の JSON がアクションにそのまま渡されていることが確認できます。

クリックすると拡大します

今回は成功した例をスクリーンショットとして貼っていますが、もし何かしらの理由でエラーが出ている場合は、該当するアクションの背景色が赤くなります。そのアクションをクリックすると、「例外」タブが出てきますので、こちらに表示されるエラー情報をデバッグの際に活用してみてください。

また、こちらの実行結果の画面について、1 点注意点がございます。この記事を執筆しているタイミングで、新しいレイアウトへの変更が進んでいるようです。マネジメントコンソール上部に「新しいページを試す」というボタンが表示されている方もいるかと思います。こちらをクリックすると、この記事の実行結果画面とは異なる画面になります。画面のレイアウトがそこまで大きく変わるわけではないのですが問題ないと思うのですが、この記事では従来の画面で進めていきますので、お手元の画面のレイアウトと異なる可能性がある旨、ご認識いただければと思います。

さて、以上で最初の Step Functions 開発が完了です。Twitter Bot アカウントを確認すると、このようなツイートがされていますでしょうか ? うまくツイートされていればここまでの作業は成功です🎉

クリックすると拡大します

Step Functions の利用の流れは掴めたでしょうか ? このあとはこの Step Functions を更に活用して、元気がなさそうだったら声をかける Bot を作っていきます。冒頭でお伝えした通り、今回は「忙殺されていそう (=ツイートがない) であれば心配する」機能の実装を進めていきます。具体的には、

  • 毎日 21 時にその日の日中のツイートがあるかを確認する
  • 「日中」の定義は「9 時頃から 21 時頃 (Bot が動いたタイミング)」とする
  • もし、ツイートがなければ、「@ketancho 元気ですか?無理せずコーヒーでも飲みながら Twitter しましょう!」とツイートする (メンション先は皆さまのアカウントにしてください🙌)

という動きをする Bot にしていきたいと思います。皆さまの Twitter の使い方に応じて、細かい動きは変えて頂いてもいいと思います。例えば、「自分はそんなに Twitter を使わないので、3 日間ツイートがなければ心配するくらいで十分」「自分はヘビーユーザーなので、1 日のツイート数が 10 件未満であれば心配してほしい」などの改修をしてみてもいいかもしれません。

さて、このあと実装に入っていきますが、改めて残りの作業の流れを示します。この後は、

  • 引数で受け取った条件でツイート検索する Lambda 関数を作成する
  • Step Functions ステートマシンを修正し、条件に応じて心配ツイートをするステートマシンにアップデートする
  • 毎日 21 時になったらステートマシンが起動するように EventBridge の設定を行う

という順番で進めていきます。それではやっていきましょう !


5. 引数で受け取った条件で、ツイート検索する Lambda 関数を作成する

それでは実装を再開します。まずは、ツイート検索する Lambda 関数を作成します。まずは、Lambda 関数のひな形を作成していきます。この記事の「3. 引数で受け取った文字列をツイートする Lambda 関数を作成する」を参考にしていただきたいのですが、下記が変更点となります。

  • 関数名は twitter-bot-search-tweet-module-function としてください
  • CloudShell を使ったスクリプトの実行時に、↑ の関数名に変更が必要な点にご注意ください
  • IAM ロールに IAM ポリシー「AmazonSSMReadOnlyAccess」を付け忘れないようご注意ください

こちらまで実施できましたら、コードのひな形も用意しているので、labmda_function.py に下記のプログラムをコピー & ペーストしてみてください。Twitter の検索 API を呼び出す get_matching_tweets メソッドは実装済みです。(こちらの詳細な解説は AWS ドリル #4 をご覧ください)

import json
from requests_oauthlib import OAuth1Session
from datetime import datetime, timedelta

import boto3
ssm_client = boto3.client('ssm')

oauth = None

def lambda_handler(event, context): 
    init()
    # TODO: ここを修正する

def init():
    response = ssm_client.get_parameter(
        Name='/credentials/twitter',
        WithDecryption=True
    )
    twitter_parameters = json.loads(response['Parameter']['Value'])

    consumer_key = twitter_parameters['consumer_key']
    client_secret = twitter_parameters['client_secret']
    access_token = twitter_parameters['access_token']
    access_token_secret = twitter_parameters['access_token_secret']
    
    global oauth
    oauth = OAuth1Session(consumer_key, client_secret, access_token, access_token_secret)
    
def get_matching_tweets(search_query, start_time, end_time):
    url = 'https://api.twitter.com/2/tweets/search/recent'
    params = {
        'query': search_query,
        'max_results': 20,
        'start_time': start_time,
        'end_time': end_time,
    }
    response = oauth.get(
       url, params=params
    )
    
    if response.status_code != 200:
        raise Exception(
            '[Error] {} {}'.format(response.status_code, response.text)
        )

    return response.json()

5-1. Lambda 関数のコードを修正する

それでは、ツイート検索する Lambda 関数にしていきましょう。先ほどのツイート用 Lambda 関数と同様、検索用の Lambda 関数も、受け取った引数 (イベント JSON) に応じて検索条件を変えられるようにしたいと思います。今回は、

  • search_query : Twitter の検索 API の query に渡す文字列 (例 : from:ketancho -is:retweet ← @ketancho のツイートを検索する。ただし、リツイートは除く。リプライや引用リツイートは検索対象に含める。)
  • search_before_hours : 何時間前までのツイートを対象にするか (例 : 12 ← 12時間前から現在までのツイートを対象にする)

の 2 つを引数として受け取るように実装していきます。また、呼び出し側 (今回は Step Functions のワークフロー) で検索結果を使うので、取得できた JSON をそのまま返却する形にしたいと思います。

 



私はこのように実装をしてみました。

def lambda_handler(event, context): 
    init()
    
    search_query = event['search_query']
    search_before_hours = int(event['search_before_hours'])
    
    start_time = (datetime.now() + timedelta(hours=search_before_hours*-1)).strftime('%Y-%m-%dT%H:%M:%SZ')
    end_time =  (datetime.now() + timedelta(seconds=-30)).strftime('%Y-%m-%dT%H:%M:%SZ')
    
    json_response = get_matching_tweets(search_query, start_time, end_time)

    return json_response

なお、前回のドリル #5 でも触れましたが、end_time は Twitter API の仕様で、検索時間より10秒以上前の時間を指定する必要があります。今回は 30 秒前にずらすことでこの仕様を回避しています。

また、こちらの end_time は必須項目ではない、かつ、現時点の要件としては終わりの時間は気にしなくていいので、end_time を指定しなくても結果はほとんど変わりません (もしかしたら後で使うかもしれないので実装しています)。

5-2. Lambda 関数のテスト実行をする

さて、ここまで実装できましたら、忘れずにデプロイを行い、関数のテストを進めましょう。例えば、イベント JSON を下記のように指定すると、今回の要件に合うテストができるはずです。

{
  "search_query": "from:ketancho -is:retweet",
  "search_before_hours": "12"
}

テスト実行を行い、レスポンスとして下記のような JSON が取得できれば OK です。直近12時間でツイートしていなければ、data の部分がないと思いますが、それが正しい動きです。イベント JSON の search_before_hours を変更し、対象期間を長くすることで、結果が変わるか見てみてください。

この後の実装では、meta の部分に含まれる result_count を使って条件分岐をしていきます。

{
  "data": [
    {
      "id": "1523764637279301632",
      "text": "6月 祝日 ない なぜ"
    }
  ],
  "meta": {
    "newest_id": "1523764637279301632",
    "oldest_id": "1523764637279301632",
    "result_count": 1
  }
}

6. Well-being Bot としての動きになるように Step Functions ステートマシンを修正する

それでは、Step Functions 側の修正を進めます。

6-1. Step Functions のステートマシンを修正する

先ほど作成したワークフローを修正します。

ステートマシンの画面に遷移し、「編集」ボタンをクリックしてください。

クリックすると拡大します

編集する画面に遷移するのですが、今回もコード (ASL) ではなく GUI で修正を進めたいので、右側にある「Workflow Studio」ボタンをクリックします。

クリックすると拡大します

Workflow Studio の画面に遷移しましたら、まずは先ほど作成した Lambda 関数 twitter-bot-search-tweet-module-function を呼び出すアクションを追加します。

Start と (最初に追加していた) Tweet Lambda Invoke の間に新しく AWS Lambda Invoke アクションを差し込みます。

クリックすると拡大します

このように間に差し込めましたでしょうか?このあと、新たに追加した Lambda Function Invoke アクションの詳細設定をしていきます。

もし、今追加したアクションをが選択し、右側の設定画面で、

  • 状態名 : Search Lambda Invoke を入力
  • Function name : twitter-bot-search-tweet-module-function:$LATEST を選択

としてください。

クリックすると拡大します

続いて、条件分岐するようにワークフローを修正していきます。その前に、もしツイート検索の結果ツイートがあった場合は、ステートマシンの実行を終了したいので、Success フローを追加しておきます。

左上のタブで、「フロー」をクリックした後、「Success」を右側にドラッグ&ドロップしてください。この時点では、「Tweet Lambda Invoke」の下、End の上に差し込む形にしてください。

クリックすると拡大します

続いて、条件分岐のための Choice ステートも追加していきます。今回は、検索結果に応じて処理を分岐させたいので、「Search Lambda Invoke」と「Tweet Lambda Invoke」の間に Choice ステートを差し込みます。

クリックすると拡大します

続いて、今差し込んだ Choice ステートの状態名を「Choice - Tweet # is 0」と変更してください。

また、Choice ステートの下が二股に分かれており、おそらく Tweet Lambda Invoke アクションと Success ステートがどちらも Default の方にぶら下がっていると思います。条件を満たしたときにのみツイートする形にするので、Tweet Lambda Invoke アクションをドラッグ & ドロップして、Rule #1 の下にぶら下がるように修正してください。

クリックすると拡大します

このような形のワークフローにできましたでしょうか ? Rule #1 の下に Tweet Lambda Invoke アクション、Default の下に Success ステートが紐付いていれば、左右が反転していても問題ありません。

続いて、条件分岐のルールを定義していきたいので、再び Choice ステートを選択し、右側の Rule #1 の編集マークをクリックしてください。

クリックすると拡大します

Add Conditions」ボタンをクリックすると、Rule #1 に遷移する条件を定義するためのポップアップがでてきます。

クリックすると拡大します

ここで条件を定義するのですが、その前に Step Functions における入出力について触れておきます。Choice ステートには、先ほどの Search Lambda Invoke の結果 JSON がそのまま入力されます。再掲ですが、下記のようなツイート検索の結果が入力されることになります。

{
  "data": [
    {
      "id": "1523764637279301632",
      "text": "6月 祝日 ない なぜ"
    }
  ],
  "meta": {
    "newest_id": "1523764637279301632",
    "oldest_id": "1523764637279301632",
    "result_count": 1
  }
}

こちらの Step Functions のドキュメント に詳細が記載されているのですが、入力された JSON の各要素にアクセスするには、$ から始まる文字列としてパスを定義します。今回は meta の下にある result_count を条件判定に使いたいので、$.meta.result_count という形で値を取得します。

それでは条件を定義していきましょう。先ほど「Add Conditions」ボタンをクリックしたあとに表示されるポップアップに、

  • Variable : $.meta.result_count
  • Operator : is equal to
  • Value : Number constant
  • Value の右側 : 0

と入力し、右下の「条件を保存する」ボタンをクリックしてください。

クリックすると拡大します

さて、元の画面に戻り、一度ワークフローを確認しましょう。こちらのように Choice ステートが定義できていれば設定完了です。

クリックすると拡大します

最後にツイートする文言を指定します。先ほどは、Step Functions 実行時の Input としてツイートしましたが、今回は Lambda Invoke アクションのペイロードとして指定する形に変更します。ここに指定した値が、固定で Lambda Invoke アクションの Input になります。

Tweet Lambda Invoke アクションを選択後、ペイロードのプルダウンから「Enter payload」を選択してください。

クリックすると拡大します

こちらに下記の JSON を指定します。

{
  "tweet_text": "@ketancho 元気ですか?無理せずコーヒーでも飲みながら Twitter しましょう!"
}

以上で、Step Functions 側の修正が完了したので、右上の「適応して終了」をクリックしてください。

クリックすると拡大します

その後、「保存」ボタンをクリックし、ステートマシンを保存しましょう。

クリックすると拡大します

するとこちらのポップアップが出てきます。まさに今回、新しい Lambda 関数を呼び出しているので、Step Functions のステートマシンに紐づく IAM ロールを修正する必要があります。

ここでは「保存を続行します」とし、後ほど IAM ロールを修正します。

クリックすると拡大します

6-2. ステートマシンに紐づく IAM ロールを修正する

IAM ロールの変更はステートマシンの画面から行います。こちらの IAM ロール ARN のリンクをクリックしてください。

クリックすると拡大します

2 つの IAM ポリシーが紐付いていると思いますので、このうち “Lambda” から始まる方の IAM ポリシーをクリックしてください。

クリックすると拡大します

ポリシーの編集」ボタンをクリックします。

クリックすると拡大します

JSON タブから IAM ポリシーを変更します。

許可を与える Resource を示す箇所が 2 箇所あるのですが、皆さまの手元の環境では、twitter-bot-post-tweet-module-function のみが含まれていると思います。これは、Step Functions に紐づく IAM ロールが自動生成されたタイミングでは、Lambda Invoke アクションとして twitter-bot-post-tweet-module-function しか使用していなかったからです。

この 2 箇所それぞれで twitter-bot-post-tweet-module-function の行をまるっとコピーし、直後に貼り付け、リソースを twitter-bot-search-tweet-module-function に変更するように修正してください (画面キャプチャを見ていただくのが分かりやすいと思います)。

修正が完了したら、右下の「ポリシーの確認」ボタンをクリックしてください。その後、ポリシーの確認画面に遷移するので、右下の「変更の保存」ボタンをクリックすると、IAM ロールに紐づく IAM ポリシーのアップデートが完了です。

クリックすると拡大します

6-3. テスト実行する

それでは、作成した Step Functions ステートマシンのテスト実行を行いましょう。先ほどの実行時と同様、ステートマシンへの Input を指定できますが、今回は下記のように指定します。

{
  "search_query": "from:ketancho -is:retweet",
  "search_before_hours": "12"
}

実装がうまくできていれば、このようにオールグリーンになるはずです。

このキャプチャの場合は、直近 12 時間以内にツイートをしている場合の例になるので、デフォルト側、つまり心配ツイートをしない方に分岐しています。

クリックすると拡大します

もし、直近 12 時間以内にツイートをしていなかった場合は、Choice ステートで Tweet Lambda Invoke アクション側に条件分岐されます。

クリックすると拡大します

結果、このように Twitter Bot から励ましのツイートが届いていれば成功です🎉
これで忙しくても頑張れそうですね !

クリックすると拡大します


7. 毎日 21 時に定期実行する設定を行う

最後に、毎日定時になったらステートマシンを実行する設定をします。これまでのドリルと同様、Step Functions の呼び出しにも EventBridge のルールを使うことができます。

ステートマシンのページの右上の「アクション」ボタンをクリックします。その中のメニューから「EventBridge (CloudWatch イベント) ルールを作成」ボタンをクリックします。

クリックすると拡大します

ルールの名前は well-being-bot-rule とし、ルールタイプは「スケジュール」を選択してください。

その後、「次へ」ボタンをクリックします。

クリックすると拡大します

どのタイミングで実行するかを指定します。今回も cron 記法で定義していきますが、毎日 21 時に実行するには、UTC に直すと 12 時を指定する必要がある点だけご注意ください。

なお、この画面では、入力した cron 式に従うと以後どのタイミングでトリガーされるかを10回分確認できます。プルダウンから「ローカルタイムゾーン」を選択すると、JST で確認することもできますので、ぜひご活用ください。

入力が終わりましたら、「次へ」をクリックします。

クリックすると拡大します

続いて、トリガーされたときに何を実行するかを示す「ターゲット」を定義します。

Step Functions ステートマシン」を選択後、皆さまが作成したステートマシン名をプルダウンから選びます。

(まだ「次へ」はクリックしません)

クリックすると拡大します

少し下にスクロールすると、「追加設定」欄があるのでこちらをクリックします。すると、実行時にターゲットに渡す Input を定義できます。

プルダウンから「定数 (JSON テキスト)」を選択し、定数の欄には先程と同じく

{
"search_query": "from:ketancho -is:retweet",
"search_before_hours": "12"
}

こちらの JSON を入力してください。

ここまででターゲットの設定が完了となりますので、右下の「次へ」ボタンをクリックしてください。

クリックすると拡大します

その後はデフォルト値のまま作成を進めるので、「タグを設定」画面はそのまま右下の「次へ」ボタンをクリックし、確認画面でもそのまま右下の「ルールを作成」ボタンをクリックしてください。こちらで EventBridge の設定は完了です。

毎日21時になると Step Functions のステートマシンが実行されることをご確認ください。もし、すぐにテストされたい場合は、このときだけ cron 式を変更し、数分後にトリガーされるようにしてみてもいいかもしれません。こちらで今回のドリルは終了です、お疲れさまでした!🎉


8. 参考情報

はじめての AWS Step Functions はいかがでしたでしょうか ? 個人的に好きなサービスのひとつなので、「おもしろい !」と思っていただけたら嬉しいです。

今回は登場しなかったのですが、次回は直接 AWS サービスを使う機能を使っていくので楽しみにしていただければと思います。が、「今すぐ使いたい !」という方もいらっしゃると思うので、そんな方にぴったりなハンズオンを参考情報として共有します。

Hands-on for Beginners - AWS Step Functions 入門編

オンデマンド形式で配信している Hands-on for Beginners シリーズの、AWS Step Functions 入門編です。今回のドリル同様、Workflow Studio を使ってワークフローを作成するのですが、加えてこのハンズオンでは「AWS SDK 統合」機能を活用し、プログラムを書かずに AWS の各種サービスを利用していきます。「日本語の記事を英訳化 & 音声化する」という内容も楽しいと思いますので、ぜひあわせてご覧いただければ嬉しいです。

クリックすると拡大します


9. まとめ

「お役立ち Twitter Bot を作りながら学ぶ AWS ドリル」連載の第 6 弾として、皆さまの普段づかいのアカウントからのツイートがないときに、心配するお声がけをする Well-being Bot を作りました。これで祝日がない 6 月を乗り越えられそうでしょうか ?

私は祝日のない辛さは乗り越えられる気がしてきたのですが、梅雨のジメジメにはまだ勝てなさそうです。そこで、来月もそんなジメジメを吹き飛ばすような Well-being Bot を作る続編を書いていこうと思います。引き続き、楽しみにしていただければ嬉しいです。

最後に改めてのお願いなのですが、皆さまのご感想・ご要望を #AWSウェブマガジン タグをつけてツイートしていただけたら嬉しいです🙏 この記事に +α して、こんなものを作ってみました ! という共有も大歓迎です。今後の連載の参考にさせてもらいたいと思っておりますので、ぜひよろしくお願いしますー !

それでは次回の記事でまたお会いしましょう !

この連載記事のその他の記事はこちら

選択
  • 選択
  • 第 1 回 おはよう Bot 編
  • 第 2 回 昔書いた記事の宣伝 Bot 編
  • 第 3 回 リファクタリング & 曜日ごとのツイート 編
  • 第 4 回 新章突入 ! 気になるワード検索 & 通知 Bot 編
  • 第 5 回 皆さまの代わりに英語でツイートしておくよ Bot 編
  • 第 6 回 AWS Step Functions を使って Well-being Bot を作ろう ! (前編)
  • 第 7 回 AWS Step Functions を使って Well-being Bot を作ろう ! (後編)
  • 第 8 回 - 総集編 & 夏休みの自由研究のすすめ
  • 第 9 回 - IaC 入門しながら作るリマインダー Bot (前編)
  • 第 10 回 - IaC 入門しながら作るリマインダー Bot (中編)
  • 完結回 - IaC & CI/CD 入門しながら作るリマインダー Bot (後編)

builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

金澤 圭 @ketancho
アマゾン ウェブ サービス ジャパン合同会社
技術統括本部 ソリューションアーキテクト

サーバーレスが好きなソリューションアーキテクト。業種業界問わず、お客様のプロダクト開発をサポートさせていただいています。オンデマンドで視聴できるハンズオン「AWS Hands-on for Beginners」、実際にものづくりをしながら AWS を学ぶ「AWS ドリルシリーズ」を企画・推進しており、楽しく学べるコンテンツを日々考えています。好きなサービスは AWS Lambda、AWS Step Functions、Amazon Personalize で、好きな休日の過ごし方は娘ふたりと川の字になって昼寝👧👧と赤ん坊を抱っこしながらの散歩です👶

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する