🚀 ニフティ’s Notion

8. GAS(演習)

演習. スプレッドシートの情報をもとに定期的にSlack投稿する。

背景

朝会の当番をローテーションで回していて、今日の当番をチャンネルに通知したい。

作るもの
  • スプレッドシート「朝会当番シート」
    image block
  • 定期的に以下を実行するスタンドアロンなApps Script
    • スプレッドシート「朝会当番シート」の当番の人(列「当番」に◯がついている人)をSlackチャンネルに通知する。
    • 列「当番」の◯を次の人にずらす。
      image block
      • 一番下の行の場合は一番上の人にする。
        image block
    • 本来は、毎朝特定の日時に実行するが、今回は動作確認のため1分間隔で実行するようにする。
(1) スプレッドシートを作成する。
  • 以下のようなスプレッドシート「朝会当番シート_<自分の名前>」をマイドライブ上に作成する。
    名前の列(A列)はお好みで変えてOK。
    image block
  • 作成したスプレッドシートのURLの <file_id> の部分をメモする。
    • https://docs.google.com/spreadsheets/d/<file_id>/edit#gid=xxxxx
(2) スタンドアロンなApps Scriptを作成する。
  • マイドライブ上の適当な場所で右クリックをし、「その他 > Google Apps Script」を選択する。
    image block
  • 赤枠の箇所をクリックして、Apps Scriptの名前を変える。
    image block
  • 以下のソースコードをスクリプトエディタに張り付ける。
    function myFunction() {
      // IDからスプレッドシートを開く。
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app?hl=ja#openbyidid
      const sheetId = "<自分で作成したスプレッドシートのID>";
      const spreadSheet = SpreadsheetApp.openById(sheetId);
    
      // シート名からシートを取得する。
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getSheetByName(String)
      const sheet = spreadSheet.getSheetByName("当番表");
    
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getdatarange
      const range = sheet.getDataRange();
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/range?hl=ja#getlastrow
      const rowCount = range.getLastRow();
      // 指定された座標から始まる範囲の矩形グリッド(2次元配列)を返す。
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getsheetvaluesstartrow,-startcolumn,-numrows,-numcolumns
      const values = sheet.getSheetValues(2,1,rowCount - 1,2);
    
      // 今日の当番を取得
      let currentAssignee = "";
      let currentAssigneeIndex = -1;
      for(let i=0; i < values.length; i++){
        let row = values[i];
        if(row[1] == "◯"){
          currentAssignee = row[0];
          currentAssigneeIndex = i;
          break;
        }
      }
      // 今日の当番をログに出力
      Logger.log("今日の当番:" + currentAssignee);
    
      // 当番をずらす
      // A1表記でセルを指定して、そのセルの値を変更する。
      // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getrangea1notation
      // A1表記 : https://developers.google.com/sheets/api/guides/concepts?hl=ja#cell
      sheet.getRange("B"+(currentAssigneeIndex + 2).toString()).setValue("");
      let nextAssigneeIndex = (currentAssigneeIndex + 1) % (rowCount - 1);
      sheet.getRange("B"+(nextAssigneeIndex + 2).toString()).setValue("◯");
    }
  • ソースコードの変数 sheetId の値を自分で作成したスプレッドシートのID(手順(1)でメモした <file_id> )にする。
  • 「実行」ボタンを押すと、以下のようなメッセージが出るので「権限を確認」を選択。
    image block
  • 自分のアカウントを選択し、「許可」ボタンを押す。
  • 実行すると、実行ログが表示され、その中にスプレッドシートに◯がついた今日の当番当番が表示される。
  • スプレッドシートを確認すると、当番カラムの◯が一つずれていることが確認できる。
    image block
(3) Slackに通知するようにする。
  • Slack Incoming Webhookを使って、Slackのチャンネルにメッセージを投稿するようにする。
  • ファイルの末尾に以下を追加し、変数 url の値を作成したIncoming WebhookのURLに置き換える。
    function postMessage(message){
      const data = {
        'text': message
      };
      const options = {
        'method' : 'post',
        'contentType': 'application/json',
        'payload' : JSON.stringify(data)
      };
      // Incoming WebhookのURLを指定
      const url = "<作成したIncoming Webhook>";
      // Incoming WebhookをPOSTで叩き、Slackにメッセージを投稿する。
      // ref. https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app?hl=ja#fetchurl,-params
      UrlFetchApp.fetch(url, options);
    }
  • myFunction()の末尾に以下を追加し、動作確認を容易にするためauthorの値を自分の名前にする。
      // 担当者をSlackチャンネルにメッセージ投稿する。
      const author = "山田 太郎";
      const message = `今回の当番 : ${currentAssignee}\n(GASの作者 : ${author})`;
      postMessage(message);
