LINEDevelopersの設定をする
- LINEDevelopersに登録とチャンネル(通知が送られてくるアカウント)を作成する。
LINE Developers公式
チャンネルを作ると、上のような設定状況を見ることができる。名前は「AWS料金通知」とした。
ここでAPIを使うためのアクセストークン
とメッセージを送信する先のユーザコード
を確認しておく。
LINEAPIのプッシュメッセージでメッセージを送る
まず自分のLINEアカウントにメッセージを送信する。
const https = require('https');
const LINEMessage = (() => {
let _postData = '';
let _message = '';
let _requestBody = new Object();
const setMessage = (data) => {
_message = String(`今月のAWS概算請求金額は「${data}$」です。`);
_requestBody.to = process.env.LINE_USER_ID;
_requestBody.messages = [{'type': 'text','text': _message}];
_postData = JSON.stringify(_requestBody);
}
const pushMessage = () => {
return new Promise(resolve => {
//setMessageの使用を強制させる
if(_postData === ''){
console.log('setMessageの使用が必要です');
return;
}
const headers= {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': `Bearer ${process.env.LINE_ACCESS_TOKEN}`,
'Content-Length':Buffer.byteLength(_postData)
};
const _options = {
hostname: 'api.line.me',
port: 443,
path: '/v2/bot/message/push',
method: 'POST',
headers: headers
};
const req = https.request(_options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('end', () => {
resolve(true);
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(_postData);
req.end();
});
}
return {
pushMessage: pushMessage,
setMessage: setMessage
}
})();
LINEから提供されているbot-sdk
は使用せず、Node.js標準のhttps
モジュールを使用してエンドポイントにPOSTする方式でメッセージを送信する。外部ライブラリを使用した場合AWS Lambda
側でパッケージを使用する用の設定が別途必要。
- 私がハマった部分
メッセージボディが下記の場合は、400エラーを返す。
const requestBody = {
'to':'XXXXXXXXXXX',
'messages':{
'type': 'text',
'text': 'Hello, world'
}
};
公式ドキュメントを見ながら、何が間違っているのかわからず2,3時間これに消費した。
メッセージボディが下記の場合は、正しい。(配列形式になっているか)
const requestBody = {
'to':'XXXXXXXXXXX',
'messages': [{
'type': 'text',
'text': 'Hello, world'
}]
};
参考:
LINEAPIへPOSTしてプッシュメッセージを送信する
テキストメッセージのJSONオブジェクト
AWSのAPI権限周りの設定をする
- IAMユーザのポリシーの設定で
CloudWatchReadOnlyAccess
を付与する。 - 認証情報をローカルに保存する
参考:
共有認証情報ファイルから Node.js に認証情報をロードする
~/.aws/credentialsファイルでプロファイルを切り替える
AWSのCloudWatchから概算請求金額を取得する
CloudWatchのgetMetricStatisticsメソッドを使って請求金額を取得する。
const AWS = require('aws-sdk');
const FechAWSBilling = (() => {
const cw = new AWS.CloudWatch({region: 'us-east-1'});
const date = new Date
const endTime = date.toISOString();
date.setDate(date.getDate() -1);
const startTime = date.toISOString();
const params = {
StartTime: startTime,
EndTime: endTime,
MetricName:'EstimatedCharges',
Namespace:'AWS/Billing',
Period:86400,
Dimensions:[
{
Name:'Currency',
Value:'USD'
}
],
Statistics:['Maximum']
};
const getMetricStatistics = async () => {
return new Promise(resolve => {
cw.getMetricStatistics(params, (err, data) => {
if (err){
console.log(err, err.stack);
}
resolve(data);
});
})
}
return{
getMetricStatistics: getMetricStatistics
}
})();
ハマりやすい部分はパラメータの部分だと思われる。
初見では各プロパティが何を意味しているのか全く分からなかった。(今もよく分かっていない)
const params = {
StartTime:'2019-06-10T12:21:45.351Z',
EndTime:'2019-06-12T12:21:45.351Z',
MetricName:'EstimatedCharges',
Namespace:'AWS/Billing',
Period:86400,
Dimensions:[
{
Name:'Currency',
Value:'USD'
}
],
Statistics:['Maximum']
};
MetricName
:アクセスするリソース名
CloudWatchメトリクス名
Namespace
:アクセスするサービス名
[CloudWatch名前空間]
(https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html)
Period:86400
:データの取得幅
1=1秒なので
60(秒) * 60(分) * 24(時間) = 86400(秒) = 1日
参考:
[CloudWatchの概念]
(https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html)
CloudWatchAPIドキュメント
Amazon Lambdaの登録を行う
毎日定時にCloudWatchのイベントを発生させ、それをトリガーにしてLambdaを起動させる。
毎日18時にイベントを発火する。(単位はGMT,日本時間との時差は9時間)
しかしこの公式通りやってもうまく設定できなかった。なぜ?
[Rate または Cron を使用したスケジュール式]
(https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html)
- Lambdaにソースコードを登録をする
const https = require('https');
const AWS = require('aws-sdk');
/**
* LINEでPushメッセージを送信する
*/
const LINEMessage = (() => {
let _postData = '';
let _message = '';
let _requestBody = new Object();
const setMessage = (data) => {
_message = String(`今月のAWS概算請求金額は「${data}$」です。`);
_requestBody.to = process.env.LINE_USER_ID;
_requestBody.messages = [{'type': 'text','text': _message}];
_postData = JSON.stringify(_requestBody);
}
const pushMessage = () => {
return new Promise(resolve => {
//setMessageの使用を強制させる
if(_postData === ''){
console.log('setMessageの使用が必要です');
return;
}
const headers= {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': `Bearer ${process.env.LINE_ACCESS_TOKEN}`,
'Content-Length':Buffer.byteLength(_postData)
};
const _options = {
hostname: 'api.line.me',
port: 443,
path: '/v2/bot/message/push',
method: 'POST',
headers: headers
};
const req = https.request(_options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('end', () => {
resolve(true);
});
});
req.on('error', (e) => {
console.error(e);
});
req.write(_postData);
req.end();
});
}
return {
pushMessage: pushMessage,
setMessage: setMessage
}
})();
/**
* AWSから請求金額を取得する
*/
const FechAWSBilling = (() => {
const cw = new AWS.CloudWatch({region: 'us-east-1'});
const date = new Date
const endTime = date.toISOString();
date.setDate(date.getDate() -1);
const startTime = date.toISOString();
const params = {
StartTime: startTime,
EndTime: endTime,
MetricName:'EstimatedCharges',
Namespace:'AWS/Billing',
Period:86400,
Dimensions:[
{
Name:'Currency',
Value:'USD'
}
],
Statistics:['Maximum']
};
const getMetricStatistics = async () => {
return new Promise(resolve => {
cw.getMetricStatistics(params, (err, data) => {
if (err){
console.log(err, err.stack);
}
resolve(data);
});
})
}
return{
getMetricStatistics: getMetricStatistics
}
})();
exports.handler = async () => {
const bill = await FechAWSBilling.getMetricStatistics();
LINEMessage.setMessage(bill.Datapoints[0].Maximum);
const pushComplete = await LINEMessage.pushMessage();
if(pushComplete){
console.log('関数は正常に実行されました。');
return;
}
};
- 私がハマった部分
Nodeの非同期処理に注意する。
下記のコードは、Pushメッセージ送信が完了する前にLambda側での処理が終了してECONNRESET
エラーが発生する場合がある。Lambda外でのデバック時はエラーが発生しない分、原因調査に数時間消費した。lambda側でのテストでメッセージの受信ができること確認する。
const bill = await FechAWSBilling.getMetricStatistics();
LINEMessage.setMessage(bill.Datapoints[0].Maximum);
LINEMessage.pushMessage();
}
下記のコードのようにPushメッセージが完了するようにする。
const bill = await FechAWSBilling.getMetricStatistics();
LINEMessage.setMessage(bill.Datapoints[0].Maximum);
const pushComplete = await LINEMessage.pushMessage();
if(pushComplete){
console.log('関数は正常に実行されました。');
return;
}
}
ソースコードの登録が完了したら、LINEで請求金額が通知されることを確認する。
以上です、ありがとうございました。
※AWSの公式ドキュメントの膨大さに気絶しそうになる。