見出し画像

Google Apps Script×Slack apiでつくるおこづかい帳管理(はじめてのSlackbot)

仕事でGoogle Apps Script(GAS)を触る機会ができて、「もうちょっと自分でスクリプトがかけるようになりたいな」と、私はこのところ勉強欲が高まっていました。
そこでスプレッドシートで管理している子どものおこづかい帳をつかって、GASでおこづかい帳から必要データを取り出し、Slackに通知する、そんなSlackbotを作ってみることにしました。

初心者の私がつまづいたところ、理解のポイントだったな、と振り返って思うことを備忘録的にまとめていきたいと思います。

はじめに(背景)

子ども世代のお金の使い道って、自分が子どもの頃からだいぶ変化したな、とつくづく私は感じています。長女は小学五年生ですが、漫画雑誌にお金を使うことはなく(私はりぼん、なかよしを友達と協力して買っていた世代)、彼女はLINEスタンプ、ネットゲームのアイテム…いわゆるネット課金におこづかいをつぎこんいます。

もっと小さい頃は現金でおこづかいを渡していましたが、だんだんと現金のおこづかいを親が受け取って、ネット課金を代行する、、、ということが増えました。

そのため我が家では、スプレッドシートでおこづかいを管理し、毎月の入金(収入への追記)、ネット課金の要望を申告してもらい支出反映、もしくは要望に応じて現金化というようなスタイルへ、おこづかい管理が変化しました。

やりたいこと

そんなわけで、スプレッドシートのおこづかい帳へ収入、支出を書き込む手間をできるだけ少なくしたい、具体的にはスプレッドシートをいちいち開かずに行いたい。
夫婦間での連絡手段にSlackをつかっているので、Slack上から行えるようにすることが目標です。

【必要な機能】

  • 毎月のおこづかい入金をもれなく行う
    (リマインド+Slackからの書き込み)

  • ネット課金申請をGoogle Formにし、通知・承認までをSlackで行う

  • (おまけ)Slackからおこづかい帳を簡単に呼び出す

完成したもの

やったことは後ほど書くことにして、まずは完成したものを紹介します。
また、今回はネット課金申請をGoogle Formから申請してもらって、Slack上から承認する、という機能について書きたいと思います。

おこづかいbot ネット課金申請&承認
  1. Google Formからおこづかい利用申請をしてもらう

  2. おこづかいスプレッドシート「フォームの回答1」Sheetに内容反映

  3. フォームの回答内容を、Slackへ自動通知

  4. Slackのメッセージボタンを押したら、おこづかいスプレッドシートの「シート1」Sheet※に内容を反映
    ※シート1がおこづかいを管理しているシート

  5. スプレッドシートに内容が反映されたら、承認通知をSalckへ自動送信

小話1

4番の操作をするにあたって、Slack apiを使う必要が出てきました。
私はAPIってなに?という状態だったのですが、私のざっくりした理解は「翻訳コンニャク」である、ということと理解しました。さまざまなサービス間の翻訳をして、会話できるようにしてくれる。

そしてこのapiを通じたやりとりをするために、サーバーが必要であることも今回初めて知りました。GASであればサーバーレスでできるとのこと。(Googleが受信できるための環境とスクリプトを準備してくれてる)
おお。素晴らしい。

…というように、全く仕組みが理解できていない状態だったので、作り始める前にKildle Unlimitedに入ってたこの本を読みました。

作成工程

  1. Slackで新規アプリを作成・WebHookURLをゲット 参照

  2. スプレッドシートの更新内容をSlackメッセージに送信するスクリプトを書く

  3. Slackでメッセージボタンが押されたときに行うことをGASで準備・デプロイする

  4. SlackアプリにデプロイしたスクリプトURLを設定 参照

  5. GASのトリガーを設定して完成!

Slack apiの設定については、参照のURLが個人的にはとてもわかりやすかったので、ここでの説明は割愛します。
ここから先は、実際に作成したスクリプトと参照ページと異なる設定をしたところを中心に書いていきます。


作ったスクリプト1:申請内容をSlackへ自動通知

Slackへ自動通知する目的は、
①申請されたことに気が付く
②スプレッドシートを開かずに、Slackから承認・シートへの反映
の2つです。
②を実現するために、Slackメッセージにボタンをつけることにしました。これはSlackのBlock kitという仕組み(?)で実現できます。

