📧

GmailをLINE Notifyを使用してLINEに転送する

2024/06/05に公開

やりたいこと

Gmailで特定のラベルがついたメールを、自動でLINEに転送して通知させたい。

きっかけ

家族と一緒に生活している中で、水道代や電気代のお知らせメールなどを私が一元管理していたのですが、重要なメールなども基本的には家族内で共有しておきたいと思い、自動でGmailに転送したら楽だなと思ったためです。

みんなのためにも「これ知らないんだけど!!」が起こりづらい環境構築をしておきましょう。

使用するもの

  • Google Apps Script(GAS)
  • LINE Notifiy API
  • Gmail
  • 上記に必要なアカウント

LINE Notify API

LINEが提供している通知をしてくれるサービス。
https://notify-bot.line.me/ja/
LINE Messaging API(いわゆるbot)も考えたのですが通知だけなのでLINE Notify APIで十分かと思います。

Google App Script

通称GAS。
Microsoft OfficeでいうVBAのようなもの。
スプレッドシートやGmailなど、さまざまなGoogleサービスを連携して使用することができる。
Webスクレイピングもできる模様。

事前準備

アカウントの作成は割愛。

GmailからLINEに通知するラベルを作成

転送するメールを絞るために転送するメールにラベルをつけておきます。
今回は自分で管理するように既にラベルをつけていてというラベルにしています。
あと、実装方法にもよりますが転送済みのメールを再度転送させないために通知済みラベルも作成しておきます。

Gmailで転送したいメールのラベルに自動振り分け

転送したメールをわざわざ手動でラベルつけていたら自動化の意味がないので、自動で振り分けしてもらえるようにGmailで設定しておきます。
検索からフィルタを作るでも良いと思います。
自分は水道や電気などのメールのドメインをORでフィルタをかけて、ヒットしたものにはラベルをつけています。
Gmailのフィルタ

LINE Notify APIトークンの発行

  1. LINE Notify APIにアクセスしてログイン。
  2. マイページからトークンを発行するからトークンの作成。
    トークン発行
  3. トークン名とトークルームを選択。
    今回はグループで送りたかったのでLINEでグループを作成しておいてそれを設定しました。
    LINEグループ
  4. 発行されたトークンを控える。

実装

GASでの実装になります。
自分もGAS自体は初めてだったのですがJavascriptはかじっていたので難しくはなかったです。

スクリプトファイルの生成はいくつかあるみたいですが、
Googleドライブから新規 > その他 > Google Apps Scriptがわかりやすい気がしました。
Google Apps Script生成場所

Editorが立ち上がるのでそこに書いていきます。
今回書いたプログラム。

コード.gs
const HOME_LABEL = '家';
const READ_LABEL = '通知済み';
const BODY_MAX_LENGTH = 300;

function main() {
  const newMessages = fetchHomeMail();
  newMessages.forEach(message => {
    sendLine(message);
  });
}

/** LINEヘ送信 */
function sendLine(message) {
  const lineToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  const payload = { 'message': message };
  const options = {
    "method": "post",
    "headers": { "Authorization": "Bearer " + lineToken },
    "payload": payload
  };

  UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options);
}

/** メールを取得 */
function fetchHomeMail() {
  const sendLabel = GmailApp.getUserLabelByName(READ_LABEL);
  const searchTerms = Utilities.formatString("label:%s -label:%s", HOME_LABEL, READ_LABEL);

  //取得
  const fetchedThreads = GmailApp.search(searchTerms);
  const fetchedMessages = GmailApp.getMessagesForThreads(fetchedThreads);
  const sendMessages = [];

  // 送信メッセージ配列に詰める
  fetchedMessages.forEach(message => {
    sendMessages.push(gmailToString(message[message.length - 1]));
  });

  // 通知済みラベルを追加
  fetchedThreads.forEach(thread => {
    thread.addLabel(sendLabel);
  });

  // LINE用に配列の順番を反転させる
  return sendMessages.reverse();
}

/** メールの整形 */
function gmailToString(mail) {
  const mailFrom = mail.getFrom();
  const subject = mail.getSubject();
  const body = mail.getPlainBody().slice(0, BODY_MAX_LENGTH);

  return Utilities.formatString("\n%s\n\n件名:\n%s\n\n内容:\n%s", mailFrom, subject, body);
}

割と見たまんまな気がします。
sendLine(message)の引数にlineTokenを追加するとか、fetchHomeMail()の引数に検索条件を渡すのもありかと思います。

メール取得

メールの取得してLINEに送信するメッセージを返しています。

↓通知済みのラベルを定義

const sendLabel = GmailApp.getUserLabelByName(READ_LABEL);

↓転送したいメールの検索条件

const searchTerms = Utilities.formatString("label:%s -label:%s", HOME_LABEL, READ_LABEL);

今回は label:家 -label:通知済みにして通知済みになっていないのラベルがついたメールを検索しています。

自分はGmail上でも確認したくて未読/既読状態を保持するためにラベルを使用しましたが、未読なら未転送の判定にして、転送したメールは既読にする方法もありかなと思います。

↓メールを取得して転送メッセージに変換

//取得
const fetchedThreads = GmailApp.search(searchTerms);
const fetchedMessages = GmailApp.getMessagesForThreads(fetchedThreads);
const sendMessages = [];

// 送信メッセージ配列に詰める
fetchedMessages.forEach(message => {
  sendMessages.push(gmailToString(message[message.length - 1]));
});

message[message.length - 1]なのは、返信なども含めてスレッドになっているのですが、今回は基本お知らせメールを転送したいので最新のメッセージのみを詰めています。
全部渡したい場合はさらにforEachとか必要になると思います。

  // 通知済みラベルを追加
  fetchedThreads.forEach(thread => {
    thread.addLabel(sendLabel);
  });

  // LINE用に配列の順番を反転させる
  return sendMessages.reverse();
}

あとは通知済みラベルを付与して再度転送しないようにする処理と、
LINEは古いメッセージが上、新しいメッセージが下なので、配列の順序を反転させています。

LINEヘ送信

LINEヘ送信する文字列を受け取ってLINEへ送信しています。

先ほど取得したAPIトークンを渡しています。

const lineToken = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');

こちらはプロジェクトプロパティにトークンを格納していて、そこから取得しています。
プロジェクトの設定からスクリプト プロパティで設定ができます。
スクリプト プロパティの設定
別にセキュアな保管ではないのですがトークンやAPIキーはハードコーディング避けたいのでこれを活用しました。

あとはmainでメールを取得する関数を実行して、forEachでLINEに送信しています

定期実行

プログラムができたのであとは定期実行をさせるだけです。

  1. トリガーからトリガーを追加
  2. 実行したい関数を選択(今回ではmain)。
  3. トリガーを今回は時間手動型にして分ベース15分おきにしました。
    メールの取得頻度はこれくらいで十分かと思います。
    定期実行

これで15分おきにメールを取得して、未転送のメールはLINEに通知してくれるようになりました。

所感

GASを始めて触りましたがほぼJavascriptだったので簡単でした。
ネットの情報も結構転がっているので、もっと調べれば他のことにもできそう。

Discussion