slackユーザーデータをJSONファイルとしてGoogleドライブに保存する

前回の記事の続きです。

前回:
GASを使ってslackユーザーデータを取得する(1000件以上対応) - シンプルに暮らしたい情シスのブログ
次回:


今回はAPIリクエストの結果(ユーザーデータ)をJSONファイルとしてGoogleドライブに保存するGASを作っていきます。

JSONファイルとして保存するメリット

リクエスト回数の節約

APIには「1時間に〇回まで」や、「1分間に〇回まで」といった制限が設けられていることが多いです。
今回使ったslack API(users.list)の場合、Rate limitsを見ると Tier2となっています。

Tier2のところをクリックすると、飛べるRate Limitsの解説ページではこのように記載があり、どうやら1分間に20回程度は行えるようです。

1000件を超えるユーザー数の場合は、1度の取得で2回以上のリクエストを行うため、実行回数によってはオーバーしてしまう可能性があります。
そこで1日に1回更新する程度で良いようなデータは、JSONファイルとして保存し、users.list APIを使う代わりにJSONファイルを読み込めば、APIの実行制限を超えるようなケースは避けられます。

呼び出し速度の向上

APIは便利かつ最新の情報を取得できますが、slackのサーバーへリクエストし、応答を待つ時間がかかります。
JSONファイルを読み込む場合もそれなりの時間はかかりますが、APIリクエストよりは早くなります。

事前準備

保存先フォルダ―IDのスクリプトプロパティ設定

今回は保存先フォルダ―のIDが必要となるため、スクリプトプロパティにセットしておきます。

まずはフォルダーを作成します(任意)

フォルダを開き、URLの folders/ より後ろの部分をコピーします。
(フォルダを右クリックしてリンクを取得からでもいけます)

GASを開いて歯車アイコン>スクリプトプロパティを編集

スクリプトプロパティを追加

プロパティ名を適当に決めて、値に先ほどコピーした文字を貼り付け、保存します。

これで準備は完了です。

コード

JSONファイルを作成するコード

まずは単純にJSONファイルを作成するコードです。
新規作成と既存ファイルの更新ではそれぞれ処理が異なるため、別々に書いていきます。

function createJson() {
  // スクリプトプロパティに設定したfolder_idを呼び出す
  const folder_id = PropertiesService.getScriptProperties().getProperty("folder_id");

  // ユーザーデータ(仮)
  const users_data = [
    { "name": "hogehoge" },
    { "name": "hagehage" }
  ];

  // 保存先のフォルダー情報を取得
  const folder = DriveApp.getFolderById(folder_id);

  // Blobオブジェクトを作成
  const content_type = "application/json";
  const file_name = "users_list_response.json";
  const blob = Utilities.newBlob("", content_type, file_name);
  blob.setDataFromString(JSON.stringify(users_data),"UTF-8"); // Blobオブジェクトにデータをセット

  // Blobオブジェクトをファイル化し、保存先フォルダーへ保存
  folder.createFile(blob);
}

これを実行すると、JSONファイルが作成されました。

中身を見てみると、

[{"name":"hogehoge"},{"name":"hagehage"}]

がちゃんと入っています。

ちなみに

  blob.setDataFromString(JSON.stringify(users_data),"UTF-8");

この部分でusers_dataをJSON.stringifyで囲っていますが、これをしない場合(データがJSONオブジェクトの場合)このようになってしまいます。

JSON.stringifyの処理をすることでJSONオブジェクトを文字列に変換しています。

JSONファイルを更新するコード

先ほど作成したファイルのIDを取得します。

リンクをコピー

取得したリンクの赤線部分がファイルIDです。

後ほどファイルIDを自動的に取得する処理を設けるので、
ここでははスクリプトプロパティには入れず、コード内に直接記載します。

function updateJson() {
  // 更新対象のファイルID
  const file_id = "xxxxxxxxxxxxxxxxxxxxxxxx";
  // 更新対象ファイルを取得
  const file = DriveApp.getFileById(file_id);

  // 更新するユーザーデータ(仮)
  const users_data = [
    { "name": "sato" },
    { "name": "suzuki" }
  ];

  // 更新対象ファイルへデータを上書きする
  file.setContent(JSON.stringify(users_data));
}

これを実行すると、先ほどのjsonファイルの中身が更新されました。

