LoginSignup
2
1

GASでメールから情報を抽出する方法 (ついでにカレンダーに登録)

Posted at

はじめに

知人にタイトルの依頼がきたので、興味本位でやってみた。(GAS触れる自慢がしたかったのです)

ロジックは非常にシンプルです。箇条書きすると下記になります。

  1. Gmail APIを使って特定のメールを取得
  2. 正規表現を使って必要な情報を本文から取得
  3. Google Calendar APIを使ってカレンダーに登録
となります!

その前に

ちなみにGmailには予約完了メール系を勝手にカレンダーに登録してくれる機能があります。 そういったメールをしたい場合は設定だけで済むのでこっちは不要です!

また、今回はプログラムだけではなく、ラベルや自動振り分けなどのGmail自体の機能も使っています。
そちらは詳細に説明しないので調べてもらえればと思います!
このコードを使うには前もって情報を抽出したいメールを手動or自動振り分けでラベル付けとスターを付けていることが前提となります。

あと、スレッドに受信するメールがたまることを前提にコードを書いているので、一部美しくないところがあります。

やってみた

実際にコードを書いてみました!
業務でも使われるコードなので適当なコードになっているところもあります。ご了承ください。

メールtoカレンダー
// gmailに飛んできた楽天からの予約完了メールを自動的にカレンダーに登録するプログラム

function main() {
  
  // オブジェクトの作成
  const label = GmailApp.getUserLabelByName("ラベル");

  console.log("メールから情報を取得");
  const infomations = getMail(label);

  console.log("カレンダーに登録");
  setCalendar(infomations);

};

function getMail(label) {
  // ラベルから必要なメールを取得して情報を取ってくる

  let infomations = [];
  let start = 0;
  let max = 30;
  while (true) {

    
    let threads = label.getThreads(start, max); // 取得エラーの原因になるので一気に取得しない
    if (threads.length <= 0) {
      break;
    };
    start += (max - 1);

    for (thread of threads) {
      if (!thread.hasStarredMessages()) { // すべてのメッセージにスターが外れている場合は無視する
        continue;
      }

      messages = thread.getMessages();
      
      for (message of messages) {
        // 本文から必要な情報を抜き出すスクリプト
        if (!message.isStarred()) { // スターが外れているメッセージは無視
          continue;
        }
        let body = message.getBody();
        body = body.replace(/\u3000/g, "") // すべての全角空白を消す
        body = body.replace(/\n/g, "").replace(/\s+/g, "") // すべての改行を消す

        infomations.push(extractRegexp(body));  
        message = message.markRead(); // 既読にする
        message = message.unstar(); // スターを外す

        console.log("既読とアンスター処理の完了");

      };

      console.log("スレッドの読み込み完了")
        
    };
  };

  return infomations;

};

function extractRegexp(body) {
  /*
    正規表現で美しくデータを抽出するスクリプト
  */
  reg = /日時:(\d{4}-\d{1,2}-\d{1,2})/
  let tmp = body.match(reg)[1];
  info = {"日時" : tmp}
  return info
};

function setCalendar(infomations) {
  // googleカレンダーに取得した情報を説明付きで登録する

  for (info of infomations) {

    let event = CalendarApp.getDefaultCalendar().createEventFromDescription(
        "いい感じのタイトル"
    ); // タイトル付きでカレンダーの作成

    let startTime = new Date("正規表現で取得した日時とか (yyyy-mm-dd hh:MM:ss フォーマットで)");
    let endTime = new Date("正規表現で取得した日時とか (yyyy-mm-dd hh:MM:ss フォーマットで)");

    // 詳細設定
    event.setTime(startTime, endTime); // 日時の登録
    event.setDescription(
      "本文または記載しておきたい予定の情報"
    );

  };
};

では順を追って説明していきます。(自分的に重要なコードのみの説明となります!)

Main部分の説明

main
// オブジェクトの作成
  const label = GmailApp.getUserLabelByName("ラベル");

このコードで自作したラベルのオブジェクトの作成をしています。このオブジェクトからスレッドやメール本文の取得が可能になります。

ちなみにラベルとはGmailのサイドバーにある、こんなやつ。
自分で作る必要があります。
image.png

メールの本文抽出部分の説明

