定期的に届くWebサービスのシステム利用料のお知らせメールからfreeeの取引を作成しよう!という挑戦の続きです。
前回の記事はこちら
前回までで、情報抽出の対象となるメールを配列で取得できましたので、それぞれのメールから
- 受信日時
- メールの宛先
- 本文(の一部)に記載されている利用料
を抜き出していきたいと思います。
GmailMessageクラスのメソッドを使ってPOST用の情報を取得しよう
すでに[GmailMessage, GmailMessage, GmailMessage ...]
という配列は取得できているので、それぞれのGmailMessageオブジェクトに対して受信日時・メールの宛先・本文を取得するメソッドを用います。使用するメソッドはいかの3つです。
getDate()
– メッセージの日付と時刻を取得します。getTo()
– メッセージの受信者をカンマ区切りで取得します。getPlainBody()
– HTML フォーマットを使用せずに本文の内容を取得します。
GmailMessageオブジェクトに対して使えるメソッドを確認するためにもリファレンスはあらためて確認しておきましょう。
https://developers.google.com/apps-script/reference/gmail/gmail-message
function postDealFromMail_01() {
/* 検索クエリを指定して対象メールを絞り込み */
const query = 'subject:(【XXサービス】決済完了メール(自動配信)) newer_than:1m';
const lastThreadMessages = new GmailSearch(query).lastThreadMessages;
/* 各メールから必要な情報を抜粋したオブジェクトを格納した配列を作成 */
const aryMailInfoObjs = lastThreadMessages.map(message => {
const date = message.getDate();
const recipient = message.getTo();
const plainBody = message.getPlainBody();
return { mailDate: date, mailRecipient: recipient, mailPlainBody: plainBody };
});
console.log(aryMailInfoObjs);
}
各メールから抜粋した情報をオブジェクトにして格納しているのは、後ほどfreee APIでPOST用に加工しやすいようにです。
これをさらfreee API用に手を加えていきます。
DateオブジェクトをAPI POST用の文字列に変換する
getDate() メソッドの戻り値はDateオブジェクトのため、そのままだとfreee APIにPOSTできません。yyyy-MM-dd型の文字列にする必要があります。
ここはUtilities.formatDate()メソッドを使用します。このメソッドに関しては以下の記事が参考になります。
いつも隣にITのお仕事|【GAS×freeeAPI】GETリクエストで当日入金予定の取引一覧を取得する
const formatDate = Utilities.formatDate(message.getDate(), 'JST', 'yyyy-MM-dd'); // 日付をyyyy-MM-dd型の文字列に
getTo()メソッドは戻り値がカンマ区切り
今回のケースでは受信者が複数になることを想定していないので関係ないのですが、getTo()メソッドの戻り値は、受信アドレスをカンマ区切りしたものです。複数の受信者がいる場合は一工夫が必要になってきます。
getPlainBody() メソッドはメール本文全体が取得されてしまう
最後の難題がこちらです。getPlainBody() メソッドは、HTMLフォーマット前のプレーン(=素)のメール本文を取得します。しかし、必要なのはこの膨大な本文のなかの金額を記した一部分だけです。
今回のケースだと以下のような文章の金額のみを抜き出したいのです。
そこで登場するのが正規表現です。
正規表現を使って文章の一部を抜粋する
正規表現については私もあまり詳しくなく、例によって詳解! Google Apps Script完全入門 [第3版](以降GAS本)とWeb検索に頼りっきりです。
しかし、実際はある関数を1つ作成して以来、その関数で私のやりたいことは概ね網羅できています。その関数とは…「指定した2つの文字列の間にある文字列を格納した配列を戻り値として返す関数」です。
まずその土台となったのは、毎度お世話になっている「いつも隣にITのお仕事」の以下の記事です。
いつも隣にITのお仕事|Google Apps Scriptで正規表現でマッチした文字列から不要な部分を削除する
この記事をたたき台に以下の関数を作成しました。
/**
* 引数として渡した文章から指定した文字列の間にある文字列を格納した配列を戻り値として返す関数
*
* @param {string} doc - 文章(複数行可)
* @param {string} prefix - 抽出したい文字列の前方にある文字列
* @param {Array} suffix - 抽出したい文字列の後方にある文字列
* @return {Array} extStrings - 抽出した文字列を格納した配列
*/
function extractStrings(doc, prefix, suffix) {
const regExp = new RegExp(prefix + '[^]*?' + suffix, 'g');
const strings = doc.match(regExp);
const extStrings = strings.map(string => string.replace(prefix, '').replace(suffix, ''));
return extStrings;
}
この関数をつかってメール本文から金額部分を抜き出します。
前後の文字列を指定して抜粋する
まずは取得したメール本文(PlainBody)の中身を確認して、抜粋したい金額の前後がどういう文字列になっているかを確認します。
今回の私のケースだと以下のようになりました。
\r\n■ ご利用金額 :1,078円\r\n\r\n\r\n
\rや\nは改行コードと呼ばれるものでgetPlainBody() メソッドを用いると改行部分などもこうして改行コードの文字列で取得されます。この改行コードも含めて抜粋したい部分の前後の文字列を指定します。
const prefix = '\r\n■ ご利用金額 :';
const suffix = '円\r\n\r\n\r\n';
const fees = extractStrings(plainBody, prefix, suffix);
console.log(fees); // [ '1,078' ]
前後の文字列の指定はログ出力から慎重に抜粋しましょう。スペースの数など目視ではわからないものも判定時に正確に指定する必要があります。
正規表現に関わる便利ツールもクラス化しておく
こうした正規表現に関わる便利な関数も都度スクリプトファイルに書き込むのではなく、まとめてクラス化しておくと便利だと思います。
/**
* class RegularExp
* 正規表現を利用した文字列操作に関するクラス
*
* extractString() - 引数として渡したテキスト行から指定した文字列の間にある文字列を抽出して戻り値として返すメソッド
* extractStrings() - 引数として渡した文章から指定した文字列の間にある文字列を格納した配列を戻り値として返すメソッド
*
*/
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
class RegularExp {
/**
* 正規表現操作オブジェクトのコンストラクタ
* @constructor
*/
constructor() {
}
/**
* 引数として渡したテキスト行から指定した文字列の間にある文字列を抽出して戻り値として返すメソッド
*
* @param {string} lineText - テキスト行(改行含まない)
* @param {string} prefix - 抽出したい文字列の前方にある文字列
* @param {string} suffix - 抽出したい文字列の後方にある文字列
* @return {string} extString - 抽出した文字列
*/
static extractString(lineText, prefix, suffix) {
const regExp = new RegExp(prefix + '.*?' + suffix, 'g');
const extString = lineText.match(regExp)[0]
.replace(prefix, '')
.replace(suffix, '');
return extString;
}
/* = = = = = = = */
/**
* 引数として渡した文章から指定した文字列の間にある文字列を格納した配列を戻り値として返すメソッド
*
* @param {string} doc - 文章(複数行可)
* @param {string} prefix - 抽出したい文字列の前方にある文字列
* @param {Array} suffix - 抽出したい文字列の後方にある文字列
* @return {Array} extStrings - 抽出した文字列を格納した配列
*/
static extractStrings(doc, prefix, suffix) {
const regExp = new RegExp(prefix + '[^]*?' + suffix, 'g');
const strings = doc.match(regExp);
const extStrings = strings.map(string => string.replace(prefix, '').replace(suffix, ''));
return extStrings;
}
}
カンマを含む文字列を数値またはカンマ抜きの文字列にする
extractStrings()関数で取得できたのは、[ ‘1,078’ ]という文字列を要素とした配列です。最終的にはこの要素を取り出す必要があるのですが、今回のケースだとカンマが含まれており、そのまま要素を取り出しただけではfreee APIのPOSTに使用できません。
カンマを置き換えるために文字列に対して使えるreplace()メソッドを使用します。
const amount = fees[0].replace(',',''); // カンマをブランクに置き換える
これで’1078’のような数字のみの文字列となりました。
メールからの情報抜粋が完了
ということで、ここまででfreee APIのPOST用に最低限必要な情報の抜粋が完了しました。ここまでの解説をまとめたコードは以下になります。
function postDealFromMail_02() {
/* 検索クエリを指定して対象メールを絞り込み */
const query = 'subject:(【XXサービス】決済完了メール(自動配信)) newer_than:1m';
const lastThreadMessages = new GmailSearch(query).lastThreadMessages;
/* 各メールから必要な情報を抜粋したオブジェクトを格納した配列を作成 */
const aryMailInfoObjs = lastThreadMessages.map(message => {
const formatDate = Utilities.formatDate(message.getDate(), 'JST', 'yyyy-MM-dd'); // 日付をyyyy-MM-dd型の文字列に
const recipient = message.getTo(); // 受信アドレスが複数の場合はカンマ区切り
const plainBody = message.getPlainBody();
const prefix = '\r\n■ ご利用金額 :';
const suffix = '円\r\n\r\n\r\n';
const fees = RegularExp.extractStrings(plainBody, prefix, suffix); // 独自クラスRegularExpを使用
const amount = fees[0].replace(',', ''); // 金額内のカンマをブランクに置き換える
return { mailDate: formatDate, mailRecipient: recipient, amount: amount };
});
console.log(aryMailInfoObjs);
}
続く…
シリーズ目次
- GAS x freeeAPIライブラリのトリセツ「定期的に届くメールからfreeeの取引を作成しよう!」その1 – 対象メールの絞り込み
- GAS x freeeAPIライブラリのトリセツ「定期的に届くメールからfreeeの取引を作成しよう!」その2 – POST用の情報を抜粋する
- GAS x freeeAPIライブラリのトリセツ「定期的に届くメールからfreeeの取引を作成しよう!」その3 – 雛形オブジェクトをGETしよう
- GAS x freeeAPIライブラリのトリセツ「定期的に届くメールからfreeeの取引を作成しよう!」その4 – 雛形上書きしてPOSTしよう
Amazon欲しい物リスト公開しています。
開発者のモチベーションアップのためにAmazon欲しい物リストを公開しております。役に立ったよ!という方の感謝の気持ちで何かいただけるのであれば嬉しいです笑