GASでSpreadSheetのデータを毎週Slackに通知する(Google Apps Script)

@Panda_Program

弊社(2018時点の前職)では、毎週月曜日に朝礼と掃除があります。 毎週イントラネットの掲示板の当番スレッドを見に行かねばならず、 煩雑だったので会社全体のSlackに通知するようにしました。

実現すること

毎週月曜日の朝に、朝礼の担当者と掃除のグループをSlackで通知する

仕様

朝礼担当

  • その日の担当者と、翌週の担当者の名前を通知する
  • 3ヶ月先まで、予定日と担当者は決まっている
  • 朝礼は2人1組である
  • 一度担当したメンバーは、リストから除外する

掃除グループ

  • チーム分け、チームメンバー、掃除場所を通知する
  • 掃除場所は、毎週ローテーションする

その他

  • 月曜日が祝日なら通知しない

なぜGoogle Apps Scriptを選択したか

  • 朝礼担当者はイントラの掲示板で管理
  • 掃除チーム、掃除場所はスプレッドシートで管理

→ スプレッドシートで一元管理したかったから

成果物

Slackの通知画面

準備すること

シート「朝礼当番」「掃除チーム」を作成

今回はGoogle Apps Scriptのコード内でシートを指定するので、それぞれシート名をつけます。

SpreadSheetの画面

SpreadSheetの画面

コード

Google Apps Scriptはサーバー不要で関数だけ書けば済むFaaSなので、作成した関数ごとに解説します。今回作成した関数は5つです。

notifyMondayMorningInfo()       // Slackに送る本文をまとめる
fetchMCs()                      // 「朝礼当番」シートから朝礼担当者の名前を取得する
fetchCleaningAreasAndMembers()  // 「掃除チーム」シートから掃除チームと掃除場所を取得する
rotateCleaningTeam(sheet)       // 掃除場所をローテーションする
setForSlack(body, channel)      // SlackのWebhook URLにHTTPリクエストを送る

順番にみていきましょう。

Slackに送る本文をまとめる

function notifyMondayMorningInfo() {

  //祝日なら実行しない
  var currentDate = new Date();
  var calendar = CalendarApp.getCalendarById('ja.japanese#[email protected]');

  if (calendar.getEventsForDay(currentDate, {max: 1}).length > 0) {
    return;
  }

  // 朝礼当番と掃除当番の本文を合わせる
  messageBody = fetchMCs() + '\n\n' + fetchCleaningAreasAndMembers();

  // Slackのチャンネルを指定する
  setForSlack(messageBody, '#slack連携のテスト');
}

毎週月曜日のトリガーで実行する関数はこれです。トリガーについては「Google Apps Script で毎日決まった時刻にスクリプトを実行するトリガー設定」を参照。

「朝礼当番」シートから朝礼担当者の名前を取得する

function fetchMCs() {

  // メンバーのデータをスプレッドシートから取得
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('朝礼当番');

  // 本日と来週の担当メンバーを取得
  var data = sheet.getRange('B1:C2').getValues();

  // 来週のメンバーが書き込まれてるか否かでメッセージを変更する
  if (data[1][1] == '') {

    // 来週のメンバーが記入されていない場合はこちらの文章を送る
    var mcBody =
       '本日の朝礼担当は ' + data[0][0] + 'さん と ' + data[0][1] + 'さん です。' + '\n'
        + 'なお、次回の朝礼当番はまだ記入されていません😅' + '\n'
        + '当番決めをお願いします!' + '\n'
        + 'https://docs.google.com/spreadsheets/d/Spreadsheetのハッシュ値/';
  } else {

  // 普段の文章
  var mcBody =
      '本日の朝礼担当は ' + data[0][0] + 'さん と ' + data[0][1] + 'さん です。' + '\n'
       + '来週は ' + data[1][0] + 'さん と ' + data[1][1] + 'さん です。' + '\n'
       + 'よろしくお願いします😆';
  }

    // 順番が来た朝礼当番の名前を削除。
    // 1行目が削除される
    sheet.deleteRow(1);

    return mcBody;
}

「朝礼当番」シート

SpreadSheetの画面

getRange関数で範囲を指定し、getValues関数で値を取得します。

Logger.log(data)で変数dataの中身を出力すると、多次元配列になっています。

GASのログ画面

変数mcBodyでは、この多次元配列から値を一つずつ取り出しています。

なお、sheet.deleteRow(1)を実行すると、このようにシートの1行目が削除されます。

SpreadSheetの画面

「掃除チーム」シートから掃除チームと掃除場所を取得する

SpreadSheetの画面

