pslaboが試したことの記録

はてなダイヤリーからはてなブログに引っ越してきました

この日記は現在実行中の減量記録を含む個人的なメモとして始めましたが、最近はコンピュータやガジェット、ハック、セキュリティネタのほうがメインになっております。

はてなダイヤリー時代はカテゴリ分けが適当だったのですが、これはそのうち直します。


Gmailで、Thunderbirdの「メールのグループ化」みたいなことをやるスクリプト

本記事は内容が古くなっています。いろいろ改修したものはこちらにあります。
pslabo.hatenablog.com


Thunderbird ではフォルダの表示を「グループ化」にすると「今日・昨日・1週間以内・2週間以内・それ以前」の5つに分類してくれます。

それと同じような分類を Gmail で自動処理するためのスクリプトGoogle Apps Script で組んでみました。(JavaScript はあまり得意じゃないのでコードが綺麗でないのはご勘弁を。)

Thunderbird とは違い、あくまで「すべてのメール」に対して前述の振り分けを行うだけですが、iPhoneiPadGmail を読むときにちょいと便利かも。

また、ついでの実装ではありますが、以下2つの実装も入れています。

「受信トレイに存在しないメールはすべて既読にする(ただしデフォルトで無効)」
「mail from のドメイン名、メールアドレスで階層化したラベルを振る」

メールソフトによっては、メールを読まずにアーカイブする際に既読にしないことがありますが、そういう場合でも自動で既読にしてくれます。この処理は setReadWithoutInbox() で実装しています。

ドメイン名、メールアドレスで階層化したラベルを振るのは、いまどきのメッセンジャーアプリのような挙動を想定しています。


実際の利用時は、cron10min や cron1day をトリガーで定期的に実行するように設定します。

// Gmail で、様々なラベル振り分けを動的に行うスクリプト
//
// このスクリプトでは以下の処理を実装しています。
//
// ・「1日以内」「1週間以内」「2週間以内」のメールにラベルをつけます
//    ラベルは初期値では INBOX の下に作られます。
//
// ・上記期間から外れるメールからは自動でラベルを外します
//
// ・受信トレイにないメールを自動で既読にします。
//   ※この機能は処理が重いため、初期値では無効にしています。
//    有効にする場合は、"in:anywhere -in:inbox is:unread" の条件で
//   ヒットするメールを Gmail の UI で予め既読にしてください。
//
// ・メールの差出元ドメイン毎にラベルをつけます。
//   メールは以下のようにラベルわけされます。
//    MailFrom
//    MailFrom/example.com
//    MailFrom/example.com/foo@example.com
//
// 使用方法:
// 1. 自動設定対象のラベル名定義を適当に調整してください。
//    なお、ラベルを事前に Gmail 側で作成する必要はありません。
//    このスクリプトが自動で作成します。
//
// 2. 送信元別ラベルを付ける、つけないの設定を調整してください。
//
// 3. トリガーで cron10min, cron1day を定期的に実行する設定としてください。

// ラベル名定義
LABEL_1DAY  = "INBOX/直近1日以内";
LABEL_1WEEK = "INBOX/直前1週間以内";
LABEL_2WEEK = "INBOX/直前2週間以内";
LABEL_MAILFROM_BASE = "MailFrom"

// 差出元ドメイン毎の振り分けを行うかどうか
ENABLE_MAILDOMAIN_LABEL=true;

// 差出ドメイン毎のラベル振り分けを行う際に
// 同時に差出人毎の振り分けも行う
ENABLE_MAILADDDRESS_LABEL=true;

// 受信トレイに無いメールを既読扱いにするかどうか
ENABLE_READED_WITHOUT_INBOX=false;

// 数分ごとに実施するバッチ
function cron10min() {
  // 「直近1日」のラベルをつける処理。
  setLabel_1day();
}

// 日次バッチ。
function cron1day() {
  // ラベルの範囲外となったメールからラベルを外す。
  cleanUpOver1day();
  cleanUpOver1Week();
  cleanUpOver2Week();

  // 受信トレイに存在しないメールを自動で既読にする
  setReadWithoutInbox();
}