createJsonとupdateJsonの2つのコードで運用する場合、

  1. 初回はcreateJsonを実行
  2. 2回目以降はfile_idを設定し、updateJsonを実行

というちょっとイケてない運用になります。
1つのコードで作成・更新をしたいので、

  1. フォルダ内から指定の特定のファイル名のファイルを探す
  2. 無ければ作成
  3. あれば更新

という処理にしたいです。

保存先フォルダ内からファイルを探してIDを取得するコード

まずは保存先のフォルダから、特定のファイル名のファイルを探し、IDを取得します。

function searchFileId() {
  // スクリプトプロパティに設定したfolder_idを呼び出す
  const folder_id = PropertiesService.getScriptProperties().getProperty("folder_id");
  // 保存先のフォルダー情報を取得
  const folder = DriveApp.getFolderById(folder_id);
  // フォルダー内のファイル一覧を取得
  const files = folder.getFiles();
  // 探したいファイル名
  const file_name = "users_list_response.json";
  // ファイルIDを格納する変数
  let file_id;

  // ファイル一覧から1件ずつ探す
  while (files.hasNext()) {
    const file = files.next();
    // ファイルが見つかった時の処理
    if (file.getName() == file_name) {
      file_id = file.getId();
      break; // ファイルが見つかった時点でループ終了
    }
  }

  Logger.log(file_id);
}

実行すると上手く取得できました。

ファイルが見つからない場合はfile_idがnullになります。(変数宣言時の値になる)


JSONファイルの作成・更新を一括で行うコード

上記3つのコードを組み合わせて、作成・更新を1つのコードにまとめます。
良い関数名が思い浮かばなかったのでChatGPTに考えてもらいました。
(ちなみにcreateUpdateJsonあたりにしようと思ってました)

折角なので作った関数を少し変えて、引数を渡すことで同じ記述を避けるように改修します。

function createOrUpdateJson() {
  // スクリプトプロパティに設定したfolder_idを呼び出す
  const folder_id = PropertiesService.getScriptProperties().getProperty("folder_id");
  // 検索や作成に使うファイル名
  const file_name = "users_list_response.json";

  // ユーザーデータ(仮)
  const users_data = [
    { "name": "hogehoge" },
    { "name": "hagehage" }
  ];

  // フォルダ内にある対象のJSONファイルのIDを取得する
  const file_id = searchFileId(folder_id, file_name);

  if (file_id == null) {
    createJson(folder_id, file_name, users_data);
    Logger.log("ファイルが存在しないため作成");
  } else {
    updateJson(file_id, users_data);
    Logger.log("ファイルが存在するため更新");
  }
}

function createJson(folder_id, file_name, users_data) {
  // 保存先のフォルダー情報を取得します
  const folder = DriveApp.getFolderById(folder_id);

  // Blobオブジェクトを作成
  const content_type = "application/json";
  const blob = Utilities.newBlob("", content_type, file_name);
  blob.setDataFromString(JSON.stringify(users_data), "UTF-8"); // Blobオブジェクトにデータをセット

  // Blobオブジェクトをファイル化し、保存先フォルダーへ保存
  folder.createFile(blob);
}

function updateJson(file_id,users_data) {
  // 更新対象ファイルを取得
  const file = DriveApp.getFileById(file_id);

  // 更新対象ファイルへデータを上書きする
  file.setContent(JSON.stringify(users_data));
}

function searchFileId(folder_id, file_name) {
  // 保存先のフォルダー情報を取得
  const folder = DriveApp.getFolderById(folder_id);
  // フォルダー内のファイル一覧を取得
  const files = folder.getFiles();
  // ファイルIDを格納する変数
  let file_id;

  // ファイル一覧から1件ずつ探す
  while (files.hasNext()) {
    const file = files.next();
    // ファイルが見つかった時の処理
    if (file.getName() == file_name) {
      file_id = file.getId();
      break; // ファイルが見つかった時点でループ終了
    }
  }

  return file_id;
}

一度フォルダ内を空にし、createOrUpdateJsonを実行してみます。

対象のファイルが無いのでcreateJsonが実行されていますね。

続いて2回目の実行

今度はファイルが存在しているので、上手くupdateJsonが実行されています。

slackユーザーデータをJSONファイルとしてGoogleドライブに保存するコード(全体)

前回作ったコードと組み合わせて、

  1. slackユーザーデータの取得
  2. JSONファイルの作成or保存

を一括で行う形にします。

