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