スプレッドシートと Slack を連携させてタスク管理 bot を作る

やりたいこと

タスクと作業日の入力されたスプレッドシートから、 その日作業する必要のあるタスクを整形して Slack に毎朝 POST してくれるような タスク管理 bot を作成したい。

こんな感じの。

スクリーンショット 2019-09-22 14.47.04.png

というわけで、bot 作成までの流れをひととおりまとめておきます。

2020/ 4/ 5 GAS の V8 ランタイム導入によりテンプレートリテラルが使用可能になったため、サンプルコードを一部変更いたしました。

準備

  • Google スプレッドシート(タスクリスト)
  • POST したい Slack チャンネル
  • Slack Incoming Webhooks URL(後述します)

スプレッドシートはこんな感じのサンプルを用意します。 キャプチャには写っていませんが、シート名は schedule にしておきます。

スクリーンショット 2019-09-22 14.46.10.png

実装する必要のある機能

  • スプレッドシートからデータを取得
  • Slack 用にテキスト整形
  • Slack に POST
  • 以上のスクリプトを毎朝実行

前準備として、スクリプトから Slack に POST する機能を実装するにあたって、 Slack Incoming Webhooks という外部連携の設定を行う必要があります。

Slack Incoming Webhooks

POST したい Slack チャンネルの URL を取得、設定をします。

こちらの記事を参考にしつつ。 Slack の Webhook URL 取得手順 設定ができたら、ターミナルから簡単なメッセージを投げてみて動作確認をしてみましょう。

curl -X POST -H 'Content-type: application/json' --data '{"text":"SampleText"}' https://hooks.slack.com/services/取得したURL

上記のコマンドを実行して、Slack に SampleText が POST されれば OK です。

GAS から簡単なメッセージを投げてみる

機能実装のためのスクリプトは GoogleAppScript(GAS) に記述します。 JavaScript ベースなので、フロントエンドの方は作業しやすいのではないかと思います。 (ES6 には対応しておらず、let 宣言やテンプレートリテラルなどは使用できないようです。つらい。) V8 ランタイム導入により ES6 記法に対応されたようですね、うれしい!

GAS では、

  1. 先にスクリプトを作成してスプレッドシートに紐つける方法と、
  2. スプレッドシートに紐ついたスクリプトを生成する 方法があるようです。

今回は後者の方法で進めていきますので、スプレッドシートのメニューから ツール > スクリプトエディタ で作成します。

メッセージ形成の機能を後で追加しやすいように、WebhookURL を管理する部分とメッセージの作成部分を別関数で作成します。

function sendMessage() {
  //Webhook URLを以下に入力
  const postUrl = "https://hooks.slack.com/services/取得したWebhookをここに入力します";
  const sendMessage = createMessage();
  const jsonData = {
    "text": sendMessage
  };
  const payload = JSON.stringify(jsonData);
  const options = {
    "method": "post",
    "contentType": "application/json",
    "payload": payload
  };
  UrlFetchApp.fetch(postUrl, options);
}

function createMessage() {
  //ここでメッセージの中身を作成します
  var message = "SampleText";
  return message;
}

スクリプトが準備できたら、メニューから sendMessage を実行します。 先ほどと同様、Slack に SampleText が POST されれば、GAS と Slack 連携の機能実装は完了です。

スプレッドシートのセルを取得して Slack に POST してみる

sendMessage は変更する必要がないので、createMessage の中身のみ書き換えていきます。

function createMessage() {
  //spreadsheetからscheduleのシートを取得
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = spreadsheet.getSheetByName("schedule");//シート名

  const output = sheet.getRange(2, 3).getValue(); //C2(2行目, 3列目セル)の値を取得
  return output;
}

スプレッドシートから出力するタスクを整形

スプレッドシートから必要な情報をいい感じに POST できるように、タスクを整形します。 以下のような感じで機能を実装します。

  • 作業予定日に本日の日付が入力されていれば、作業内容と納品日を整形してメッセージに追加
  • 備考もあれば追加
  • これをデータのあるすべての行に対して実行
  • タスクがなければ本日のタスクはありませんと POST
function createMessage() {
  //spreadsheetからscheduleのシートを取得
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = spreadsheet.getSheetByName("schedule");

  const dataObject = new Date();
  const today = Utilities.formatDate(dataObject, "Asia/Tokyo", "yyyy/MM/dd");

  const detailColumn = 2; //作業内容の入力されているカラム
  const workDateColumn = 3; //作業日が入力されているカラム (C列)
  const publishDateColumn = 4; //公開日の入力されているカラム
  const commentColumn = 5; //備考が入力されているカラム
  const startRow = 2; //列データはタイトルを除き2行目から

  const columnFVals = sheet.getRange('C:C').getValues(); //C列の値を配列で取得
  const lastRow = columnFVals.filter(String).length; //空白を除き、配列の数を取得

  var taskList = ""; //タスクリスト
  var output = ""; //createMessageからの最終的な出力

  for (row = startRow; row <= lastRow; row++) {
    var detail = sheet.getRange(row, detailColumn).getValue() + " ";
    //作業予定日を取得
    var workDate = sheet.getRange(row, workDateColumn).getValue();
    var publishDate = sheet.getRange(row, publishDateColumn).getValue();
    var comment = sheet.getRange(row, commentColumn).getValue();

    //日付の整形
    workDate = Utilities.formatDate(workDate, "Asia/Tokyo", "yyyy/MM/dd");
    publishDate = Utilities.formatDate(publishDate, "Asia/Tokyo", "MM/dd");

    //今日の日付と作業予定日を比較、taskListの生成
    if (today == workDate) {
      taskList = `${taskList}${detail}納品日時:${publishDate} ${publishTime}` + "\n";
      if (comment !== "") {
        taskList = `${taskList}  - ${comment}` + "\n";
      }
    }
  }

  //最終的な出力の生成
  if (taskList == "") {
    output = `${today}のタスクはありません`;
  } else {
    output = `${today}のタスクです \`\`\`${taskList}\`\`\``;
  }

  return output;
}

こうしてできあがったものが、一番最初に貼り付けてあったこちらになります。

スクリーンショット 2019-09-22 14.47.04.png

タスクリストを出力するためには sendMessage 関数を実行する必要があるので、 最後に GAS が毎朝 sendMessage 関数を実行するように設定します。

関数の定期的な実行

GAS に実行タイマーを設定すれば OK です。 下記赤枠部分から実行トリガーを設定できます。

スクリーンショット 2019-09-22 15.11.17.png

トリガーを追加 → 以下のように設定で完了です。

スクリーンショット 2019-09-22 15.16.46.png

これで毎日 9-10 時くらいにその日のタスクを通知してくれる bot ができました。 うれしい。