見出し画像

【GAS】送受信したGmailをLINE WORKSへつぶやくスクリプトの現状

どうも、イリエモンです。

いままでPTA活動をしたことがないのに、今年度から都内の公立中学校でPTA会長をしています。

PTAのアレコレをこのnoteで報告します。


GmailつぶやきBotの概要

イリエモンのPTAでは、役員や委員向けのコミュニケーションツールとしてLINE WORKSを使い、教職員・会員・外部団体とのやりとりのためにGmailアドレスを1つ取得しています。

工夫として、役員がタイムリーにGmailでのやりとりを受け取れるよう、送受信したメールをLINE WORKS上につぶやくBotを作って1年ほど運用しました。

このGmailつぶやきBot機能は、Google Apps Script (GAS) と LINE WORKSのAPIを使っています。

定期的にスクリプトを動かし、Google側からLINE WORKSへメールの内容を送って、下記のようにメールをつぶやかせています。

GmailつぶやきBot機能のつぶやき

現状のスクリプトを共有します。

GmailつぶやきBotのスクリプト

GmailつぶやきBotの現状のスクリプトはこちらです。

function postGmailToLineworks() {
  
  //LINE WORKS APIのリクエストURLを定義 (Bot No.と トークルームのチャンネルID は適宜変更する)
  const url = 'https://www.worksapis.com/v1.0/bots/XXXXXXX/channels/XXXXXXXX/messages';

  //LINE WORKSへ接続するための基本設定
  const config = {
    SCOPE: 'bot,bot.read',
    CLIENT_ID: 'XXXXXXXXXXXXXXXXXX',
    CLIENT_SECRET: 'XXXXXXXX',
    SERVICE_ACCOUNT: 'XXXXX.serviceaccount@XXX',
    PRIVATE_KEY: `-----BEGIN PRIVATE KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
-----END PRIVATE KEY-----`
  }

  //Gmailから スターが付いていない スレッドを検索して取得
 const threads = GmailApp.search('-is:starred');
  for(const thread of threads){
  }

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

  //GASから届くエラーメールの送り元メールアドレスを定義
  const strFromGoogle = 'noreply-apps-scripts-notifications@google.com';

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

      //スターが付いていない かつ 下書きでない メッセージのみ処理
      if(!myMessages[i][j].isStarred() && !myMessages[i][j].isDraft()){ 

        //メッセージからFrom、日付、表題を取り出す
        let strFrom = myMessages[i][j].getFrom();
        Logger.log (strFrom);
        let strDate = Utilities.formatDate(myMessages[i][j].getDate(), "JST", "yyyy/MM/dd'('E') 'HH:mm:ss");
        let strSubject = myMessages[i][j].getSubject();

        //GASから届くエラーメールは本文を定型とし、その他は送られてきた本文を1,000文字まで取り出す
        let strMessage;
        if(strFrom.includes(strFromGoogle)){
          strMessage = '(このメッセージは無視してください)';
        }else{
          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(); 
      }
    }
  }
}

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

GmailつぶやきBotの機能

GmailつぶやきBotはこのような機能です。
つぶやきは補助機能なので、メールを送るとき、長いメールや添付ファイルを見たいときは、Gmailに直接アクセスしてもらう前提で運用しています。

  • Gmailで受信したメールを、LINE WORKSのトークルームでつぶやきます。

  • 送信済のメールもつぶやきます。
    ※PTA役員側から送ったメールも共有するため。

  • すでにつぶやいたメールか否かを、スターで判断します。

  • つぶやく内容は、送信元 、送受信日時、題名、本文(1,000文字まで)です。

  • つぶやいたメールにスターを付けます。

  • GASのエラーメールは本文を「このメールは無視して…」に置き換えます。
    ※ときどきGASからエラーが上がります。エラーがあったことをシステム管理者が知りたいのと、ユーザがおどろかないよう本文のみ省略してつぶやきます。通信路やAPI応答の問題により、だいたい週イチ程度のエラーメールが届きます。

GmailつぶやきBotの動作環境

GmailつぶやきBotは、下の図のような環境で動かしています。

GASのファイルをGoogle Drive上に配置し、トリガー機能を使って5分ごとに実行しています。

つぶやきの内容は、LINE WORKSのAPIを経由して、役員メンバーを含んだグループトークルーム上にあるBotがつぶやきます。

GmailつぶやきBot機能の動作環境

1年ほど運用してみて

現在、イリエモンのPTA と 自治体のP連が使っているLINE WORKSで、このGmailつぶやきBotを導入しています。

Gmailの内容がプッシュ型で届くためリアルタイムに確認できますし、役員全員に内容が共有されるため大変便利です。

導入当初は、下書き中のメールをつぶやいたり、GASからのエラーメールにユーザがびっくりするなど問題があって徐々に改修を加えました。

PTAやスポーツサークルなどで、共通用Gmail と LINE WORKS をお使いの方々には是非ご活用いただきたいです。

GmailつぶやきBotの設定方法

少し長いですが、昨年 Google と LINE WORKS の設定をまとめましたので、ご覧ください。

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


この記事が参加している募集

振り返りnote

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