// 受信トレイに存在しないメールを自動で既読にする
function setReadWithoutInbox() {

  // ENABLE_READED_WITHOUT_INBOX が true 以外ならそのまま終了。
  if ( ENABLE_READED_WITHOUT_INBOX != true ) {
    return;
  }
  
  // すべてのメールで、受信トレイになく、未読のメールを探す
  var searchString = "in:anywhere -in:inbox is:unread";

  Logger.log(searchString);  
  var threads = GmailApp.search( searchString );
    
  // 見つかったスレッドを対象に、ラベルをつける
  for (var i = 0; i < threads.length; i++) {
    Logger.log( "[" + i + "] " + threads[i].getLastMessageDate() + " " + threads[i].getFirstMessageSubject() );

      // 既読にする
        threads[i].markRead()

        // 処理が速すぎるとエラーが出るので適度に休む
        //Utilities.sleep(10);
  }
}

// 「直近1日」のラベルをつける処理。
// トリガーで数分ごとに実行する想定です。
function setLabel_1day() {
  _setLabel_nday(1);
}

// テストコード
function setLabel_XXday() {
  _setLabel_nday(13);
}

// 「直近 n 日」のラベルをつける処理。
function _setLabel_nday(daysBefore) {

  // ラベル文字列を label オブジェクトに変換する。
  var label1day  = _createLabel( LABEL_1DAY );

  // ラベル名 LABEL_1DAY がないメールを、直近 daysBefore 日分のメールから探す
  var dateWithin1day = new Date();
  dateWithin1day.setDate(dateWithin1day.getDate() - daysBefore); 
  var searchString = Utilities.formatDate( dateWithin1day, "GMT+9", "yyyy/MM/dd" );
  searchString = "after:" + searchString + " -label:" + LABEL_1DAY;

  Logger.log(searchString);  
  var threads = GmailApp.search( searchString );
    
  _createLabel( LABEL_MAILFROM_BASE );
  
  // 見つかったスレッドを対象に、ラベルをつける
  for (var i = 0; i < threads.length; i++) {
    Logger.log( "[" + i + "] " + threads[i].getLastMessageDate() + " " + threads[i].getFirstMessageSubject() );

    // 直近1日、1週間、2週間というラベルをつける
    threads[i].addLabel(label1day);

    // メールの差出元ドメインごとにラベルを振る
    var messages = threads[i].getMessages();
    for ( var j = 0 ; j < messages.length; j++ ) {

      if ( ENABLE_MAILDOMAIN_LABEL == true ) {
        // mail from からドメイン名を抽出する
        var mailFromDomain = _getDomainFromMailaddress( messages[j].getFrom() );
        var labelString = LABEL_MAILFROM_BASE + "/" + mailFromDomain;
        var labelDomain = _createLabel(labelString);

        // スレッドにドメイン名のラベルを付ける
        threads[i].addLabel(labelDomain);

        Logger.log( "[" + i + "." + j + "] addLabel: " + labelString );
      }

      if ( ENABLE_MAILDOMAIN_LABEL == true && ENABLE_MAILADDDRESS_LABEL == true ) {
        // メールアドレス毎のラベルも作る。
        var mailFromAddress = _getMailaddressFromMailaddress( messages[j].getFrom() );
        var labelString = LABEL_MAILFROM_BASE + "/" + mailFromDomain + "/" + mailFromAddress;
        var labelDomain = _createLabel(labelString);

        // スレッドにメールアドレス毎のラベルもつける
        threads[i].addLabel(labelDomain);
      
        Logger.log( "[" + i + "." + j + "] addLabel: " + labelString );
      }

      Logger.log( threads[i].getLabels() );
    }
  }
}

function cleanUpOver1day() {
  var label = LABEL_1DAY;

  _removeLabel(label, 1, "before:" ); 
}  


function cleanUpOver1Week() {
  var label = LABEL_1WEEK;

  _removeLabel(label, 7, "before:" ); 
  _removeLabel(label, 1, "after:" ); 
  _setLabel_week(LABEL_1WEEK, 7, 1);
}  

function cleanUpOver2Week() {
  var label = LABEL_2WEEK;

  _removeLabel(label, 14, "before:" ); 
  _removeLabel(label, 7,  "after:" ); 
  _setLabel_week(LABEL_2WEEK, 14, 7);
}  