ソースコード全文
function myFunction() {
  // IDからスプレッドシートを開く。
  // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app?hl=ja#openbyidid
  const sheetId = "<自分で作成したスプレッドシートのID>";
  const spreadSheet = SpreadsheetApp.openById(sheetId);

  // シート名からシートを取得する。
  // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getSheetByName(String)
  const sheet = spreadSheet.getSheetByName("当番表");

  // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getdatarange
  const range = sheet.getDataRange();
  // ref. https://developers.google.com/apps-script/reference/spreadsheet/range?hl=ja#getlastrow
  const rowCount = range.getLastRow();
  // 指定された座標から始まる範囲の矩形グリッド(2次元配列)を返す。
  // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getsheetvaluesstartrow,-startcolumn,-numrows,-numcolumns
  const values = sheet.getSheetValues(2,1,rowCount - 1,2);

  // 今日の担当者を取得
  let currentAssignee = "";
  let currentAssigneeIndex = -1;
  for(let i=0; i < values.length; i++){
    let row = values[i];
    if(row[1] == "◯"){
      currentAssignee = row[0];
      currentAssigneeIndex = i;
      break;
    }
  }
  // 今日の担当者をログに出力
  // ref. https://developers.google.com/apps-script/reference/base/logger?hl=ja#logdata
  Logger.log("今日の担当者:" + currentAssignee);

  // 担当者をずらす
  // A1表記でセルを指定して、そのセルの値を変更する。
  // ref. https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja#getrangea1notation
  // A1表記 : https://developers.google.com/sheets/api/guides/concepts?hl=ja#cell
  sheet.getRange("B"+(currentAssigneeIndex + 2).toString()).setValue("");
  let nextAssigneeIndex = (currentAssigneeIndex + 1) % (rowCount - 1);
  sheet.getRange("B"+(nextAssigneeIndex + 2).toString()).setValue("◯");

  // 担当者をSlackチャンネルにメッセージ投稿する。
  const author = "山田 太郎";
  const message = `今回の当番 : ${currentAssignee}\n(GASの作者 : ${author})`;
  postMessage(message);
}

function postMessage(message){
  const data = {
    'text': message
  };
  const options = {
    'method' : 'post',
    'contentType': 'application/json',
    'payload' : JSON.stringify(data)
  };
  // Incoming WebhookのURLを指定
  const url = "<作成したIncoming Webhook>";
  // Incoming WebhookをPOSTで叩き、Slackにメッセージを投稿する。
  // ref. https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app?hl=ja#fetchurl,-params
  UrlFetchApp.fetch(url, options);
}
  • 「実行」ボタンを押すと、権限の承認を要求されるので承認する。
  • 実行すると、Incoming Webhookで投稿先として指定したSlackチャンネルに当番が投稿され、実行するたびに当番がローテーションすることも確認できる。
    image block
(4) 定期実行させる。
  • このスクリプトを定期的に実行するようにする。
    • 実際は「毎朝8時台に定期実行する」みたいな設定になるが、今回は動作確認のため、毎分実行するようにする。
  • 左のメニューから「トリガー」を選択し、右下の「トリガーを追加」ボタンを押す。
    image block
  • 以下のようにトリガーを設定し、「保存」ボタンを押す。
    image block
  • 1分間隔でSlackチャンネルに当番が投稿され、スプレッドシートの当番列がローテーションされていることが確認できる。
  • Slackチャンネルがメッセージで埋め尽くされるので、キリがいいところ(5~10分ぐらい)で追加したトリガーを削除すること。
    • 左のメニューから「トリガー」を選択し、作成したトリガーの右のケバブメニューから「トリガーを削除」を選択して、「完全に削除」を選択する。
      image block
      image block

時間がある方向け

📄 8-ex. GAS(発展)

時間が余った方は以下の演習に取り組む。

  • 📄 2-ex. Slack(発展) で作成したスプレッドシートに以下のApps Scriptを作成する。
    • スプレッドシートが変更されたら、その内容をSlackに投稿する。

次の資料

📄 9. どうやって自動化するか(課題の見つけ方)