function fetchCleaningAreasAndMembers() {

  // メンバーのデータをスプレッドシートから取得
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('掃除チーム');

  // 本日と来週の担当メンバーを取得
  var data = sheet.getRange('A1:C4').getValues();

  // 場所とメンバーを文字列にまとめる
  var AreasAndMembers = '';
  data.forEach(function(value){
        AreasAndMembers += value[0] + ' : ' + value[1] + '\n' + '                    ' + value[2] + '\n\n';
  });

  var cleaningBody =
        '本日の掃除場所' + '\n'
        + AreasAndMembers
         + '詳細はこちら' + '\n'
         + 'https://docs.google.com/spreadsheets/d/Spreadsheetのハッシュ値/'
         ;
  // 掃除場所をローテーションさせる
  rotateCleaningTeam(sheet)

  return cleaningBody;
}

Logger.log(data)で変数dataの中身を出力すると、このようになります。

GASのログ画面

これでは扱いづらいので、文字列にして変数AreasAndMembersにまとめて入れてしまいます。ちなみに、forEach内のvalue[1]とvalue[2]の間でスペースを入れているのは、Slackに通知した時に見やすくするためです。

変数AreasAndMembersの中身はこのようになります。

GASのログ画面

掃除場所をローテーションする

function rotateCleaningTeam(sheet) {

  // 掃除場所のレンジオブジェクトを取得
  var areaRange = sheet.getRange('B1:B4');

  var areas = areaRange.getValues();

  // 配列の銭湯の要素を一番最後に移動させる
  areas.push(areas.shift());

  // シートにローテーション結果を記入する
  areaRange.setValues(areas);
}

ここでは配列の操作をしています。

shift()の返り値が配列areasの先頭の要素なので、それをpush()で配列の一番最後に持っていきます。その配列をスプレッドシートに記入すると、ローテーションの完成です。

(実行前) SpreadSheetの画面

(実行後) SpreadSheetの画面

B列がローテーションしているのがわかります。

なお、同じGoogle Apps Script内で何度もSpreadsheetを呼び出すと関数の実行速度が遅くなります。このため、関数の引数にsheetを取ることにより、「掃除チーム」シートの呼び出しを1回で済むようにしています(Google Apps Scriptでは関数の実行に5分以上かかると動作が停止します)。

SlackのWebhook URLにHTTPリクエストを送る

function setForSlack(body, channel) {
  var url = 'https://hooks.slack.com/services/ハッシュ値';

  // Slackに通知する際の名前、色、画像を決定する
  var data = {
    'channel' : channel,
    'username' : '朝礼・清掃当番教える君',
    'attachments': [{
      'color': '#008000',
      'text' : body,
    }],
    'icon_url' : 'https://2.bp.blogspot.com/-pFOv6JJOGbc/Wb8gR79McsI/AAAAAAABGwk/X4rTOgVBt206izOmKKUpVGV49o4NDxAYACLcBGAs/s800/group_young_people.png'
  };

  var payload = JSON.stringify(data);
  var options = {
    'method' : 'POST',
    'contentType' : 'application/json',
    'payload' : payload
  };

  UrlFetchApp.fetch(url, options);
}

この関数ではSlackに通知するときの表示名、アイコンなどを設定し、POSTでHTTPリクエストを送ります。

コードの検討

notifyMondayMorningInfo()

◆ 月曜日の日付

そもそもA列のカラムで祝日を避けた日付を記入しているので、A1セルの日付を使ってif文を作り、「今日がA1セルの日であれば、以下の動作を行う」というコードを、この関数の一番頭に持ってくることもできます。

一方、人の手で祝日を避けた月曜日の日付を入力すると、仮にミスがあった場合、slackに通知されてしまいます。実際に、別箇所に記載されている朝礼担当のリストには本来祝日である7/16が記載されていました。なので、できるだけ人の手は避けるべきと思っています。

ただ、それは自分がカレンダーをみながら修正すればいいので、単なる好みの問題だと思います。しかし、カレンダーではお盆休みは反映されませんし、一長一短ですね。

fetchMCs()

◆ スプレッドシートの担当者

例えば、一つのセルに「Aさん、 Bさん」と入れると、変数dataは多次元配列にならずに済みます。

ただ、新しく担当者リストを作成する際、名前を記入する人が面倒だと思うのでやめました。

なお、翌週の担当者が記入されていない場合は、Slackの文言が変わります。

Slackの通知

あとがき

エンジニアになって半年が経ったので何か残そうと思ったのと、Google Apps Scriptを使ってみたかったのでSlack連携のコードを書いてみました。会社でも好評なので作ってよかったです。皆様の参考になればと思います。

以上、プログラミングをするパンダ(@panda_program)でした。

その他参照

掃除当番の割当を Google Apps Script で自動化して Slack の BOT として通知

Happy Coding 🎉

パンダのイラスト
パンダ

記事が面白いと思ったらツイートやはてブをお願いします!皆さんの感想が執筆のモチベーションになります。最後まで読んでくれてありがとう。

  • Share on Hatena
  • Share on Twitter
  • Share on Line
  • Copy to clipboard