8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SupershipグループAdvent Calendar 2022

Day 5

Google Apps Script を使って Gmail を Slack に投稿する

Last updated at Posted at 2022-12-04

はじめに

この記事は、Supershipグループ Advent Calendar 2022の5日目の記事になります。

とても簡単な内容ですが、ご一読いただければ幸いです。
本文では、Gmailのアクセス方法・Slackの投稿方法を記載しています。
また、このスクリプトを実装して気づいた点を末尾に記載しております。

Google Apps Script (GAS) で Gamil から情報を取得する

まず、Apps Script へアクセスし、新しいプロジェクトを作成します。
次にGmailをGASで利用可能にするためサービスを追加します。
サービスの + ボタンを押して、 Gmail API v1 を追加します。
サービスを追加

Gmailへのアクセスリクエストが入るので許可を押します。
Gmailへのアクセスリクエスト
サービスの追加完了後、コード.gs に以下のコードを貼り付けます。

function receiveMail() {
  const query = "重要";
  const threads = GmailApp.search(query, 0, 100).reverse();  
  for (var thread of threads) {
    for (var msg of thread.getMessages()) {
      Logger.log(`${msg.getDate()} ${msg.getSubject()} ${msg.getPlainBody()}`);
    }
  }
}

最後に実行ボタンを押すとログに日時・タイトル・本文が表示されます。
Gmailログ

これでGmailの取得が可能になりました。

Google Apps Script (GAS) で Slack に投稿する

まずはSlackにてアプリを作成します。
詳しい方法は別途Qiitaで記事を検索するなりしてください。
そこで得られたアクセストークンを利用します。
先程のプロジェクトに以下のスクリプトを追加します。

// Slack API
var AUTH_TOKEN = "xoxp-...";
var SLACK_URL_ROOMS_MESSAGE = "https://slack.com/api/chat.postMessage";

// Slack にメッセージを投稿
function chatPostMessage(text) {
  var payload = {
    'token'      : AUTH_TOKEN,
    'channel'    : "#general",
    'parse'      : 'none',
    'username'   : 'Gmailくん',
    'icon_emoji' : ':icon_name:',
    'text': text
  };  
  var params = {
    'method' : "post",
    'payload' : payload
  };
  var response = UrlFetchApp.fetch(SLACK_URL_ROOMS_MESSAGE, params);
}

Slack用のAPIが用意されているわけではないのでGASにある汎用APIを利用します。
UrlFetchApp を利用することで他のアプリケーションと通信したり、ウェブ上の他のリソースにアクセスしたりできます。
これで外部へのデータの送信が可能になりました。
今回は送信したいデータを chatPostMessage(text) に渡してあげるだけでSlackに投稿できます。

一度取得したメールを再度取得しないようにする

同じメールが何度も処理されるのを防ぐ方法を記載します。

処理した日時を保存しておけば、次の処理時にその日時以降のメールだけを処理できます。
どこに保存するかの問題ですが、GASの機能の1つであるスクリプトプロパティを利用します。

以下、コードになります。

const LATEST_CHECK_MAIL_DATE = 'LATEST_CHECK_MAIL_DATE';

// メールの更新をチェックした日時
function saveLatestCheckMailDate() {
  const scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty(LATEST_CHECK_MAIL_DATE, new Date());
}
function getLatestCheckMailDate() {
  const scriptProperties = PropertiesService.getScriptProperties();
  return new Date(scriptProperties.getProperty(LATEST_CHECK_MAIL_DATE));
}

function receiveMail() {
  const query = "重要";
  const threads = GmailApp.search(query, 0, 100).reverse();  
  const latestCheckMailDate = getLatestCheckMailDate();
  for (var thread of threads) {
    for (var msg of thread.getMessages()) {
            // 日時の判定
      if (latestCheckMailDate) {
        if (latestCheckMailDate.getTime() > msg.getDate().getTime()) {
          return;
        }
      }        
      Logger.log(`${msg.getDate()} ${msg.getSubject()} ${msg.getPlainBody()}`);
    }
  }
  saveLatestCheckMailDate();
}

