お役立ち Twitter Bot を作りながら学ぶ AWS ドリル
~第 10 回 IaC 入門しながら作るリマインダー Bot (中編)
Author : 金澤 圭
ソリューションアーキテクト(SA)の金澤 (@ketancho) です。10 月になりましたが皆さまいかがお過ごしでしょうか ? 先月号 で "スポーツが終盤戦に差し掛かるシーズン” と書きましたが、これからシーズンが始まるスポーツもたくさんありますよね ! 私は駅伝が大好きなので、毎年観るのを楽しみにしています。チームによる戦略の違い、番狂わせな快走、苦楽を共にしたメンバーが繋いできた襷の重みなどなど、見てる側もワクワクドキドキしてしまいます。このドリル連載も最終盤、この記事を含めてあと 2 回で終了です。ゴールまでしっかり襷を運んでいきたいと思っております。(私は記事の締切にドキドキしています。)
さて、前回の記事 では皆さまに Infrastructure as Code (IaC) に挑戦していただきながら、リマインダー Bot を作りました。具体的には、 AWS Serverless Application Model (AWS SAM) を使い、テンプレートから AWS Lambda 関数、Amazon EventBridge ルールを作成しました。これまで手で構築していたものが自動で構築されていく快感 ( ? ) を味わっていただけたのでは ? と思っています。
ただし、ひとつの記事では AWS SAM の触り程度しかできなかったのと、リマインダー Bot もまだ道半ばでしたので、この記事でも AWS SAM を使ったリマインダー Bot 開発の続きをやっていきます。
この連載記事のその他の記事はこちら
- 選択
- 第 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 (後編)
目次
ご注意
本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »
毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。
1. この記事で作る Bot の機能と、とりあげる AWS サービス・機能
前回は、下記の要件でリマインダー Bot を作成しました。
- 「あとで読む」を含む自分のツイートに対して、3 日後に「本当にあとで読みましたか ?」とメンションを送る (3 日後なので、検索がそのまま使えます)
- メンションを 12 時に送る
そして、現状の構成図はこのようになっています。今回の記事では、ここまで構築を進めていただいている前提で開発を進めるので、もしまだ取り組まれていない方は、前回の記事 にまずはチャレンジしてみてください。
そして、今回の記事ではこのリマインダー Bot を下記の動きになるようにアップデートします。
- 前日分の「あとで読む」を含む自分のツイート ID をデータベースに格納しておく (この処理は毎日 0 時過ぎに行う)
- ツイートから 14 日後に そのツイートに対して「本当にあとで読みましたか ?」とメンションを送る (メンションを 12 時に送る)
先ほどまでは「3 日後に」でしたので Twitter の検索 API で情報を取得できたのですが、前回の記事でも触れた通り、この検索 API では 7 日以内のツイートしか取得できません。ですので、「14 日後に」リマインドするために、①「あとで読む」ツイートを検索し、データベースにその情報を格納しておく、②日次でデータベースを確認し、リマインドするタイミングになったツイートがあればリマインドする、という二段階の構成にする必要があるわけです。
そのためにこのような構成を目指します。前回までの構成に加え、Lambda 関数をひとつ追加しています。そして、リマインド対象となるツイート情報を格納するためのデータベースとして Amazon DynamoDB を追加しています。