createOrUpdateJsonにusers_dataを引数で渡す形にして使いまわします。

function Main() {
  let cursor = "";
  let users_data = [];

  // slackAPIは1度で1000件までしか取得できないので、1000件を超える場合(next_cursorに値がある場合)繰り返す
  do {
    let response = callUsersList(cursor);
    cursor = response["response_metadata"]["next_cursor"]; // こちらの説明は後述
    users_data.push(...response["members"]); // こちらの説明は後述
    test(response);
  } while (cursor != ""); // next_cursorに値が無い場合=全て取得し終わった場合、ループを終了する

  createOrUpdateJson(users_data);
}


function callUsersList(cursor) {
  // スクリプトプロパティに設定したbot_tokenを呼び出す
  const token = PropertiesService.getScriptProperties().getProperty("bot_token")

  const url = "https://slack.com/api/users.list" //APIのURL

  // payloadを設定
  const payload = {
    "token": token,
    "cursor": cursor
  }

  // optionsを設定
  const options = {
    "method": "GET",
    "payload": payload,
    "headers": {
      "contentType": "x-www-form-urlencoded",
    }
  }

  // APIリクエストの結果をresponseに格納
  const response = UrlFetchApp.fetch(url, options);

  // responseの文字列をJSON解析しオブジェクト化して返す
  return JSON.parse(response);
}

function createOrUpdateJson(users_data) {
  // スクリプトプロパティに設定したfolder_idを呼び出す
  const folder_id = PropertiesService.getScriptProperties().getProperty("folder_id");
  // 検索や作成に使うファイル名
  const file_name = "users_list_response.json";

  // フォルダ内にある対象のJSONファイルのIDを取得する
  const file_id = searchFileId(folder_id, file_name);

  if (file_id == null) {
    createJson(folder_id, file_name, users_data);
  } else {
    updateJson(file_id, users_data);
  }
}

function createJson(folder_id, file_name, users_data) {
  // 保存先のフォルダー情報を取得します
  const folder = DriveApp.getFolderById(folder_id);

  // Blobオブジェクトを作成
  const content_type = "application/json";
  const blob = Utilities.newBlob("", content_type, file_name);
  blob.setDataFromString(JSON.stringify(users_data), "UTF-8"); // Blobオブジェクトにデータをセット

  // Blobオブジェクトをファイル化し、保存先フォルダーへ保存
  folder.createFile(blob);
}

function updateJson(file_id,users_data) {
  // 更新対象ファイルを取得
  const file = DriveApp.getFileById(file_id);

  // 更新対象ファイルへデータを上書きする
  file.setContent(JSON.stringify(users_data));
}

function searchFileId(folder_id, file_name) {
  // 保存先のフォルダー情報を取得
  const folder = DriveApp.getFolderById(folder_id);
  // フォルダー内のファイル一覧を取得
  const files = folder.getFiles();
  // ファイルIDを格納する変数
  let file_id;

  // ファイル一覧から1件ずつ探す
  while (files.hasNext()) {
    const file = files.next();
    // ファイルが見つかった時の処理
    if (file.getName() == file_name) {
      file_id = file.getId();
      break; // ファイルが見つかった時点でループ終了
    }
  }

  return file_id;
}

Mainを実行すると、作成・更新されたJSONの中身が、前回の実行結果と同じになりました!

このMainを時間トリガーで実行することで、最新のユーザーデータ(JSON)を更新し続けることができます。

おまけ(トリガー設定)

時間実行トリガーの設定方法です。

GASの左メニューの時計アイコンをクリック

トリガーを追加

設定項目 内容
実行する関数を選択 Main
イベントのソースを選択 時間主導型
時間ベースのトリガーのタイプを選択 お好み
時間の間隔を選択(時間) お好み

上記の設定をして保存します。

このスクショの設定の場合は6時間ごとに実行されます。

※トリガー設定時の注意
トリガーは、トリガーを作成したアカウントに依存します。
作成したアカウントが退職などで削除された場合、実行されなくなるため、削除の可能性が無いアカウントでトリガーを作成することを推奨します。

まとめ

今回のコードでユーザーデータをJSONファイルで保存することができました。
次回は保存したJSONファイルを読み込み、活用するやり方をご紹介します。

前回:
GASを使ってslackユーザーデータを取得する(1000件以上対応) - シンプルに暮らしたい情シスのブログ
次回: