凛です。夜勤明けの朝、スマホを開くとGmailの未読バッジが「87」なんて数字になっていて、それだけでどっと疲れた経験はありませんか。わたしは以前、その未読の山を上からひとつずつ開いては閉じ、また開いては閉じ、を繰り返していました。でも中身をよく見ると、半分以上はメルマガや通知メールで、本当に大事な人からのメールはほんの数通。「この差出人ごとの内訳が、朝いちばんに一目でわかったら楽なのに」と思ったのが、この仕組みを作るきっかけでした。
Google Apps Script(GAS)を使えば、未読メールを差出人別に数えて、毎朝きれいに整理したサマリーを自分宛にメールで送る、という自動化がプログラム初心者でも作れます。サーバーもお金も要りません。この記事では、そのコードと考え方を、わたしがつまずいたポイントも含めて丁寧にお伝えします。
GASで未読メールを差出人別に集計して毎朝メールで受け取る
「未読が多すぎて何が大事かわからない」を、GASで「差出人ごとの件数一覧」に変えてしまいましょう。仕組みはとてもシンプルで、①未読メールを取ってくる、②差出人ごとに数える、③結果を自分にメールで送る、の3ステップだけです。順番に組み立てていきます。
そもそもこの仕組みで何ができるのか
まずは完成形のイメージを共有させてください。ゴールがはっきりしていると、途中のコードもすっと頭に入ります。
毎朝届くサマリーメールの中身
この仕組みが動くと、毎朝7時に自分のGmailへ、こんな内容のメールが届くようになります。
未読メールの差出人別 集計(合計 42件)
12件 楽天市場 <info@rakuten.co.jp>
8件 Amazon.co.jp <no-reply@amazon.co.jp>
5件 職場の連絡網 <renraku@example.com>
3件 ○○銀行 <mail@example-bank.co.jp>
...
差出人ごとに何件たまっているかが、多い順にずらっと並びます。これを見れば「あ、職場から5件も未読があった。これは今すぐ開こう」「楽天の12件はあとでまとめて消せばいいや」と、朝いちばんに優先順位がつけられます。未読の山を1通ずつ開かなくても、全体像が先にわかるのがポイントです。
なぜGASと相性がいいのか
この作業は、人間がやると地味に面倒なわりに、機械にとってはとても得意な種類の仕事です。「決まった条件で数えて、決まった形にまとめて、送る」という定型作業だからです。
GASはGoogleのサービス(Gmail・スプレッドシート・カレンダーなど)を操作するのが得意で、しかもGoogleアカウントさえあれば無料で使えます。Gmailを操作する専用の機能(GmailApp)も最初から用意されているので、「メールを検索する」「差出人を取り出す」「メールを送る」といった操作を、短いコードで書けてしまいます。パソコンをつけっぱなしにする必要もなく、Googleのサーバー側で時間になったら勝手に動いてくれるのも、忙しい人にぴったりです。
準備:GASプロジェクトを作る
コードを書く前に、コードを置く場所を用意します。ここは数分で終わります。
スクリプトエディタを開く
ブラウザで script.google.com にアクセスして、Gmailと同じGoogleアカウントでログインします。左上の「新しいプロジェクト」をクリックすると、コードを書く画面(スクリプトエディタ)が開きます。
最初は function myFunction() {} という空っぽの関数だけが表示されています。この中身をこれから書いていきます。プロジェクトの名前は、画面上部の「無題のプロジェクト」の部分をクリックして、「未読メール集計」など自分がわかる名前に変えておくと、あとで探しやすくて便利です。
まず未読の件数だけ数えてみる
いきなり全部を作ると混乱するので、まずは「未読メールの件数を数えて画面に出す」だけの、いちばん小さいコードから始めます。動くものを少しずつ育てていくのが、挫折しないコツです。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 未読メールの件数をログに出すだけのテスト関数
function countUnread() {
// GmailApp.search で「is:unread(未読)」のスレッドを検索する
// 第2引数の 0 は「先頭から」、第3引数の 100 は「最大100件まで」の意味
const threads = GmailApp.search('is:unread', 0, 100); // 未読スレッドの配列が返ってくる
// threads は配列なので .length でスレッドの数がわかる
Logger.log('未読スレッド数:' + threads.length); // 実行ログに件数を表示する
}
このコードを貼り付けたら、画面上部の「実行」ボタンを押します。初回だけ「このアプリがGmailにアクセスすることを許可しますか?」という確認画面(承認画面)が出るので、自分のアカウントを選んで許可してください。これは自分で作ったスクリプトが自分のGmailを読むための許可なので、安心して進めて大丈夫です。実行後、画面下の「実行ログ」に 未読スレッド数:○○ と出れば成功です。
is:unread という書き方は、ふだんGmailの検索窓に打ち込む検索キーワードとまったく同じです。Gmailで使える検索の書式が、そのままGASでも使えると覚えておくと応用が効きます。
差出人を取り出して集計する
件数が数えられたら、次は本題の「差出人ごとに数える」部分です。ここがこの記事の心臓部になります。
スレッドとメッセージの関係を知る
まず、Gmailの中身の構造を少しだけ理解しておきましょう。Gmailは、やりとりが続くメールを「スレッド」というまとまりで扱います。1つのスレッドの中に、複数の「メッセージ(1通1通のメール)」が入っている、という入れ子の関係です。
先ほどの GmailApp.search が返してくるのは「スレッドの配列」なので、その中から1通1通のメッセージを取り出す必要があります。今回は「そのスレッドでいちばん新しいメールの差出人」を代表として使いたいので、最新のメッセージを取り出します。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 1つのスレッドから最新メッセージの差出人を取り出すテスト
function checkLatestFrom() {
const threads = GmailApp.search('is:unread', 0, 100); // 未読スレッドを取得
// 先頭のスレッドを1つだけ取り出して中身を見てみる
const thread = threads[0]; // 配列の0番目(1つ目)のスレッド
// getMessages() でスレッド内の全メッセージを配列で取得し、
// pop() で配列の最後の要素(=いちばん新しいメッセージ)を取り出す
const latestMessage = thread.getMessages().pop(); // 最新の1通
// getFrom() で差出人の文字列を取得する
const from = latestMessage.getFrom(); // 例:「楽天市場 <info@rakuten.co.jp>」
Logger.log('差出人:' + from); // ログに差出人を表示
}
getMessages() はそのスレッドのメッセージを「古い順」の配列で返してくれます。だから pop() で最後尾を取ると、いちばん新しいメールになる、というわけです。getFrom() で得られる文字列は、多くの場合「表示名 <メールアドレス>」という形になっています。
差出人ごとにカウントする
いよいよ集計です。プログラミングで「◯◯ごとに数える」というときは、オブジェクト(キーと値のペアを入れられる入れ物)をカウンター(計数器)として使うのが定番です。
考え方はシンプルで、「差出人の名前をカギ(キー)にして、そのカギの数字を1ずつ増やしていく」だけです。同じ差出人が出てくるたびに、その人の数字がプラス1されていきます。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 未読メールを差出人ごとに集計して、件数のオブジェクトを返す関数
function countBySender() {
const threads = GmailApp.search('is:unread', 0, 100); // 未読スレッドを取得
// 差出人ごとの件数を貯めていく入れ物(カウンター)を空で用意する
const counts = {}; // 例:{ "楽天市場 <...>": 12, "Amazon <...>": 8 }
// すべてのスレッドを1つずつ順番に処理する
for (const thread of threads) {
const latestMessage = thread.getMessages().pop(); // 最新メッセージを取得
const from = latestMessage.getFrom(); // 差出人の文字列を取得
// まだ登録がなければ 0 とみなして、そこに 1 を足す
// (counts[from] が未定義なら 0 として扱うテクニック)
counts[from] = (counts[from] || 0) + 1; // この差出人のカウントを1増やす
}
Logger.log(counts); // 集計結果をログで確認
return counts; // 集計結果を呼び出し元に返す
}
counts[from] = (counts[from] || 0) + 1; の一行がキモです。「もし counts[from] にまだ数字が入っていなければ(=初めて見る差出人なら)0とみなして、それに1を足す」という意味です。この書き方を覚えておくと、いろんな「◯◯ごとの集計」に使い回せます。
差出人の表示を見やすく整える
getFrom() の文字列は「楽天市場 info@rakuten.co.jp」のように、表示名とアドレスがくっついています。人によっては表示名がなく、アドレスだけのこともあります。集計のカギとして使う前に、少し整えておくと結果が読みやすくなります。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 差出人文字列を「表示名(なければアドレス)」に整える関数
function cleanFrom(from) {
// 「表示名 <address>」の形かどうかを正規表現で調べる
const match = from.match(/^\s*"?(.*?)"?\s*<(.+?)>\s*$/); // 表示名とアドレスを分けて取り出す
if (match) {
const name = match[1].trim(); // 表示名部分(前後の空白を除去)
const address = match[2].trim(); // メールアドレス部分
// 表示名があればそれを、無ければアドレスを返す
return name ? name + ' <' + address + '>' : address;
}
// 「<>」が無く、アドレスだけの場合はそのまま返す
return from.trim();
}
この cleanFrom を集計のところで通してあげると、counts[cleanFrom(from)] のように整えた差出人でカウントできます。ここは好みの範囲なので、最初はなくても大丈夫です。慣れてきたら組み込んでみてください。
サマリーを組み立てて自分に送る
集計ができたら、それを人が読める文章にまとめて、メールで自分に送ります。ここまで来ればゴールはすぐそこです。
多い順に並べ替える
集計結果のオブジェクトは、そのままだと順番がバラバラです。件数の多い差出人を上に持ってきたいので、並べ替え(ソート)をします。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 集計オブジェクトを「件数の多い順」に並べた配列に変換する関数
function sortByCount(counts) {
// Object.entries でオブジェクトを [キー, 値] の配列の配列に変換する
// 例:[ ["楽天 <...>", 12], ["Amazon <...>", 8] ]
const entries = Object.entries(counts);
// b[1] - a[1] で、2番目の要素(件数)が大きい順に並べ替える
entries.sort((a, b) => b[1] - a[1]); // 件数の降順(多い→少ない)
return entries; // 並べ替え済みの配列を返す
}
Object.entries で「差出人と件数のペアの一覧」に変換してから、sort で件数の大きい順に並べています。b[1] - a[1] と書くと降順(多い順)になる、というのはお決まりのパターンとして覚えてしまってOKです。
本文を作って送信する
最後に、並べ替えた結果を文章にして、GmailApp.sendEmail で自分宛に送ります。宛先は自分のアドレスを直接書いてもいいのですが、Session.getActiveUser().getEmail() を使うと、実行しているアカウント自身のアドレスを自動で取ってきてくれるので、コードを使い回しやすくなります。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// ここまでの部品を全部つないで、集計メールを自分に送るメイン関数
function sendUnreadSummary() {
const threads = GmailApp.search('is:unread', 0, 100); // 未読スレッドを取得
const counts = {}; // 差出人ごとのカウンター
let total = 0; // 未読の合計件数
// すべてのスレッドを1つずつ集計する
for (const thread of threads) {
const latestMessage = thread.getMessages().pop(); // 最新メッセージ
const from = cleanFrom(latestMessage.getFrom()); // 差出人を整えて取得
counts[from] = (counts[from] || 0) + 1; // カウントを1増やす
total = total + 1; // 合計も1増やす
}
const sorted = sortByCount(counts); // 件数の多い順に並べ替える
// メール本文を組み立てる(1行目は見出し)
let body = '未読メールの差出人別 集計(合計 ' + total + '件)\n\n';
// 並べ替え済みの一覧を1行ずつ本文に追加していく
for (const [from, count] of sorted) {
// 件数を右寄せ風にそろえるため、少しスペースを足して読みやすくする
body = body + count + '件\t' + from + '\n';
}
// 未読が0件のときのメッセージも用意しておく
if (total === 0) {
body = '今日は未読メールがありません。おつかれさまです。';
}
// 自分自身のメールアドレスを取得する
const myEmail = Session.getActiveUser().getEmail(); // ログイン中のアドレス
// 件名・本文を指定して、自分宛にメールを送信する
GmailApp.sendEmail(myEmail, '未読メール集計', body); // 送信実行
}
この sendUnreadSummary を実行して、自分のGmailに集計メールが届けば完成です。届かないときは、少し下の「よくある失敗」の章も参考にしてください。
検索条件を絞って、もっと使いやすくする
is:unread だけだと、古いメルマガの未読まで全部数えてしまいます。検索条件を変えるだけで、目的に合わせた集計に育てられます。ここが実は一番おもしろいところです。
期間やラベルで絞る
GmailApp.search に渡す検索クエリは、Gmailの検索窓と同じ書式が使えます。よく使う組み合わせを表にまとめました。
| 目的 | 検索クエリの例 | 意味 |
|---|---|---|
| 直近1週間の未読だけ | is:unread newer_than:7d | 7日以内に届いた未読 |
| 直近1日の未読だけ | is:unread newer_than:1d | 24時間以内の未読 |
| 特定ラベルの未読 | label:仕事 is:unread | 「仕事」ラベルの未読 |
| 添付ファイル付きの未読 | is:unread has:attachment | 添付ありの未読 |
| 特定の人からの未読 | is:unread from:renraku@example.com | その人からの未読 |
たとえば「最近たまった未読だけ知りたい」なら、コードの GmailApp.search('is:unread', 0, 100) を GmailApp.search('is:unread newer_than:7d', 0, 100) に変えるだけです。仕事のラベルだけ見張りたいなら label:仕事 is:unread にします。目的に合わせて自由にカスタマイズしてください。
件数の上限を意識する
GmailApp.search の第3引数(今回は 100)は「最大何件まで取ってくるか」という上限です。ここを大きくしすぎると、処理するスレッドが増えて実行に時間がかかり、あとで触れる「実行時間の上限」に引っかかりやすくなります。
毎朝の集計なら、直近の未読が見えれば十分なことが多いので、newer_than:7d などで期間を絞ったうえで、上限は100〜200件くらいにしておくと安定します。「全部を完璧に数える」より「大事な傾向がつかめる」ことを優先すると、軽くて壊れにくい仕組みになります。
毎朝7時に自動で動かす
最後の仕上げです。ここまでは自分で「実行」ボタンを押していましたが、これをGoogleに任せて、毎朝勝手に動くようにします。
時間主導トリガーを設定する
GASには「トリガー」という、決まったタイミングで関数を自動実行する仕組みがあります。設定は画面操作だけででき、コードは要りません。
- スクリプトエディタの左側のメニューから、目覚まし時計のアイコン「トリガー」をクリックします。
- 右下の「トリガーを追加」ボタンを押します。
- 「実行する関数を選択」で
sendUnreadSummaryを選びます。 - 「イベントのソースを選択」で「時間主導型」を選びます。
- 「時間ベースのトリガーのタイプ」で「日付ベースのタイマー」を選び、時刻を「午前7時〜8時」に設定します。
- 「保存」を押せば完了です。
これで毎朝7時台に、自動で集計メールが届くようになります。GASの時間主導トリガーは「午前7時〜8時」のように1時間の幅で指定する形式なので、きっかり7時00分ではなく、その時間帯のどこかで実行される、という点だけ覚えておいてください。パソコンの電源が入っていなくても、Googleのサーバー側で動くので大丈夫です。
動作を確認する
トリガーを設定したら、翌朝ちゃんとメールが届くか確認してみましょう。すぐに確認したいときは、スクリプトエディタで sendUnreadSummary を手動実行して、届く内容をチェックしておくと安心です。
わたしは最初、平日だけ届けばいいと思っていたのですが、GASの日付ベースのタイマーは曜日の指定ができないタイプなので毎日届きます。曜日を絞りたいときは「週ベースのタイマー」を使う方法もあるので、必要に応じて切り替えてください。まずは毎日届く形で使ってみて、多すぎると感じたら調整するくらいがちょうどいいと思います。
よくある失敗と回避策
わたし自身がつまずいた点を中心に、初心者がハマりやすいポイントと対処法をまとめておきます。ここを先に読んでおくと、時間を無駄にせずにすみます。
メールが届かない・エラーになる
いちばん多いのが「承認(許可)が済んでいない」パターンです。初回実行のときに出る承認画面を途中で閉じてしまうと、Gmailを操作できずにエラーになります。もう一度手動で実行して、最後まで許可を進めてください。
もうひとつ、Session.getActiveUser().getEmail() が空になってメールが届かないことがあります。これはアカウントの種類によって、自分のアドレスをうまく取得できないケースがあるためです。その場合は、宛先を自分のアドレスの文字列で直接書いてしまうのが確実です。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// getActiveUser が使えないときは、宛先を直接文字列で指定する
const myEmail = 'あなたのアドレス@gmail.com'; // 自分のアドレスに書き換える
GmailApp.sendEmail(myEmail, '未読メール集計', body); // 送信
処理が途中で止まる・遅い
未読が何百件もある状態で上限を大きくすると、処理が重くなって、GASの「1回あたりの実行時間の上限(無料アカウントでは数分程度)」を超えてしまうことがあります。そうなると途中でエラーになって最後まで動きません。
対策は、先ほど紹介した newer_than:7d などで対象を絞ることと、GmailApp.search の件数上限を欲張りすぎないことです。「全件」より「直近の傾向」に割り切るのが、軽くて安定した運用のコツです。
クォータ(1日あたりの上限)に注意する
GASのGmail操作には、1日あたりに使える回数の上限(クォータ)があります。たとえば無料のGmailアカウントでは、GASからのメール送信は1日あたり100通程度が目安です。今回の仕組みは1日1通しか送らないので、まず問題になることはありませんが、これを応用してたくさんメールを送る仕組みを作るときは、この上限を意識してください。読み取り側にも上限があるので、大量のスレッドを何度も処理する使い方は避けるのが無難です。
プライバシーへの配慮
最後に大切な注意点です。この仕組みは、あくまで「差出人と件数」という一覧を、自分自身の宛先に送るだけにとどめておきましょう。メールの本文や、他人の個人情報を、外部のアドレスや他人に送る形に改造するのはおすすめしません。自分の受信トレイを整理するための、自分だけのツールとして使うのが安全です。便利さと引き換えに、大事な情報がうっかり漏れることのないよう、送り先は必ず自分自身にしておいてください。
自分でも作れるようになりたい方へ
ここまで読んでくださって、ありがとうございます。「難しそう」と身構えていた方も、意外と短いコードでできそうだと感じてもらえたら嬉しいです。わたしも最初は for の一行ですら緊張しながら書いていましたが、こうして小さな自動化を1つ作れると、「次はあれもできるかも」と世界が広がっていきます。看護師のわたしにできたのですから、きっと大丈夫。まずは今日、countUnread の10行から始めてみてください。
Dive into Code(未経験からエンジニアを目指すプログラミングスクール)
関連記事
- Gmail未読を条件検索してラベル付与するGAS:Gmailの検索クエリとラベルを使って、目的のメールだけを正確に絞り込む方法を解説しています。
- GASで一斉メール送信300件までの安全な書き方:スプレッドシートの宛先リストから、GASでメールを一斉送信する仕組みの作り方です。
- 毎朝8時にGASを実行する時刻指定トリガー|時間主導型の設定と活用例10選:時間主導トリガーで、毎日決まった時刻にスクリプトを自動実行する手順を詳しく紹介しています。
この記事を書いた人:凛
2児2児のママで現役ナース。夜勤明けの細切れ時間を副業GASに投じ、月5〜8万円の副収入を継続中。「看護師でもコードは書ける」を合言葉に、家事育児とプログラミングを両立する等身大の情報を発信しています。
掲載コードは構文とAPI仕様を確認して載せていますが、お使いの環境に合わせて調整してください。