// 指定期間でラベルをつける処理。
function _setLabel_week(labelString, dateAfter, dateBefore) {

  // ラベル文字列を label オブジェクトに変換する。
  var labelObject = _createLabel( labelString );

  // dateAfter, dateBefore を現在日付基準の日付に変換
  var dayAfter = new Date();
  dayAfter.setDate(dayAfter.getDate() - dateAfter);

  var dayBefore = new Date();
  dayBefore.setDate(dayBefore.getDate() - dateBefore);

  // Gmail の検索条件文字列を生成
  var searchAfter  = Utilities.formatDate( dayAfter,  "GMT+9", "yyyy/MM/dd" );
  var searchBefore = Utilities.formatDate( dayBefore, "GMT+9", "yyyy/MM/dd" );
  var searchString = "after:" + searchAfter + " before:" + searchBefore + " -label:" + labelString;

  Logger.log(searchString);  
  var threads = GmailApp.search( searchString );
    
  // 見つかったスレッドを対象に、ラベルをつける
  for (var i = 0; i < threads.length; i++) {
    Logger.log( "[" + i + "] " + threads[i].getLastMessageDate() + " " + threads[i].getFirstMessageSubject() );

      // 指定されたラベルをつける
        threads[i].addLabel(labelObject);

        // 処理が速すぎるとエラーが出るので適度に休む
//        Utilities.sleep(10);
  }
}

// 指定期間のラベルを抜く処理。
function _removeLabel(labelString, delayDays, afterbefore ) {
  // ラベル文字列を label オブジェクトに変換する。
  var label = _createLabel(labelString);

  // 削除基準の日付(現在日時から delayDays 日前)をセットする。
  var dateWithin1day = new Date();
  dateWithin1day.setDate(dateWithin1day.getDate() - delayDays);

  // 指定ラベルの中から削除対象のものを抽出する
  var searchString = Utilities.formatDate( dateWithin1day, "GMT+9", "yyyy/MM/dd" );
  searchString = afterbefore + searchString + " label:" + labelString
  Logger.log( searchString );
  var threads = GmailApp.search( searchString );
    
  // 取得されたスレッドを対象に、削除を実行する。
  for (var i = 0; i < threads.length; i++) {
    Logger.log( "[" + i + "] " + threads[i].getLastMessageDate() + " " + threads[i].getFirstMessageSubject() );

        // メールをゴミ箱へ
        // threads[i].moveToTrash();

        // メールからラベルを外す
        threads[i].removeLabel(label);

        // 処理が速すぎるとエラーが出るので適度に休む
//        Utilities.sleep(10);
  }
}

// 以下のようなパターンの文字列からメールアドレスだけ抜く
//  foo bar <foo@example.com>
//  foo@example.com
function _getMailaddressFromMailaddress(mailaddress_raw) {

  var mailAddress = mailaddress_raw;
  var mailAddress_return;
  Logger.log ( "_getMailaddressFromMailaddress input: " + mailAddress );

  // メールアドレスが <> で囲まれているかどうかの判定
  
  if ( mailAddress.indexOf("<") >= 0 && mailAddress.lastIndexOf(">") >=0 ) {
    mailAddress.match("<(.+)>");  
    mailAddress_return = RegExp.$1;
  } else {
    mailAddress_return = mailaddress_raw;
  }
  
  Logger.log ( "_getMailaddressFromMailaddress " + mailAddress_return);
  return mailAddress_return;
}

// メールアドレスからドメイン名を抜く
function _getDomainFromMailaddress(mailaddress_raw) {
  
  var mailAddress = _getMailaddressFromMailaddress(mailaddress_raw);
  Logger.log ( "_getDomainFromMailaddress " + mailAddress);

  // メールアドレスからドメイン部分を抜く
  mailAddress.match("^(.+)@(.+)$");  
  var mailDomain = RegExp.$2;
    
  Logger.log ( "_getDomainFromMailaddress " + mailDomain);
  return mailDomain;
}

function _createLabel(labelString) {
  labelDomain = GmailApp.getUserLabelByName(labelString);

  // ドメイン名のラベルが無い場合は作る
  if ( labelDomain == null ) {
    labelDomain = GmailApp.createLabel(labelString);
  }
  
  return labelDomain;
}