この記事では、下記の 3 つのステップに分けて実装を進めていきます。
- リマインドする対象のツイート情報を保存する DynamoDB テーブルを作成する
- 前回作成した Lambda 関数 twitter-bot-send-reminder-function をアップデートし、DynamoDB テーブルからリマインド対象のツイート情報を取得し、リマインドツイートする形に変更する
- Lambda 関数 twitter-bot-find-tweets-to-remind-function を新規に作成し、「あとで読む」ツイートを探し、DynamoDB テーブルに格納するようにする
完成版の Bot の動きを時系列に並べ替えると、3. の Lambda 関数が実行される → 1. の DynamoDB テーブルにデータが挿入される → 2. の Lambda 関数が実行されるとなり、この記事の作業順とは異なる点だけ最初にご認識いただければと思います。それでは少しずつやっていきましょう !
2. AWS Cloud9 環境を起動し直す
それでは作業を開始していきます。前回に引き続き、AWS Cloud9 を使っていきます。前回の記事で作成したものをそのまま使いたいので、今回は新しく環境を作るのではなく、環境を再開させていきます。
AWS Cloud9 が利用できない場合、こちらのブログ をご参考に AWS IDE Toolkits または AWS CloudShell をご利用ください。
AWS Cloud9 のホーム画面 に移動すると、このような状態になっていると思います。
こちらの「Open IDE」ボタンをクリックしましょう。前回の作業から間が空いている場合、“EC2 instance is stopped. Attempting to start instance...” というメッセージが出て少しお待ちいただく形になります。
少し待つと AWS Cloud9 環境が再起動してくると思います。その際、ターミナルのカレントディレクトリが、~/environment に戻っていると思います。ビルドしたりデプロイしたりする作業は、前回作成した aws-drill-reminder-bot ディレクトリで行う必要がありますので、
$ cd ~/environment/aws-drill-reminder-bot/
としてディレクトリを移動しておきましょう。
3. AWS SAM を用いて DynamoDB テーブルを追加する
では DynamoDB テーブルを作成するように template.yaml を修正していきます。DynamoDB は AWS ドリル 第 2 回 にて使用した以来ですが、皆さま DynamoDB の特徴やテーブルの作り方は覚えておりますでしょうか ? もし、復習しておきたい方は こちらの記事 もあわせてご確認いただければと思います。
今回は、下記の設定で DynamoDB テーブルを構築していきます。
- テーブル名 : TweetsToRemindTable
- パーティションキー : RemindDate
- ソートキー : TweetId
パーティションキーの RemindDate には、そのツイートをいつリマインドするかを YYYYMMDD 形式で保持する想定です。検索にヒットしたツイートが見つかったら、そのツイートから 14 日後の日付を入れるイメージになります。TweetId にはツイートごとにユニークに割り当てられる ID を格納します。具体的にはこちらのような項目が挿入される形になります。