先程のSlackのアクセストークンもスクリプトプロパティに保存しておくと良いかもしれません。

コード全体

実際に試したコードを掲載しておきます。

走らせる場合は実行する関数receiveMailにして下さい。

const LATEST_CHECK_MAIL_DATE = 'LATEST_CHECK_MAIL_DATE';
// メールの更新をチェックした日時
function saveLatestCheckMailDate() {
  const scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty(LATEST_CHECK_MAIL_DATE, new Date());
}
function getLatestCheckMailDate() {
  const scriptProperties = PropertiesService.getScriptProperties();
  return new Date(scriptProperties.getProperty(LATEST_CHECK_MAIL_DATE));
}

var AUTH_TOKEN = "xoxp-...";
var SLACK_URL_ROOMS_MESSAGE = "https://slack.com/api/chat.postMessage";
// Slack にメッセージを投稿
function chatPostMessage(text) {
  var payload = {
    'token'      : AUTH_TOKEN,
    'channel'    : "#general",
    'parse'      : 'none',
    'username'   : 'Gmailくん',
    'icon_emoji' : ':icon_name:',
    'text': text
  };  
  var params = {
    'method' : "post",
    'payload' : payload
  };
  var response = UrlFetchApp.fetch(SLACK_URL_ROOMS_MESSAGE, params);
}

// メールの受信処理
function receiveMail() {
  const query = "{取得したいメールのクエリ}";
  const threads = GmailApp.search(query, 0, 100).reverse();  
  for (var thread of threads) {
    for (var msg of thread.getMessages()) {
      sendMessageChangeAssign(msg.getSubject(), msg.getPlainBody(), msg.getDate());
    }
  }
  saveLatestCheckMailDate();
}

// メール情報からアサイン情報をチェックしてSlackに投稿
function sendMessageChangeAssign(subject, plainBody, date) {
  if (!date) {
    return;
  }

  var latestCheckMailDate = getLatestCheckMailDate();
  if (latestCheckMailDate) {
    if (latestCheckMailDate.getTime() > date.getTime()) {
      return;
    }
  }

  var user = plainBody.match(/担当者 (.*)\r\n/m)[1];

  // Slack に送信する前に動作が問題ないかログで確認しよう
  // Logger.log(`${date} ${subject}`);  
  chatPostMessage(`${user} から ${subject} が届きました`);
}

雑感

実装している途中で非常に恐ろしいなと思う点があったので書き記します。

Google Apps Script (GAS) の脆弱性について

便利に利用させてもらっているGASですが、大きな問題となる機能があります。
それが UrlFetchApp です。
UrlFetchApp を利用すれば、GASで取得可能なGoogle Workspace内のデータは全て外部に流出させることが可能となる点です。
今回は例としてGmailとSlackにしましたが、ここはいくらでも変更可能で他のサービスに投稿することが出来ます。
Google Workspaceの管理者の方々は不審なスクリプトファイルが存在しないかチェックすることをおすすめします。

また、アクセストークンに関しても偽装が簡単に行えます。
とても簡単でアクセストークンを他のSlackのワークスペースのものにしてしまえばいいのです。
アクセストークンはひと目で正しいものか判別ができない上に、アクセストークンにアクセスできる人が限られているため正しさをチェックできないのが欠点です。

対策としては余計な人をメーリングリストに追加したり、アクセス権を渡したりしないことです。
このスクリプトはあくまで手動でできる処理を自動化させただけなので、スクリプトファイルの利用を禁止しても根本的な解決にはなりません。

おわりに

Google Apps Script を使って Gmail を Slack に投稿するスクリプトを書きました。
十分注意を払った上で利用しましょう。

Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。

Supership 採用サイト

是非ともよろしくお願いします。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?