function myFunction() {
//今使ってるファイルを取得
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); 
  var sheet = spreadsheet.getSheetByName("フォームの回答 1")

//最新の申請内容の確認。フォームの回答1Sheetの最終行の内容となる。
//A1セルから下方向に最終行の番号を取得
  var lastrow = sheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
 
//申請された内容を抽出する 
  var dataUse = sheet.getSheetValues(lastrow, 2, 1, 1); //用途
  var dataAmount = sheet.getSheetValues(lastrow, 3, 1, 1);//金額
  var apply = "用途:" + dataUse + "\n金額:" + dataAmount + "円\n"

//現在の残高を確認する
  var balance = spreadsheet.getSheetByName("シート1").getRange(1, 2).getValue();
  Logger.log(balance);

//Slackに申請内容、現在の残高を内容に入れてメッセージ送信する
  sendMsg(lastrow, apply, balance);//具体的な送信スクリプトは↓↓↓function sendMsgへ
}



//slackにメッセージを送る====================================
function sendMsg(lastrow, apply, balance){
  var slackWebhooks = "SlackのIncoming Webhooksのアドレスを入力する"//カスタマイズ必要
  var celllink = おこづかいスプレッドシート、管理シートのA列のリンクを入力する//カスタマイズ必要 + lastrow;

//Slackにリンクつきメッセージを送る記法は <リンク|表示させたい文言>
  var msg = "●●ちゃんからお小遣い使用申請があります。<" + celllink + "|確認>しましょう\n\n現在の残高は" + balance + "円です\n\n【申請内容】\n" + apply;

  var blockKit = [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": msg
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "OK楽しく使ってね"
          },
          "style": "danger",
          "value": "ok",
          "action_id": String(lastrow)
        }
      ]
    }
  ];

  var payload    = {"blocks": blockKit};
  var options =
  {
    "method":"post",
    "contentType":"appliction/json",
    "payload":JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  UrlFetchApp.fetch(slackWebhooks,options);
}

■理解のポイント

1. GASからSlackにメッセージを送るときは、テキストデータをJSONと言われるデータ形式に変換して送る必要がある

2. ボタン付きメッセージは、Block kitというSlackが準備した仕組みを利用するが、メッセージ部分とボタン部分でTypeが異なる

ボタン付きメッセージの部分は「type」を「action」にしないと、Slackからの応答が行われません(多分)。
また、Block kit部分は文字データを記入しておく必要があります。数値データがはいっていると、Slackへのメッセージを送る際にJSON形式への変換が行われるのですが、その時にエラーが出てしまうためです。

私はBlock kitのaction_idに変数(申請内容が書いてある行番号)を入れたかったので、lastrow(数値。行番号)をaction_idにそのまま記入したらエラーになってハマりました…。String(数値)で数値を文字列に変換。大事。

作ったスクリプト2:Slackでボタンが押されたらスプレッドシートを更新

Slackに申請内容を送信するとき「承認」のボタンだけをつけました。却下する場合には、何もスプレッドシートを更新しなくていいからです。
ボタンが押された場合にすることは
①フォームの回答内容をシート1(管理シート)に反映する
②反映したことをSlackに通知する
の2つをおこないます。

function doPost(e) {
  var parameter = e.parameter;
  var data = parameter.payload;
  var json = JSON.parse(decodeURIComponent(data));
  var slackWebhooks = //"SlackのIncoming Webhooksのアドレスを入力する"//;

//block kit のボタン押下すると、response_urlが含まれたpayloadがくるので、応答しないとその後の処理がすすまないとのこと
  let response_url = json.response_url;
  ack(response_url);//詳細の処理内容は↓↓function ack(url)に記載

 if (json.actions[0].value == "ok"){
    approveWrite(json);//詳細の処理内容は↓↓approveWrite(json)に記載
    var message = "承認しました!";
   } else {
    var message = "I don't understand.";
   }


  // ボタン押した後の対応を新規メッセージで返す
  sendMessage(slackWebhooks, message)

  return response()
};

 // acknowledgment response を返す==================
function ack(url) {
  let options = {
    "method": "post",
    "muteHttpExceptions": true
  }
  return UrlFetchApp.fetch(url, options)
}

//// Slackにメッセージを送信===========================
function sendMessage(url, message) {
  let payload = {
    "text": message
  }
  let options = {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  }
  return UrlFetchApp.fetch(url, options)
}


