pslaboが試したことの記録

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

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

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


Gmail で受信したメールを差出人の氏名またはアドレスで自動でラベル付けし、未読が無いラベルは自動的に消してみる。

以前にこういうものを作っていたのですが、これの発展形です。
pslabo.hatenablog.com


以前に作成したものは以下のような問題がありました。

  • ラベルは作るだけなので、運用期間が長くなるとラベルの数がふえて逆に探しづらい。
  • メールアドレスだけだと、だれから来たのか分かりづらい。
  • inbox.google.com では非常に使いづらい(ラベルの階層構造に非対応なので、単に大量のラベルが並ぶ羽目になる)


そこで次のような改善を行ってみました。

  • 未読が無いラベルは定期的に消すようにした。これにより、直近で送受信が行われたメールだけにラベルが付く。
  • Google Contact に登録済みのアドレスについては、メールアドレスの代わりに名前を使うようにした。
  • inbox.google.com でも見やすくするためにラベル名を _Frm/名前 または _Frm/[メールアドレス] のように短縮した。


使い方は以前のものと同じです。cron10min() はトリガーで10分毎に実行、cron1day() はトリガーで1日1回の実行となるように設定します。

なお、いずれの関数も1回だけ手作業で実行しておき、承認のダイアログに対して OK しておきます。

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

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

// Google Contact から取得した名前のラベルを作るかどうか
ENABLE_CONTACTNAME_LABEL=true;

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

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

// 差出人毎のラベルはユーザー名だけで作る
ENABLE_MAILADDDRESS_LABEL_ONLY_USERNAME=false;

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

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

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

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

  // 未読がないラベルを消す。
  _deleteAlreadyReadedLabel()
}

// 受信トレイに存在しないメールを自動で既読にする
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(180);
}

// 「直近 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;
  searchString = "after:" + searchString;
Logger.log( "searchString = " + 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_CONTACTNAME_LABEL == true ) {
        var mailFromAddress = _getMailaddressFromMailaddress( messages[j].getFrom() );
        var labelString = LABEL_MAILFROM_BASE + '/' + _getContactName( mailFromAddress );

        // スレッドに差出人名のラベルを付ける
        if ( labelString != LABEL_MAILFROM_BASE + '/' ) {
          var labelDomain = _createLabel(labelString);
          threads[i].addLabel( labelDomain );
        }
      }
      
      if ( ENABLE_MAILDOMAIN_LABEL == true ) {
        // mail from からドメイン名を抽出する
        var mailFromDomain = _getDomainFromMailaddress( messages[j].getFrom() );
        var labelString = LABEL_MAILFROM_BASE + "/" + mailFromDomain;

        // スレッドにドメイン名のラベルを付ける
        if ( labelString != LABEL_MAILFROM_BASE + '/' ) {
          var labelDomain = _createLabel(labelString);
          threads[i].addLabel( labelDomain );
        }

        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() );

        // メールアドレスのユーザ名部分だけを抜く指示が有効ならば、それだけを抜く。
        if ( ENABLE_MAILADDDRESS_LABEL_ONLY_USERNAME == true ) {
          mailFromAddress = _getUsernameFromMailaddress( mailFromAddress );
        }
        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 _getUsernameFromMailaddress(mailaddress_raw) {
  
  var mailAddress = _getMailaddressFromMailaddress(mailaddress_raw);
Logger.log ( "_getDomainFromMailaddress " + mailAddress);

  // メールアドレスからユーザ名部分を抜く
  mailAddress.match("^(.+)@(.+)$");  
  var userName = RegExp.$1;
    
Logger.log ( "_getUsernameFromMailaddress " + userName);
  return userName;
}


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

  // 指定されたラベルが無い場合は作る
  if ( labelDomain == null ) {
    labelDomain = GmailApp.createLabel(labelString);
  }
  
  return labelDomain;
}


function testcode()
{
  var mailFromAddress = _getMailaddressFromMailaddress( 'info@twitter.com' );
  var labelstring = LABEL_MAILFROM_BASE + '/' + _getContactName( mailFromAddress );
Logger.log ( "labelsting = " + labelstring );
  var labelDomain = _createLabel(labelstring);
Logger.log ( "labelDomain = " + labelDomain.getName() );

  _getContactName('info@twitter.com')
  
}

// メールアドレスに対応する人名を取得する
function _getContactName(emailaddress) {
  var contact = ContactsApp.getContact(emailaddress);
  var contactName = "";

  if ( contact != null && contact.getFullName() != '' ) {
    // Google Contact にエントリがある場合は名前を取得する
    contactName = contact.getFullName();
  } else {
    contactName = '[' + emailaddress + ']';
  }

Logger.log( contactName );
  return contactName
}


// 未読が無いラベルを自動的に消す。
// 1日1回動かす想定。
function _deleteAlreadyReadedLabel() {
  var labels = GmailApp.getUserLabels();
  for ( var labelindex = 0 ; labelindex < labels.length ; labelindex++ ) {
    var unreads=labels[labelindex].getUnreadCount();

    if ( unreads > 0 ) {
      // 未読がある場合は件数をログに出す。
Logger.log( "label[%s] = %s, count = %s", labelindex, labels[labelindex].getName(), unreads );
    } else {
      // 未読が無く、なおかつラベルがメールアドレス振り分けのラベルの場合は消す。
      if ( labels[labelindex].getName().indexOf( LABEL_MAILFROM_BASE + "/" ) == 0 ) { 
Logger.log( "label[%s] = %s, count = %s (delete)", labelindex, labels[labelindex].getName(), unreads );
        labels[labelindex].deleteLabel();
      }
    }
  }  
}