はじめに
AWS初心者のはち子です。普段の業務では、AWSシステムの要件定義/企画/開発をしています。
プログラミングの勉強は自分で好きなアプリを作るのが一番ということで・・・
普段の生活の不満を解消するアプリをAWS Lambda / Node.jsを使って作りました
所要時間は、休日を利用し丸4日ほどです。
この記事では、私と同じようにAWS勉強中の方が、Lambdaでアプリを作ってみようかなと思った時の参考になるよう、実施内容を共有します。
自分で考えたものが動くのはとても楽しいので、ぜひ本記事を応用して、何か作ってみてください!
どんなアプリケーション?
Twitterを監視し、特定のキーワードが急増したらスマホに通知してくれるアプリケーションです。
自分の通勤路線は人身事故が多く、朝、駅に行ってみると運転を見合わせていた・・・ということがしばしばあります。家を出る前に、この状況に気づければ、会社に早々に連絡して、テレワーク宣言をして、ゆっくり朝ごはんを食べることができます。
そこで、Twitterで最寄り路線に関するツイートが急増した場合に通知をしてくれるアプリを作成しました。
利用する技術
AWSのマネージドサービスを使います。
- AWS Lambda ー サーバレスでアプリを実行するために利用します。言語はNode.jsを使います。
- Amazon SNS - SMSを飛ばすために使います。
- Amazon EventBridge ー Lambda関数を定期的に自動実行するために使います。
AWS以外では以下の技術を使います。
- Node.js ー バックエンドをJavaScriptで書けるようにした言語。Lambdaでは、一般的にソースコードはPythonかNode.jsで書きます。
- Twitter API ー Twitter社がREST-APIを提供しており、好きなクエリを作ってツイート情報を取得することができます。
システム仕様
システム構成図
システム仕様
- 「山手線」という言葉が含まれているツイートを検索します。
- 現在の時刻~30分前のツイートを取得します。(例:2022/6/19 19:00~19:30)
- 昨日の、今の時刻~30分前のツイートを取得します。(例:2022/6/18 19:00~19:30)
- 今日のツイート数が昨日の同じ時間のツイート数の5倍以上の場合、SMS通知を行ないます。
- この処理を15分おきに行ないます。
残念ながら山手線沿線に住んでいるわけではないです
TwitterAPIでツイート数を取得する
ここから実際の開発に入ります。
TwitterAPIのDeveloper申請
TwitterのAPIを利用するには、Developer申請して、アクセスキーを手に入れる必要があります。
方法は以下記事にまとめています。
TwitterAPIの叩き方
このアプリでは、Tweet counts APIという、期間を指定して特定のツイート数を確認できるAPIを利用します。
Twitter APIの叩き方は以下の記事にまとめています。
試しにcurlコマンドで叩いてみましょう。
GETメソッドを利用します。
※curlコマンドだと日本語のコマンドが簡単に扱えないので、ここではquery=JR
とします。
$ curl --request GET 'https://api.twitter.com/2/tweets/counts/recent?start_time=2022-06-17T10:40:00.000Z&end_time=2022-06-17T10:45:00.000Z&granularity=day&query=JR' --header 'Authorization: Bearer {Bearer Tokenの値}'
Bearer Tockenの値の取得方法はAPIアクセスキーをメモする。を参照してください。
応答が返却されます。
{
"data": [
{
"end": "2022-06-17T10:45:00.000Z",
"start": "2022-06-17T10:40:00.000Z",
"tweet_count": 478
}
],
"meta": { "total_tweet_count": 478 }
}
tweet_count
でツイート数を取得できます。ツイート数が増えたら通知する仕組みを作ります。
Node.jsでアプリを作成する
開発環境
- VurtualBox 6.0
- オペレーティングシステム Ubuntu(64-bit)
初期設定
ローカルでNode.jsアプリを作成します。
$ npm init -y
$ touch index.js
$ npm install
GitHubとも連携しておきます。
$ git init
$ git remote add origin git@github.com:<ユーザ名>/<リポジトリ名>.git
$ git branch -m main
$ git add .
$ git commit -m "initial commit"
$ git push origin main
モジュールインストール
3つのモジュールが必要です。
axios
: REST-API(Twitter API)を叩くために必要です。dotenv
: 環境変数を利用するために必要です。Twitter APIのアクセスキーは環境変数として管理します。@aws-sdk/client-sns
:SNSを関数から操作するために必要です。
$ npm install axios dotenv @aws-sdk/client-sns
TwitterのAPIから情報を取得する関数
utils.js
のファイルを作成して、getTweetCount
関数を作成します。
開始時刻と、終了時刻と、検索文字列を与える関数にします。
const axios = require("axios");
require("dotenv").config();
module.exports.getTweetCount = async (startTime, endTime, query) => {
// startTimeの書式:"2022-05-12T22:00:00.000Z"
// endTimeの書式:"2022-05-13T00:00:00.000Z"
// queryの書式:"池袋線"
const url = "https://api.twitter.com/2/tweets/counts/recent";
const granularity = "day";
const token = process.env.TOKEN;
console.log(`startTime:${startTime}`)
console.log(`endTime:${endTime}`)
const req = encodeURI(
`${url}?query=${query}&start_time=${startTime}&end_time=${endTime}&granularity=${granularity}`
);
try {
const res = await axios.get(req, { headers: { Authorization: `Bearer ${token}` } });
return res.data;
} catch (error) {
console.log(error);
}
};
トークンの取得
Twitter APIにはAPIアクセスキーが必要ですが、秘密情報はGitHub等にpushしてはいけないので、環境変数化します。
TOKEN=Twitterから払いだされたAPIキー
※.gitignore
に.env
が含まれることを忘れずに確認する。
dotenv
モジュールを使って環境変数を利用できます。
axiosを利用したAPI要求
axios
モジュールを利用します。
axios.get(<APIのURL>, <ヘッダーなどのconfig>)
という構文になっています。
JavaScriptの非同期関数
axios
を利用する場合は、関数全体をasync
で囲みます。
get
文を投げる処理にはawait
を付けます。
こうすることで、APIから情報を取得し終わる前にreturn
してしまうことを防げます。
TwitterAPIにリクエストする情報の準備
開始時刻と終了時刻を取得して、getTweetCount
に渡します。
今日のツイート数と昨日のツイート数を取得するので、getTweetCount
は2回実行します。
※きれいなコードではないので参考程度に。
const { setDateTime, getTweetCount } = require("./utils/utils");
const index = () => {
const now = new Date();
const now1mAgo = setDateTime(now, 0, 1);
const yesterday = setDateTime(now, 1, 1);
const now30mAgo = setDateTime(now, 0, 31);
const yesterday30mAgo = setDateTime(now, 1, 31);
const todayData = await getTweetCount(
now30mAgo.toISOString(),
now1mAgo.toISOString(),
"山手線"
);
const yesterdayData = await getTweetCount(
yesterday30mAgo.toISOString(),
yesterday.toISOString(),
"山手線"
);
const todayCount = todayData.data[0].tweet_count;
const yesterdayCount = yesterdayData.data[0].tweet_count;
};
Twitter APIに入れる時刻パラメータはISO 8601形式にする必要があるため、toISOString()
を利用して変換しています。
utils.js
の中にsetDateTime
関数を追加します。
現在時刻から、day
の日付分、minite
の分数分ずらした時刻オブジェクトを取得する関数です。
module.exports.setDateTime = (datetime, day = 0, minute = 0) => {
return new Date(
datetime.getFullYear(),
datetime.getMonth(),
datetime.getDate() - day,
datetime.getHours(),
datetime.getMinutes() - minute,
datetime.getSeconds(),
datetime.getMilliseconds()
);
};
今日のツイート数は、「30分前~現在時刻」にしたかったのですが、現在時刻にすると
"Invalid 'end_time':'2022-06-19T10:45Z'. 'end_time' must be a minimum of 10 seconds prior to the request time."
というエラーが発生します。
ent_time
は現在から10秒以上前にする必要があります。
そこで今回は1分前~31分前のツイートを取得する方法をとります。
カウント数は、オブジェクトから以下のように取り出すことができます。
const todayCount = todayData.data[0].tweet_count;
const yesterdayCount = yesterdayData.data[0].tweet_count;
条件式に従ってSMS通知(Amazon SNS)
sns
関数を作成し、aws-sdkの記法に従ってメッセージの送信命令を出せるようにします。
TwitterからSMSを送る方法は以下の記事にまとめています。
const REGION = "ap-northeast-1" //e.g. "us-east-1"
const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns") // npm install @aws-sdk/client-sns が必要
module.exports.sns = async (todayCount, yesterdayCount) => {
const client = new SNSClient({ region: REGION })
const params = {
Message: `ツイート数が増えました。今日のカウント数:${todayCount}。昨日のカウント数:${yesterdayCount}` ,
TopicArn: "arn:aws:sns:ap-northeast-1:<AWSアカウント>:ay-s-topic-test",
}
const command = new PublishCommand(params)
try {
const data = await client.send(command)
console.log(data)
} catch (error) {
console.log(`error: ${error}`)
} finally {
}
}
sns
関数をindex.js
から呼び出します。
const { sns } = require("./utils/sns"); // ロール設定忘れずに。const { setDateTime, getTweetCount } = require("./utils/utils");
const index = async () => {
const now = new Date();
const now1mAgo = setDateTime(now, 0, 1);
const yesterday = setDateTime(now, 1, 1);
const now30mAgo = setDateTime(now, 0, 31);
const yesterday30mAgo = setDateTime(now, 1, 31);
const todayData = await getTweetCount(
now30mAgo.toISOString(),
now1mAgo.toISOString(),
"山手線"
);
const yesterdayData = await getTweetCount(
yesterday30mAgo.toISOString(),
yesterday.toISOString(),
"山手線"
);
const todayCount = todayData.data[0].tweet_count;
const yesterdayCount = yesterdayData.data[0].tweet_count;
console.log(`今日のカウント:${todayCount}`);
console.log(`昨日のカウント:${yesterdayCount}`);
if (yesterdayCount !== 0) {
const growthRate = todayCount / yesterdayCount;
if (growthRate > 5) {
await sns(todayCount, yesterdayCount); }
return growthRate;
} else {
return 0;
}
};
module.exports.handler = index;
このとき、
module.exports.handler = index;
はLambdaでindex関数を実行するための記述です。
非同期処理
client.send(command)
の処理にはawait
を付けます。そのため関数全体もasync
で囲む必要があります。
メッセージの内容
メッセージに、今日のツイート数と昨日のツイート数を出力したかったため、todayCount
とyesterdayCount
を引数で受け取っています。
LambdaでNode.jsアプリを実行する
Lambda関数を作成し、作成したソースコードをアップロードします。
具体的なやりかたは以下の記事を参照。
Lambda関数にIAMロールを設定するのも忘れないようにします。
EventBridgeを使ってLambdaを定期実行する
AWSのマネジメントコンソールより、EventBridgeのルール作成します。
Lambda関数にトリガーとして設定します。
具体的なやり方は以下の記事にまとめています。
実行結果の確認
最寄りの路線(ここでは山手線)関連のツイートが増えると、スマホに通知が来るようになります。
人身事故があったときは、通知が15分置きに来ます。
2日連続で人身事故があったら通知されないじゃないか、とか、課題はいろいろありますがひとまず完成です。
つまづいたところ
簡単なアプリですが、、全ての工程でつまづきました。
- TwitterAPIを使う準備
- 開発者申請が必要
- ISO 8601形式の時刻指定を間違えた(協定世界時(UTC)と日本標準時(JST))
- 環境変数の勉強が必要だった
- Lambda関数を実行するところ
- 外部モジュールの入れ方がわからない
- イベントハンドラ(
module.exports.handler
)が無い - 環境変数を間違えた
- SNSでショートメールを送るところ
- IAMロールが設定できていない問題
- Lambdaがタイムアウトする問題
- ES6記法が使えない問題
- SMSの使用上限がデフォルトで月1ドルだった問題
など。
まとめ
本記事では、Twitterのツイート数から電車遅延を検知し、通知するアプリをAWS Lambda / Node.jsを使って作りました。
AWSどころか、個人開発自体も初心者なので、まずは簡単なところから実施してみました。 勉強中の方は一緒に頑張りましょう!
間違いなどあればご連絡いただければと思います。