// フォームの回答内容をお小遣いシート本体に反映される==================
function approveWrite(json){
  var value = json.actions[0].value;
  var applyno = json.actions[0].action_id; //申請内容が記載されてる行番号

//こづかいスプレッドシートを取得
  var spreadsheet = SpreadsheetApp.openById("スプレッドシートのIDを記入する"); //開きたいスプレッドシートをIDで指定する

//「フォームの回答1」シートの申請内容が記載されてる行の内容を取得
  var ss1 = spreadsheet.getSheetByName("フォームの回答 1");
  var data = ss1.getRange(applyno, 2, 1, 2).getValues();

//Slackメッセージの小遣い申請承認のボタンが押されたことを確認できたら処理を進める
  if (value == "ok") {
  //シート1の最終行にフォームの内容を反映
  var copyto = spreadsheet.getSheetByName("シート1");
  var copyto_lastrow = copyto.getRange(3, 1).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();

  copyto.getRange(copyto_lastrow+1, 1).setValue(new Date());
  copyto.getRange(copyto_lastrow+1, 2).setValue(data[0][0]);//内訳に値をセット
  copyto.getRange(copyto_lastrow+1, 4).setValue(data[0][1]);//金額に値をセット

  }else{
  }
}

■理解のポイント

1. Slackからはpayloadというデータが送られきて、response_urlが書いてあるので、そのURLに反応を返してあげないと、Slack側は送信がエラーしたと認識してしまう。例えると、電話で「もしもし?」という問いかけに無言でいたら、「あれ?なんかがダメなのか?一回切ろうかな」ガチャ、という感じ。

2. GASは別サービスから送られてくる(Postされる)データは、必ず
function doPost(e)で受け取る


3. このスクリプトはスプレッドシートに紐づけずに保存するため、操作したいスプレッドシートはIDで指定する。IDの確認方法はこちらを参照

私は最初、response_urlへの応答が必要出ることがわからずに、「動かない」と泣きそうでした。まずは応答を返してあげると、そこから処理が進められるようになるそうです。

また、Slackから送られてくる payloadに含まれる項目も理解できてなくて、入ってない項目でif文を書いていたみたいです(恥)。
Slackへメッセージを送る時に設定した項目は、Slackからも返ってくる!という思い込みからでしたが、冷静に考えればそんなことはあるはずもなく。Slackからの返答はSlack公式が紹介してくれているので、こちらから確認するのが良いです。(Examples以降に記載してあります)

Slackからの応答スクリプトをデプロイして、SlackのRequest URLに設定する

デプロイって何?
という状態でしたが、公開することのようですね。。
デプロイの方法とSlackのRequest URLの設定は、こちらを参考にしました。Step3の「Web アプリとしてデプロイする」以降の手順です。

また、デプロイする時の注意点があります。
このRequest URLが変わってしまうと、自動応答できなくなってしまうので、スクリプトの更新を行なった時には「デプロイを管理」から内容の更新をかける必要があります。手順はこちらのページで詳しく解説してくれています。

スクリプトが動くトリガーを設定

さて、いよいよ設定の最後の項目です。
今回作ったGASのスクリプトは「何をきっかけに動くのか」、つまりトリガーを設定してあげる必要があります。

この仕組みのスタートは、Google Formの回答となります。
なので、「作ったスクリプト1:申請内容をSlackへ自動通知」のスクリプトに、こんな内容で↓トリガー設定をします。

トリガー設定手順

完成です!!!!!

振り返り

「スクリプトって本読んでもよくわからないし、実践できるいい内容ないかなぁ」と思って、気軽な気持ちで始めたのですが、個人的には思ったよりも壮大なお勉強になりました。
Slack apiでは「アプリをまず作りましょう」という説明文に行き当たり、「え!アプリをつくるの!?」となりましたし、思いがけず、APIとは、各種サービスにはサーバーがいるんだよ、といった基本事項も学ぶことができました。

やっぱりスクリプトを学ぶには実践あるのみ!ということ実感した取り組みでした。そしてもうちょっとスプレッドシート内だけの処理などの自動化を勉強したい。

そして、ハマった時に一人で抜け出すのは大変なので、サポーターを増やしたい今日この頃です。
ちなみに今回の強力なサポーターは、夫(エンジニア)とChromeの拡張機能のDeepLでした。Slackの説明、英語ページしかないところはDeepLが救ってくれました。感謝!


この記事が気に入ったらサポートをしてみませんか?