水無瀬のプログラミング日記

プログラムの勉強した時のメモ

GoogleSiteの変更通知をSlackで受け取る

はじめに

ある日、Googleサイトの通知をSlackで受け取りたいという話がありました。
しかし、DriveやCalendarの通知を受け取ることはできるSlackくんですが、なんとSiteの通知は受け取れないのです。
調べたところ先人の方たちは自力で実装していた。
今回は先人に習って自力での実装をしてみた。

Googleサイトの変更通知をSlackに流すまで

上記の通り、SlackとGoogleサイトを紐付けるAPPはなかった。
仕方ないから自力で実装する。
流れは下記の通り。

  1. サイトの変更通知をメールで受け取る
  2. GASでGmail見て変更通知から必要な情報を抜き取る
  3. SlackAPI使って抜き取った情報を適当なチャンネルに流す

サイトの変更通知を受け取る

サイトでShift + fで通知を受け取れるようになる。
もしくは、サイト右上の歯車から「サイトの操作→サイトの変更通知を受け取る」でもOK。

GASからGmailの情報を抜きとる

サイトの通知メールはnoreply@google.comというアドレスで来るので、
このアドレスから送られてきた未読メールを対象に情報を取得する。
未読を取得するには下記ようにするだけでOK。

function getUnreadNotificationMail() {
  const unReadThreads = GmailApp.search("from:noreply@google.com is:unread");
}

メールはHTML形式 & テンプレフォーマットで届くので、
必要な情報をごちゃごちゃして抜き出す。
抜き出すのは下記のように。

function getUnreadNotificationMail() {
  const unReadThreads = GmailApp.search("from:noreply@google.com is:unread");
  unReadThreads.forEach(function(unReadThread, index, array) {
    const mails = unReadThread.getMessages();
    mails.forEach(function(mail, index, array){
      if (mail.isUnread()){
        const items = createItems(mail);
        if (items != null) {
            // ここにSlackへの通知処理を記載する
        }
      }
    });
  });
}

function extractBody(body) {
  if (body.indexOf("しました") == -1) {
    // テンプレフォーマット以外は無視
    return null;
  }
  //本文から必要な範囲を取り出す
  return body.substring(0,body.indexOf("しました") + "しました".length);
}

function createItems(mail) {
  const message = extractBody(mail.getBody());
  if (message == null) {
    return null;
  }
  //通知に必要な情報をオブジェクトにまとめる
  const items = {};
  // タイトルから変更ページ名を取得する。
  // タイトルの頭にはサイト名が着いているのでそこは削除
  items.text = mail.getSubject().substring("[サイト名称]".length) + "\r" + mail.getDate().toLocaleString();
  // 更新者取得
  items.author_name = message.substring(message.indexOf(">") + 1, message.indexOf("さん") + "さん".length);
  // 更新ページ名取得
  items.title = message.substring(message.indexOf(">", 80) + 1, message.indexOf("<", 80)) + message.substring(message.lastIndexOf("を"));
  // 更新ページURL取得
  items.title_link = message.substring(message.indexOf("http://sites.google.com/site/"), message.indexOf("\"", message.indexOf("http://sites.google.com/site/")));
  return items;
}

Slackに変更内容を通知する

toke取得

SlackAPIを使うのにtokenが必要となる。
ここからtoken取得しておく。

Slackに通知を飛ばす

Slackへの通知はAPIを使う。
プレーンテキストで送ることもできるが、見やすいようにちょっと見た目を変えて上げる。

function postNotice(items) {
  const token = "先程取得したトークン";
  const channel = "チャンネル名";
  const username = "通知するBOT名";
  const attachments = JSON.stringify([
      {
        color: "#89CEEB", //インデント線の色
        author_name: items.author_name, //インデント内に表示される著者名
        title: items.title ,//インデント内に表示されるタイトル
        title_link: items.title_link,//そのリンク
        text: "上記リンクをクリックすると対象のページやファイルを表示します。" //インデント内に表示されるテキスト
      }
    ]);
  const payload = {
    "channel" : channel, //通知先チャンネル名
    "text" : items.text, //送信テキスト
    "username": username, //BOTの名前
    "attachments": attachments //リッチなメッセージを送る用データ
  };
  const option = {
    "method" : "POST", 
    "payload" : payload
  };
  return UrlFetchApp.fetch("https://slack.com/api/chat.postMessage?token=" + token, option);
}

この関数をgetUnreadNotificationMail()のコメント部で呼び出してあげれば完成。

まとめ

コードの最終形は下記の通り。

function getUnreadNotificationMail() {
  const unReadThreads = GmailApp.search("from:noreply@google.com is:unread");
  unReadThreads.forEach(function(unReadThread, index, array) {
    const mails = unReadThread.getMessages();
    mails.forEach(function(mail, index, array){
      if (mail.isUnread()){
        const items = createItems(mail);
        if (items != null) {
          // ここにSlackへの通知処理を記載する
          const result = JSON.parse(postNotice(items));
          if (result.ok) {
            mail.markRead();
          }
        }
      }
    });
  });
}

function extractBody(body) {
  if (body.indexOf("しました") == -1) {
    return null;
  }
  //本文から必要な範囲を取り出す
  return body.substring(0,body.indexOf("しました") + "しました".length);
}

function createItems(mail) {
  const message = extractBody(mail.getBody());
  if (message == null) {
    return null;
  }
  //通知に必要な情報をオブジェクトにまとめる
  const items = {};
  items.text = mail.getSubject().substring("[UbiquitousHome]".length) + "\r" + mail.getDate().toLocaleString(); //年/月/日 時:分:秒 JST形式になる
  items.author_name = message.substring(message.indexOf(">") + 1, message.indexOf("さん") + "さん".length);
  items.title = message.substring(message.indexOf(">", 80) + 1, message.indexOf("<", 80)) + message.substring(message.lastIndexOf("を"));
  items.title_link = message.substring(message.indexOf("http://sites.google.com/site/"), message.indexOf("\"", message.indexOf("http://sites.google.com/site/")));
  return items;
}

function postNotice(items) {
  const token = "先程取得したトークン";
  const channel = "チャンネル名";
  const username = "通知するBOT名";
  const attachments = JSON.stringify([
      {
        color: "#89CEEB", //インデント線の色
        author_name: items.author_name, //インデント内に表示される著者名
        title: items.title ,//インデント内に表示されるタイトル
        title_link: items.title_link,//そのリンク
        text: "上記リンクをクリックすると対象のページやファイルを表示します。" //インデント内に表示されるテキスト
      }
    ]);
  const payload = {
    "channel" : channel, //通知先チャンネル名
    "text" : items.text, //送信テキスト
    "username": username, //BOTの名前
    "attachments": attachments //リッチなメッセージを送る用データ
  };
  const option = {
    "method" : "POST", 
    "payload" : payload
  };
  return UrlFetchApp.fetch("https://slack.com/api/chat.postMessage?token=" + token, option);
}

参考サイト

Google Apps Scriptを用いてライブラリを使わずにSlackにGoogleサイトの変更通知を投稿する