3-1. template.yaml を修正する
では、前回作成した template.yaml に、DynamoDB テーブルリソースの記述を追加します。前回は、AWS リソースとして Lambda 関数 AWS::Serverless::Function を使用しました。DynamoDB テーブルも、AWS SAM の機能としては AWS::Serverless::SimpleTable というリソースが用意されており、こちらを用いることでシンプルな DynamoDB テーブルを作成できます。
しかし、今回の DynamoDB テーブルを作る際に細かい設定をしたいのですが、AWS::Serverless::SimpleTable では一部の定義ができません。そこで、AWS::DynamoDB::Table という AWS CloudFormation のリソースを使って定義します。前回の記事でもご紹介しましたが、AWS SAM は AWS CloudFormation の拡張で、AWS CloudFormation で用意されているすべてのリソースも使用できますので、今回はこれを使っていきます。
aws-drill-reminder-bot ディレクトリの直下にある template.yaml を下記のように修正します。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
aws-drill-reminder-bot
Globals:
Function:
Timeout: 3
Resources:
SendReminderFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: twitter-bot-send-reminder-function
CodeUri: twitter-bot-send-reminder-function/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- AmazonSSMReadOnlyAccess
Events:
SendReminderEvent:
Type: Schedule
Properties:
Schedule: 'cron(0 3 * * ? *)'
Name: SendReminderEvent
Enabled: True
# DynamoDB に関する記述を追加(ここから)
TweetsToRemindDynamoDbTbl:
Type: AWS::DynamoDB::Table
Properties:
TableName: TweetsToRemindTable
AttributeDefinitions:
-
AttributeName: "RemindDate"
AttributeType: "S"
-
AttributeName: "TweetId"
AttributeType: "S"
KeySchema:
-
AttributeName: 'RemindDate'
KeyType: 'HASH'
-
AttributeName: 'TweetId'
KeyType: 'RANGE'
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
# DynamoDB に関する記述を追加(ここまで)
各項目の細かい説明は AWS::DynamoDB::Table のドキュメント に譲りますが、今回テンプレートに追加した内容としては、
- テーブルの名前
- 属性 (Attribute) として何を用意するか
- テーブルのキーはどの属性 (Attribute) にするか
- テーブルの Read / Write Capacity Unit をどうするか
を定義しています。テンプレートの修正がおわりましたら、忘れずにファイルを保存してください。
3-2. ビルドとデプロイを行い、DynamoDB テーブルができていることを確認する
それでは、ビルドとデプロイを行います。
$ sam build
$ sam deploy
デプロイコマンドを実行し、少し待つと Change Set が表示されるはずです。
$ sam deploy
(中略)
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
---------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add TweetsToRemindDynamoDbTbl AWS::DynamoDB::Table N/A
---------------------------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:981766141304:changeSet/samcli-deploy1662856992/46b5cce0-8682-4959-8389-8a4cbe7a2108
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]:
DynamoDB テーブルが新たに追加されることを確認したのち、y と入力してください。少しお待ちいただくと、デプロイが完了したメッセージが表示されるはずです。
Deploy this changeset? [y/N]: y
2022-09-09 06:51:07 - Waiting for stack create/update to complete
CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::DynamoDB::Table TweetsToRemindDynamoDbTbl -
CREATE_IN_PROGRESS AWS::DynamoDB::Table TweetsToRemindDynamoDbTbl Resource creation Initiated
CREATE_COMPLETE AWS::DynamoDB::Table TweetsToRemindDynamoDbTbl -
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS AWS::CloudFormation::Stack aws-drill-reminder-bot -
UPDATE_COMPLETE AWS::CloudFormation::Stack aws-drill-reminder-bot -
---------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - aws-drill-reminder-bot in ap-northeast-1
DynamoDB テーブルが作られているかをマネージメントコンソールで確認しましょう。DynamoDB のテーブル一覧画面 に遷移します。このように TweetsToRemindTable ができていれば無事デプロイが成功しています🎉
4. 前回作成した AWS Lambda 関数 twitter-bot-send-reminder-function をアップデートする
DynamoDB テーブルの準備ができたので、続いてリマインドツイートをする Lambda 関数 twitter-bot-send-reminder-function をアップデートします。前回の記事の時点では、この関数は
- 検索条件に合う「あとで読む」ツイートがあるか Twitter 検索する
- 該当のツイートがあれば、それに対して「読んだ ?」とリプライを飛ばす
の 2 つの役割を持っていました。
しかし、この記事では、前者の役割を後ほど新規作成する Lambda 関数に譲る形とし、
- その当日にリプライを飛ばすべき過去ツイートがあるか DynamoDB を検索する
- 該当のツイートがあれば、それに対して「読んだ?」とリプライを飛ばす
という役割を担う形に修正します。後者は元々あった機能をそのまま使えるので、前者のリマインド対象の取得部分を修正していきましょう。
4-1. Lambda 関数 twitter-bot-send-reminder-function をアップデートする
それでは、Lambda 関数 twitter-bot-send-reminder-function を修正します。twitter-bot-send-reminder-function ディレクトリ配下の app.py をエディタで開きます。
前回の記事で、リマインド対象ツイートは Twitter API を叩く形で取得していました。該当部分を再掲します。
def lambda_handler(event, context):
init()
# ★Twitter API を叩いて検索している部分(ここから)
search_query = 'あとで読む from:ketancho -is:retweet'
start_time = (datetime.now() + timedelta(days=-4)).strftime('%Y-%m-%dT15:00:00Z')
end_time = (datetime.now() + timedelta(days=-3)).strftime('%Y-%m-%dT15:00:00Z')
json_response = get_matching_tweets(search_query, start_time, end_time)
if('data' not in json_response):
return
# ★Twitter API を叩いて検索している部分(ここまで)
matched_tweets = json_response['data']
for matched_tweet in matched_tweets:
reply("@ketancho あとで読みましたか?", matched_tweet['id'])
今回は、既に DynamoDB テーブルにリマインド対象のツイート情報が格納されていることを前提とし、テーブルからデータを取得する形に変更します。最終的なソースコードは後ほど紹介しますが、先にポイントだけ紹介します。
まず、DynamoDB にクエリを投げる下準備をします。こちらのページ が参考になるので、参照いただきたいのですが、lambda_handler メソッドの外側に下記のコードを追加します。
from boto3.dynamodb.conditions import Key
dynamodb_tweets_to_remind_tbl = boto3.resource('dynamodb').Table('TweetsToRemindTable')
そして、元々 検索 API を呼んでいた部分を変更し、DynamoDB テーブルに対して Lambda 関数が実行された当日日付をキーにクエリするように修正します。
today_yyyymmdd = datetime.now().strftime('%Y%m%d')
json_response = dynamodb_tweets_to_remind_tbl.query(
KeyConditionExpression=Key('RemindDate').eq(today_yyyymmdd)
)
1点だけ注意点を共有します。当日日付を表す today_yyyymmdd ですが、こちらは UTC 時間での現在日付になります。今回、この Lambda 関数は日本時間で 12 時に実行する設定にするため、UTC でも 同日の 3 時になり、日付としては同じ値になります。もし、日本時間の 9 時より前にこのリマインド処理を実行したい場合は同じやり方だと 1 日ずれてしまうので、調整してください。
そして、データが取れたあとの処理ですが、前回と同様、該当するツイートがなければ終了、あればメンションするという形は変わりません。ただし、データの格納のされ方が少し違うので、この点だけ微修正します。
私は下記のように実装してみました。リプライを送る先は皆様の Twitter アカウントに変更する点だけお忘れなきようお願いいたします。
import json
from requests_oauthlib import OAuth1Session
from datetime import datetime, timedelta
import boto3
ssm_client = boto3.client('ssm')
# 追加(ここから)
from boto3.dynamodb.conditions import Key
dynamodb_tweets_to_remind_tbl = boto3.resource('dynamodb').Table('TweetsToRemindTable')
# 追加(ここまで)
oauth = None
def lambda_handler(event, context):
init()
# 修正(ここから)
today_yyyymmdd=datetime.now().strftime('%Y%m%d')
json_response = dynamodb_tweets_to_remind_tbl.query(
KeyConditionExpression=Key('RemindDate').eq(today_yyyymmdd)
)
if ('Items' not in json_response):
return
target_tweets = json_response['Items']
for target_tweet in target_tweets:
reply("@ketancho あとで読みましたか?", target_tweet['TweetId']) #FIXME: 皆さまの Twitter アカウントに変更
# 修正(ここまで)
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)
# get_matching_tweets 関数は使用しなくなるので削除
def reply(text, target_tweet_id):
payload = {
'text': text,
'quote_tweet_id': target_tweet_id,
'reply': {
'in_reply_to_tweet_id': target_tweet_id
}
}
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)
)
また、この実装の中で DynamoDB に対してクエリを投げるので、Lambda 関数 twitter-bot-send-reminder-function に紐づく IAM ロールに対して、DynamoDB テーブルにアクセスを許す IAM ポリシーを付与する必要があります。本来、最小権限になるように IAM ロールを定義すべきですが、今回は標準で用意された AmazonDynamoDBFullAccess ポリシーをアタッチするように、template.yaml を修正します。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
aws-drill-reminder-bot
Globals:
Function:
Timeout: 3
Resources:
SendReminderFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: twitter-bot-send-reminder-function
CodeUri: twitter-bot-send-reminder-function/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- AmazonSSMReadOnlyAccess
- AmazonDynamoDBFullAccess # 1行追加
Events:
SendReminderEvent:
Type: Schedule
Properties:
Schedule: 'cron(0 3 * * ? *)'
Name: SendReminderEvent
Enabled: True
TweetsToRemindDynamoDbTbl:
Type: AWS::DynamoDB::Table
Properties:
TableName: TweetsToRemindTable
AttributeDefinitions:
-
AttributeName: "RemindDate"
AttributeType: "S"
-
AttributeName: "TweetId"
AttributeType: "S"
KeySchema:
-
AttributeName: 'RemindDate'
KeyType: 'HASH'
-
AttributeName: 'TweetId'
KeyType: 'RANGE'
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
ここまでできたら、再度ビルド & デプロイします。
$ sam build
$ sam deploy
表示される変更点を確認し、y と入力してください。少しお待ちいただくと、デプロイが完了したメッセージが表示されるはずです。
$ sam deploy
(中略)
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
---------------------------------------------------------------------------------------------------------------------------------------------------------
* Modify SendReminderFunctionRole AWS::IAM::Role False
* Modify SendReminderFunctionSendReminderEven AWS::Lambda::Permission Conditional
tPermission
* Modify SendReminderFunctionSendReminderEven AWS::Events::Rule False
t
* Modify SendReminderFunction AWS::Lambda::Function False
---------------------------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:675174999332:changeSet/samcli-deploy1662711586/bf11e62e-b349-4d81-9f62-33762df4118c
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2022-09-09 08:21:57 - Waiting for stack create/update to complete
CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS AWS::IAM::Role SendReminderFunctionRole -
UPDATE_COMPLETE AWS::IAM::Role SendReminderFunctionRole -
UPDATE_IN_PROGRESS AWS::Lambda::Function SendReminderFunction -
UPDATE_COMPLETE AWS::Lambda::Function SendReminderFunction -
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS AWS::CloudFormation::Stack aws-drill-reminder-bot -
UPDATE_COMPLETE AWS::CloudFormation::Stack aws-drill-reminder-bot -
---------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - aws-drill-reminder-bot in ap-northeast-1
以上で、Lambda 関数 twitter-bot-send-reminder-function の修正は完了です。
4-2. Lambda 関数 twitter-bot-send-reminder-function が正しく動作するかを確認する
デプロイが上手くいきましたら、期待する動きになっているか確認していきましょう。この Lambda 関数の動きを確認するには DynamoDB テーブルに項目を追加する必要がありますが、今の時点でテーブルは空だと思います。そこで、テスト用の項目を追加しましょう。
皆さまの普段づかいの Twitter アカウントから、お試しでリプライを送るツイートを選んでください。そのツイートの詳細ページに飛ぶと URL の末尾に 20 桁前後の数字が含まれているはずで、こちらがツイート ID になります。例えばこの例だと 1568446822561247240 です。
このツイート ID を DynamoDB に格納します。DynamoDB のサービス画面を開き、「項目を探索」、「TweetsToRemindTable」、「項目を作成」の順にクリックします。
RemindDate には YYYYMMDD 形式でこの作業を行っている当日日付 (ただし、UTC での日付になるので、日本時間の 0 時から 9 時までの間に実装している場合は前日の日付)、TweetId には先ほど調べたテスト対象のツイート ID を入力し、「項目を作成」をクリックしてください。
DynamoDB に項目を追加したら、Lambda 関数 twitter-bot-send-reminder-function を実行してみましょう。先ほど対象としたツイートに対して、このようにリマインドされれば成功です🎉
5. Lambda 関数 twitter-bot-*find-tweets-to-remind*-function を新規に作成する
DynamoDB にリマインド対象のツイート情報が入れられればリマインドできるところまで来ました。ここからは今回のドリルの仕上げとして、Twitter 検索し、ヒットした「あとで読む」ツイートの情報を DynamoDB テーブルに格納する Lambda 関数 twitter-bot-find-tweets-to-remind-function を作成します。検索する部分は前回のドリルの実装をそのまま利用することができます。
まず、AWS SAM で Lambda 関数を新規作成するための、ディレクトリとファイル群を作成します。これは前回のドリルでも行った作業ですね。このように Lambda 関数ごとにディレクトリや Python ファイルを用意します。
$ cd ~/environment/aws-drill-reminder-bot/
$ mkdir twitter-bot-find-tweets-to-remind-function
$ touch twitter-bot-find-tweets-to-remind-function/app.py
$ touch twitter-bot-find-tweets-to-remind-function/requirements.txt
作成した twitter-bot-find-tweets-to-remind-function ディレクトリの下の app.py に、Lambda 関数のコードを実装していきます。DynamoDB テーブルへの挿入はこのドリルシリーズでは初めてになりますが、こちらの put_item 関数 を使います。
また、要件として 14 日後にリマインドする必要があるので、本来 remindDate には 14 日後の日付を YYYYMMDD 形式で入れるべきなのですが、これだとテスト実行で 14 日待つ必要があります。まずは稼働確認をしたいので、1 日後の日付を DynamoDB テーブルに挿入してみましょう。
この実装部分は腕試しとしてチャレンジしてみていただきたいので、時間を取って取り組んでみてください。
いかがでしょうか ? 私は下記のように実装してみました。途中の search_query の部分は、皆様の Twitter アカウントに置き換える必要があることにご注意ください。
import json
from requests_oauthlib import OAuth1Session
from datetime import datetime, timedelta
import boto3
ssm_client = boto3.client('ssm')
dynamodb_tweets_to_remind_tbl = boto3.resource('dynamodb').Table('TweetsToRemindTable')
oauth = None
def lambda_handler(event, context):
init()
search_query = 'あとで読む from:ketancho -is:retweet' #FIXME: 皆さまの Twitter アカウントに変更
start_time = (datetime.now() + timedelta(days=-1)).strftime('%Y-%m-%dT15:00:00Z')
end_time = datetime.now().strftime('%Y-%m-%dT15:00:00Z')
json_response = get_matching_tweets(search_query, start_time, end_time)
if('data' not in json_response):
return
matched_tweets = json_response['data']
for matched_tweet in matched_tweets:
tweet_text_id = matched_tweet['id']
dynamodb_tweets_to_remind_tbl.put_item(
Item = {
"RemindDate": (datetime.now() + timedelta(days=+1)).strftime('%Y%m%d'), # テストのために1日後の日付にする
"TweetId": tweet_text_id
}
)
return json_response
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()
また、twitter-bot-find-tweets-to-remind-function ディレクトリの下の requirements.txt には、
requests_oauthlib
と記入していただき、保存してください。
最後に、template.yaml の修正です。Lambda 関数ですので AWS::Serverless::Function タイプのリソースを追加します。この Lambda 関数も、Parameter Store と DynamoDB へのアクセスが必要なので適切な権限を付与します。また、毎日 0 時 5 分に起動させる設定も必要です。template.yaml の修正もぜひ挑戦してみてください。
いかがでしょうか ? 私は下記のようにアップデートしました。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
aws-drill-reminder-bot
Globals:
Function:
Timeout: 3
Resources:
# 追加(ここから)
FindTweetsToRemindFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: twitter-bot-find-tweets-to-remind-function
CodeUri: twitter-bot-find-tweets-to-remind-function/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- AmazonSSMReadOnlyAccess
- AmazonDynamoDBFullAccess
Events:
FindTweetsToRemindEvent:
Type: Schedule
Properties:
Schedule: 'cron(5 15 * * ? *)'
Name: FindTweetsToRemindEvent
Enabled: True
# 追加(ここまで)
SendReminderFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: twitter-bot-send-reminder-function
CodeUri: twitter-bot-send-reminder-function/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- AmazonSSMReadOnlyAccess
- AmazonDynamoDBFullAccess
Events:
SendReminderEvent:
Type: Schedule
Properties:
Schedule: 'cron(0 3 * * ? *)'
Name: SendReminderEvent
Enabled: True
TweetsToRemindDynamoDbTbl:
Type: AWS::DynamoDB::Table
Properties:
TableName: TweetsToRemindTable
AttributeDefinitions:
-
AttributeName: "RemindDate"
AttributeType: "S"
-
AttributeName: "TweetId"
AttributeType: "S"
KeySchema:
-
AttributeName: 'RemindDate'
KeyType: 'HASH'
-
AttributeName: 'TweetId'
KeyType: 'RANGE'
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
ファイルの保存を忘れずに行い、再度ビルド & デプロイします。
$ sam build
$ sam deploy
表示される変更点を確認し、y と入力してください。少しお待ちいただくと、デプロイが完了したメッセージが表示されるはずです。
$ sam deploy
Uploading to aws-drill-reminder-bot/f1995df3e16464bfa1b3c157e0a0dd8a 641024 / 641024 (100.00%)
File with same data already exists at aws-drill-reminder-bot/f0bbe53823be2f63da5d9fa62169a404, skipping upload
Deploying with following values
===============================
Stack name : aws-drill-reminder-bot
Region : ap-northeast-1
Confirm changeset : True
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-kp9g03ds7rpt
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
Uploading to aws-drill-reminder-bot/63b7cbe646a578fd84c792e2850308e8.template 1858 / 1858 (100.00%)
Waiting for changeset to be created..
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
---------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add FindTweetsToRemindFunctionFindTweets AWS::Lambda::Permission N/A
ToRemindEventPermission
+ Add FindTweetsToRemindFunctionFindTweets AWS::Events::Rule N/A
ToRemindEvent
+ Add FindTweetsToRemindFunctionRole AWS::IAM::Role N/A
+ Add FindTweetsToRemindFunction AWS::Lambda::Function N/A
---------------------------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:981766141304:changeSet/samcli-deploy1662871651/a7b5fd2e-eb3f-43d8-9dad-16ef9fb274b1
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2022-09-11 04:47:46 - Waiting for stack create/update to complete
CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::IAM::Role FindTweetsToRemindFunctionRole -
CREATE_IN_PROGRESS AWS::IAM::Role FindTweetsToRemindFunctionRole Resource creation Initiated
CREATE_COMPLETE AWS::IAM::Role FindTweetsToRemindFunctionRole -
CREATE_IN_PROGRESS AWS::Lambda::Function FindTweetsToRemindFunction -
CREATE_IN_PROGRESS AWS::Lambda::Function FindTweetsToRemindFunction Resource creation Initiated
CREATE_COMPLETE AWS::Lambda::Function FindTweetsToRemindFunction -
CREATE_IN_PROGRESS AWS::Events::Rule FindTweetsToRemindFunctionFindTweets -
ToRemindEvent
CREATE_IN_PROGRESS AWS::Events::Rule FindTweetsToRemindFunctionFindTweets Resource creation Initiated
ToRemindEvent
CREATE_COMPLETE AWS::Events::Rule FindTweetsToRemindFunctionFindTweets -
ToRemindEvent
CREATE_IN_PROGRESS AWS::Lambda::Permission FindTweetsToRemindFunctionFindTweets -
ToRemindEventPermission
CREATE_IN_PROGRESS AWS::Lambda::Permission FindTweetsToRemindFunctionFindTweets Resource creation Initiated
ToRemindEventPermission
CREATE_COMPLETE AWS::Lambda::Permission FindTweetsToRemindFunctionFindTweets -
ToRemindEventPermission
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS AWS::CloudFormation::Stack aws-drill-reminder-bot -
UPDATE_COMPLETE AWS::CloudFormation::Stack aws-drill-reminder-bot -
---------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - aws-drill-reminder-bot in ap-northeast-1
AWS Lambda の画面にて、Lambda 関数 twitter-bot-find-tweets-to-remind-function が作成されていることをご確認ください。その後、皆さまの普段遣いの twitter アカウントで「あとで読む」ツイートを行い、その翌日の 0 時 5 分にこの Lambda 関数が動き、DynamoDB テーブルに項目が追加されることを確認してみてください。
なお、手動で Lambda 関数を動かすことでこの確認はできるのですが、Twitter の検索 API に渡すパラメータ end_time の「API リクエスト時間より 10 秒以上前であること」という制約 (詳細は、AWS ドリル 第 5 回 の 7. 落ち穂拾い & 参考情報 を参照してください) にぶつかる可能性があります。この場合は、「'end_time' must be a minimum of 10 seconds prior to the request time.」というエラーが出るので、もしこのエラーに遭遇した場合は、検索条件のパラメーターを調整するなどの対応が必要になります。
そして、DynamoDB テーブルに項目が追加されると、その日のお昼にリマインドリプライが来るはずです。
リマインド Bot がうまく動くすることが確認できましたら、Lambda 関数 twitter-bot-find-tweets-to-remind-function を再度修正し、14 日後にリマインドされるようにしてみてください。この作業は次回のドリルでも扱うので、しばらくはこのまま運用していただいても構わないです😉
以上で、今回の AWS ドリルの実装は完了になります ! お疲れさまでした🎉
6. まとめ
「お役立ち Twitter Bot を作りながら学ぶ AWS ドリル」連載の第 10 弾として、前回の記事から作成を開始したリマインダー Bot を完成させました。このおせっかいな Bot によって、皆さまの学習ライフがよりよいものになることを願っています😉
「リマインダー Bot を完成」と書きましたが、タイトルは「中編」となっているのに気付かれた方もいらっしゃるかもしれません。このリマインダー Bot の機能としては完成したのですが、今後運用するにあたって最後に継続的インテグレーション (Continuous Integration; CI)、継続的デリバリー (Continuous Delivery; CD) な仕組みを追加しておこうと思います。この CI/CD パイプラインの構築をご体験いただき、この AWS ドリル完走 ! としたいと思いますので、来月の最終回も楽しみにしていただければ嬉しいです。
また、次回で最終回ということで、来年以降の記事についてもボチボチ考えていきたいと思っています。皆さまのご感想・ご要望に加え、来年以降こんな連載があれば読んでみたいというご希望がございましたら、#AWSウェブマガジン タグをつけてツイートしていただきたいです🙏 もし、パブリックに書くのが嫌だな.. という方がいらっしゃいましたら、DM 開放しておりますので @ketancho まで直接投げつけていただいても構いませんー ! ぜひお待ちしております。
それではまた来月お会いしましょう ! 🙌
この連載記事のその他の記事はこちら
- 選択
- 第 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 (後編)
筆者プロフィール

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