見出し画像

Gmailの新着メールをLINE WORKSにつぶやく (3.Google設定編)

どうも、イリエモンです。
いままでPTA活動をしたことがないのに、2023年度(令和5年度)のPTA会長を引き受けることになりました。
PTAのあれこれをこのnoteで報告します。

この記事はすこし技術寄りの内容です。

この記事は三部作です

① 全体概要は下の記事をご覧ください。

② LINE WORKSの設定は下の記事をご覧ください。

③ Googleの設定は、いまご覧の記事です。

概要

下の図はGmailつぶやき機能の概要図です。
この記事では左側にあるGoogle側の設定について、GASのスクリプトの作成からトリガーによる定期実行までの設定手順を記載しました。

Gmailつぶやき機能の概要図

参考資料

LINE WORKS API 2.0 接続は下のページのコードを使わせていただきました!

Gmailの取得は下のページがとても参考になりました!

設定に必要なLINE WORKSの設定

Googleの設定をするために、LINE WORKSの設定時にメモした以下の情報が必要です。ご確認ください。

  • Bot ID の値

  • Client ID の値

  • Client Secret の値

  • Service Account の値

  • Private Key のファイル

  • チャンネルID の値

設定の流れ

この記事では以下の流れで、Gmailつぶやき機能のLINE WORKS側の設定をします。

  • GASのプロジェクトファイルの作成

  • スクリプトの作成 と 上記のLINE WORKSの設定値の置き換え

  • スクリプトのデプロイ

  • トリガーの設定

設定手順

GASのプロジェクトファイルを作成する

該当のGmailを持つGoogleアカウントでログインし、Google Driveを開きます。
次にGASのプロジェクトファイルを格納するフォルダを作成するため、マイドライブを右クリック > 新しいフォルダ を選択します。

新しいフォルダの作成画面

フォルダ名を入力して「作成」をクリックします。
ここでは「App Script」という名前のフォルダを作成しています

新しいフォルダ名の入力画面

作成したApp Scriptフォルダを開き、右クリック > その他 > Google Apps Script をクリックします。

作成したフォルダは配下にGASのプロジェクトファイルを作成

Google Apps Scriptのプロジェクトが作成されるので、ウィンドウ上部の「無題のプロジェクト」をクリックして名前を変更します。

無題のプロジェクト画面

画面中央にプロジェクトの名前の変更画面が表示されたら、Gmailつぶやき機能のプロジェクト名を入力して「名前を変更」をクリックします。
ここでは「Gmail-LW」というプロジェクト名にしています。

プロジェクトの名前を変更する画面

「Gmail-LW」のプロジェトファイルが作成されました。

Gmail-LWのプロジェトファイル

スクリプトをコピーして必要な値を置き換える

コード.gsに以下のスクリプトをコピーしてください。
元から記載されている functionで始まる数行のコードは削除してからコピーします。

function postGmailToLineworks() {
  
  //LINE WORKS APIのリクエストURLを定義 (下の XXX… を Bot ID に、YYY… をトークルームのチャンネルID に変更する)
  const url = 'https://www.worksapis.com/v1.0/bots/XXXXXXX/channels/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY/messages'
  
  //LINE WORKSへ接続するための基本設定 (下の XXX… をそれぞれの値に変更する)
  const config = {
    SCOPE: 'bot,bot.read',
    CLIENT_ID: 'XXXXXXXXXXXXXXXXXXXX',
    CLIENT_SECRET: 'XXXXXXXXXX',
    SERVICE_ACCOUNT: 'XXXXX.serviceaccount@XXXXX',
    PRIVATE_KEY: `-----BEGIN PRIVATE KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
-----END PRIVATE KEY-----`
  }

  //Gmailから スターが付いてない かつ 受信トレイ内 のスレッドを検索して取得する
  const threads = GmailApp.search('-is:starred in:inbox'); 
  for(const thread of threads){
  }

  //LINE WORKS API の Access Token を取得する
  const accessToken = getAccessToken(config).access_token;

  //スレッドからメールを取得し二次元配列に格納する
  const myMessages = GmailApp.getMessagesForThreads(threads);
  for(let i in myMessages){
    for(let j in myMessages[i]){

      //スター付きでない メッセージのみ処理する
      if(!myMessages[i][j].isStarred()){ 

        //メッセージから差出人、受信日時、件名、本文(1000文字まで)を取り出す
        let strFrom = myMessages[i][j].getFrom();
        let strDate = Utilities.formatDate(myMessages[i][j].getDate(), "JST", "yyyy/MM/dd'('E') 'HH:mm:ss");
        let strSubject = myMessages[i][j].getSubject();
        let strMessage = myMessages[i][j].getPlainBody().slice(0,1000);
        
        //文字列を結合し、本文の改行を \n に置換する
        strMessage = strFrom + '\n' + strDate + '\n' + strSubject + '\n----------------------------\n' + strMessage.replace(/\r?\n/g,"\n");

        //文字を配列へ格納する
        let resArray = strMessage.split(",");
        Logger.log (resArray)

        //LINE WORKS へ Post するデータを作成する
        const options = {
          method: 'post',
          headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json'
          },
          payload: JSON.stringify({
            'content': {
            'type': 'text',
            'text': `${resArray}`
            }
          })
        }
      
        // LINE WORKS へ Post する
        const response = UrlFetchApp.fetch(url, options);

        //処理したメッセージにスターを付与する
        myMessages[i][j].star(); 
      }
    }
  }
}

//LINE WORKS API から Access Token を取得する関数
function getAccessToken(config) {
  const time = Date.now();
  const header = Utilities.base64Encode(JSON.stringify({ 'alg': 'RS256', 'typ': 'JWT' }));
  const claimSet = Utilities.base64Encode(JSON.stringify({
    'iss': config.CLIENT_ID,
    'sub': config.SERVICE_ACCOUNT,
    'iat': Math.floor(time / 1000),
    'exp': Math.floor(time / 1000 + 3600)
  }));
  const signature = Utilities.base64Encode(Utilities.computeRsaSha256Signature(`${header}.${claimSet}`, config.PRIVATE_KEY));
  const jwt = `${header}.${claimSet}.${signature}`;

  const endpoint = 'https://auth.worksmobile.com/oauth2/v2.0/token';
  const options = {
    method: 'post',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    payload: {
      'assertion': jwt,
      'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
      'client_id': config.CLIENT_ID,
      'client_secret': config.CLIENT_SECRET,
      'scope': config.SCOPE
    }
  }
  return JSON.parse(UrlFetchApp.fetch(endpoint, options));
}

コピーすると下の画面のようになります。
スクリプトの頭に設定情報をまとめていますので、LINE WORKSの設定時にメモした値に置き換えます。

スクリプトコピー後の「コード.gs」の画面

下のスクリプトの「XXX…」の部分を「Bot IDの値」に置き換えます。
「YYY…」をトークルームの「チャンネルID」に置き換えます。

  const url = 'https://www.worksapis.com/v1.0/bots/XXXXXXX/channels/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY/messages'

次に、下のスクリプトの「CLIENT_ID:」「CLIENT_SECRET:」「SERVICE_ACCOUNT:」の右の XXX… をそれぞれ、「Client ID の値」「Client Secret の値」「Service Account の値」に置き換えます。

    CLIENT_ID: 'XXXXXXXXXXXXXXXXXXXX',
    CLIENT_SECRET: 'XXXXXXXXXX',
    SERVICE_ACCOUNT: 'XXXXX.serviceaccount@XXXXX',

Private Key のファイルを開き、下のスクリプトの「-----BEGIN PRIVATE KEY-----」から「-----END PRIVATE KEY-----」の間を置き換えます。
改行などもそのままにして置き換えてください。

    PRIVATE_KEY: `-----BEGIN PRIVATE KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
-----END PRIVATE KEY-----`

スクリプトをデプロイする

画面上段の プロジェクトを保存する のアイコン(フロッピーディスクのようなアイコン)をクリックして保存します。
保存できたら[デバッグ]をクリックします。
※「ブレークポイントが設定されていません」と表示されることがありますが気にしないでください。

コード.gs の編集画面

データアクセス権限の確認画面が表示されたら「権限を確認」をクリックし、表示される画面を見て承認してください。

データアクセス権限の確認画面

コード.gs の編集画面に戻ったら、[デプロイ]をクリックします。

コード.gs の編集画面

画面中央に新しいデプロイの画面が表示されるので、「説明」に今回のデプロインのバージョンの説明(初回デプロイや修正点など)を入力します。
「次のユーザーとして実行」は「自分 (xxxxx@gmail.com)」を選び、「アクセスできるユーザー」は「自分のみ」が選ばれていることを確認して[デプロイ]をクリックします。

新しいデプロイ画面

デプロイの更新が完了したら[完了]をクリックします。

新しいデプロイの完了画面

スクリプトを定期実行するためのトリガーを設定する

コード.gs の画面の左ペインから時計のアイコンをクリックし、画面右下の[+トリガーを追加]をクリックします。

トリガー画面

画面中央にトリガーを追加する画面が表示されたら、以下を選んで[保存]をクリックします。

  • 実行する関数を選択 → postGmailToLineworks

  • 実行するデプロイを選択 → Head
    ※Headを指定すると最新のスクリプトが実行されます。実行するスクリプトを厳格に指定したい場合は対象のデプロイバージョンを選択してください。

  • イベントのソースを選択 → 時間主導型

  • 時間ベースのトリガーのタイプを選択 → 分ベースのタイマー

  • 時間の間隔を選択 (分) → 10分おき

  • エラー通知設定 → 毎日通知を受け取る

トリガーを追加する画面

トリガーが追加されたら、Gmailへテストメールを送るなどして動作を確認してみてください。

トリガー画面

スクリプトの解説

スクリプトの解説はコメントに記載していますので、いくつかポイントだけを補足します。

スターがないものを新着メールと判断

このスクリプトはスターが付いていないものを新着メールと扱います。
役員がGmail自体の画面を直接見ることがあるため、メールの削除やフォルダ移動はしていません。
未読を判断に使わない理由は、未読/既読よりスターの有無の方が使用頻度が少ないだろうと想定したからです。
役員にはGmail自体の画面を見たときにスターをいじらないよう周知しています。

スレッドとメール(メッセージ)

Gmailにはスレッドという概念があります。
スレッドの中に1通ずつのメール(メッセージ)が含まれる構造ですので、まずスレッドを取得し、メッセージを取り出しています。

作成中のメールをつぶやかないために受信ボックスのみを参照

下記のコードの2行目の検索条件を当初 GmailApp.search('-is:starred') としていましたが、作成中のメールをつぶやいてしまうことが分かり in:inbox を追加して受信トレイ内を検索するようにしました。

  //Gmailから スターが付いてない かつ 受信トレイ内 のスレッドを検索して取得する
  const threads = GmailApp.search('-is:starred in:inbox'); 
  for(const thread of threads){
  }

メールの受信日付の表示形式

メールの受信日付はUtilities.formatDateメソッドを使ってDateオブジェクトを文字列に変換しています。
現状は「YYYY/MM/DD(英数字三桁の曜日) HH:MM:SS」としています。

let strFrom = myMessages[i][j].getFrom();
        let strDate = Utilities.formatDate(myMessages[i][j].getDate(), "JST", "yyyy/MM/dd'('E') 'HH:mm:ss");

LINE WORKS API 2.0 での Access Token の取得

こちらはQiita の Hellokichiさんの関数を拝借しています。記事の頭の参考資料にリンクを載せていますので詳細はそちらをご覧ください。
API 2.0 の情報が少ない中、難解な公式ドキュメントを解読して開発し、コードを公開いただきました。大変感謝しております。

//LINE WORKS API から Access Token を取得する関数
function getAccessToken(config) {

まとめ

この記事 と 前回の記事でGoogle側、LINE WORKS側を設定し、Gmailつぶやき機能を利用できるようになりました。

この記事がPTAやその他のLINE WORKSをお使いの企業・団体のお役に立てれば幸いです。

最後まで読んでいただきありがとうございました。
では、またっ!

この記事が気に入ったらサポートをしてみませんか?