getMail①
    
    let threads = label.getThreads(start, max); // 取得エラーの原因になるので一気に取得しない
    if (threads.length <= 0) {
      break;
    };
    start += (max - 1);

ドキュメントに一度で読み込むメールの量が大きすぎるとエラー落ちすると記載があったので、エラー対策として書いています。

getMail②
    
    for (thread of threads) {
      if (!thread.hasStarredMessages()) { // すべてのメッセージにスターが外れている場合は無視する
        continue;
      }

      messages = thread.getMessages();
      
      for (message of messages) {
        // 本文から必要な情報を抜き出すスクリプト
        if (!message.isStarred()) { // スターが外れているメッセージは無視
          continue;
        }

情報を抽出する対象となるメールはスターがついているもののみを対象としています。

thread.hasStarredMessages()でスレッド内のメールにスターがついているか、
message.isStarred() でメール単体にスターがついているかを確認しています。
受信時にスターをつけて、情報を抽出できてカレンダーに登録できたものはスターを外すとしているので、この処理をすることで同じメールを処理しないようにしてます。

getMail③
        let body = message.getBody();
        body = body.replace(/\u3000/g, "") // すべての全角空白を消す
        body = body.replace(/\n/g, "").replace(/\s+/g, "") // すべての改行を消す

        infomations.push(extractRegexp(body));  
        message = message.markRead(); // 既読にする
        message = message.unstar(); // スターを外す

メールの本文を取得して、正規表現で情報を抜き出す自作関数を呼んでいます。
本文から情報を取得する際に面倒なのは、スクショのように整った形(改行がしっかり入る。)で取得してしまうことです。
image.png

なので、 body.replace(/\n/g, "").replace(/\s+/g, "") を使って改行や空白を削除して、一行の文字列になるように変更します。これをしないと正規表現が上手く使えないので MUSTです
必要であれば全角削除をするために body.replace(/\u3000/g, "") も使うといいかもしれません。

正規表現部分の説明

function extractRegexp(body) {
  /*
    正規表現で美しくデータを抽出するスクリプト
  */
  reg = /日時:(\d{4}-\d{1,2}-\d{1,2})/
  let tmp = body.match(reg)[1];
  info = {"日時" : tmp}
  return info
};

すべてを見せることができないのですが、こんな感じで、正規表現で情報を取ります。
body.match(reg)[1] とすることで (\d{4}-\d{1,2}-\d{1,2}) この()部分の情報のみを取得出来ます。
正規表現は非常に考えるのが面倒ですが、chatGPTさんが優秀なので、考えてもらいましょう。

カレンダー登録部分の説明

setCalendar
function setCalendar(infomations) {
  // googleカレンダーに取得した情報を説明付きで登録する

  for (info of infomations) {

    let event = CalendarApp.getDefaultCalendar().createEventFromDescription(
        "いい感じのタイトル"
    ); // タイトル付きでカレンダーの作成

    let startTime = new Date("正規表現で取得した日時とか (yyyy-mm-dd hh:MM:ss フォーマットで)");
    let endTime = new Date("正規表現で取得した日時とか (yyyy-mm-dd hh:MM:ss フォーマットで)");

    // 詳細設定
    event.setTime(startTime, endTime); // 日時の登録
    event.setDescription(
      "本文または記載しておきたい予定の情報"
    );

  };
setCalendar①
let event = CalendarApp.getDefaultCalendar().createEventFromDescription(
        "いい感じのタイトル"
    ); // タイトル付きでカレンダーの作成

ここで event(カレンダー)の作成を行います。
createEventFromDescription desciption となってややこしいですが、カレンダーのタイトルになります。

setCalendar②
// 詳細設定
    event.setTime(startTime, endTime); // 日時の登録
    event.setDescription(
      "本文または記載しておきたい予定の情報"
    );

日時の登録と詳細の情報の記入を行っています。
日時は dateオブジェクトを入れる必要があるので注意です!

さいごに

Gmailはスレッドの概念があり、ちょっとコーディングめんどいなと思った。(スレッド単位でラベル付けしちゃうところとか)

APIのドキュメントはめちゃくちゃ分かりやすいので、是非参照しながらコードをかいてみてください!
APIドキュメント

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1