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の実行制限を超えるようなケースは避けられます。
事前準備
コード
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ファイルを更新するコード
先ほど作成したファイルの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つのコードで運用する場合、
- 初回はcreateJsonを実行
- 2回目以降はfile_idを設定し、updateJsonを実行
というちょっとイケてない運用になります。
1つのコードで作成・更新をしたいので、
- フォルダ内から指定の特定のファイル名のファイルを探す
- 無ければ作成
- あれば更新
という処理にしたいです。
保存先フォルダ内からファイルを探して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ドライブに保存するコード(全体)
前回作ったコードと組み合わせて、
- slackユーザーデータの取得
- 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件以上対応) - シンプルに暮らしたい情シスのブログ
次回:
GASを使ってslackユーザーデータを取得する(1000件以上対応)
お久しぶりです。カインです。
転職して1年が経過し、色々なGASやSlackアプリを作ったのでぼちぼち記事にしてきたいと思います。
Slackのユーザー情報の呼び出しAPI(users.list)、何かと便利ですよね。
最初はユーザー情報が欲しい時はusers.list APIで毎回呼び出して使用していましたが、実行時間を削減したりスプレッドシートで活用したく、取って来たデータをJSONやスプレッドシートに保存して使っています。
今回は、users.listの呼び出し、JSONファイルの保存、スプレッドシートへの書き込みの3つ記事に分けて紹介していきます。
この記事で紹介すること
- slack API users.list の使い方
続きの記事
- slackユーザーデータをJSONファイルとしてGoogleドライブに保存する - シンプルに暮らしたい情シスのブログ
- 保存したJSONを読み込み、スプレッドシートへ書き込む(執筆予定)
slack API users.listの使い方
slackアプリの作成・権限設定
まずはslackアプリを作成し、users.listに必要な権限設定を行います。
アプリの作成
slack左上のメニュー>設定と管理>アプリを管理する
ビルドをクリック
Create New Appをクリック
From scratchをクリック
App Nameに任意の名前を入れて、Pick a workspace to develop your app inにインストール先のワークスペースを選択します。
ここでusers.listに必要な権限を確認します。
https://api.slack.com/methods/users.listを開き、
ここを確認すると、users:read が必要なようです。
が…!これには罠があって、読み進めていくと、
メールアドレスを取得するにはusers:read.emailも必要とのこと!
Required scopesのところに書いておいて欲しいですねぇ…。
権限の設定
OAuth & Permissionsをクリック
少し下がったところにあるScopes>Bot Token Scopesの下にあるAdd an OAuth Scopeをクリック
users:readを探してクリック
users:read.emailを探してクリック
OAuth & Permissionsのページ上部にあるInstall to Workspaceをクリック
内容を確認して、許可するをクリック
これでslackアプリの準備は完了です。
後ほどGASで使用する情報を確認しておきます。
OAuth & Permissionsのページ上部にBot User OAuth Token(xoxb-から始まるもの)があるので控えておきます。
GASの作成・実行
コード作成の前に
ここでusers.listをもう一度確認します。
Argumentsのところで、tokenのみRequiredが付いているので、users.listはtokenだけあれば実行は可能です。
ただし、読み進めていると、
一度に1000件までしか取得できないようで、1000件を超える場合はcursorが必要になるようです。
弊社は1000ユーザーを超えているので、cursorも含めたコードを作成します。
処理の流れ
- 空のcursorと配列を定義
- users.listを呼び出し、配列にuser情報を格納
- responseのnext_cursorに値があれば、再度users.listを実行(1000件を超えている場合next_cursorに値が入る)
- next_cursorが空であれば繰り返しを終了
トークンの設定
コード内に直接トークンを記載するのはリスクがあるため、スクリプトプロパティ内に設定します。
歯車アイコンをクリック
ページ下部のスクリプト プロパティを追加をクリック
プロパティは任意ですが、bot_tokenとしておきます。
値には、slackアプリの設定で控えた、Bot User OAuth Tokenを貼り付けて保存します。
コードの構成
コード
function Main() { let cursor = ""; let users_data = []; // users.listは1度で1000件までしか取得できないので、1000件を超える場合(next_cursorに値がある場合)繰り返す do { let response = callUsersList(cursor); cursor = response["response_metadata"]["next_cursor"]; // こちらの説明は後述 users_data.push(...response["members"]); // こちらの説明は後述 } while (cursor != ""); // next_cursorに値が無い場合=全て取得し終わった場合、ループを終了する // ユーザーデータをJSON文字列に変換 const users_data_str = JSON.stringify(users_data); Logger.log(users_data_str); } 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); }
一部解説
next_cursorの取り出し
users.listのresponseデータ(以下参照)の中に以下の部分があります。
"response_metadata": { "next_cursor": "dXNlcjpVMEc5V0ZYTlo=" }
この場合は、response_metadata.next_cursorに値があるので、
do~while内の
cursor = response["response_metadata"]["next_cursor"];
この部分でcursorにdXNlcjpVMEc5V0ZYTlo=が入ります。
cursor=""ではないため、cursorを引数としてcallUsersListに渡して再度APIを呼び出します。
続きが無い場合、response_metadata.next_cursorは
"response_metadata": { "next_cursor": "" }
と返ってくるため、cursorには""が入り、do~whileループから抜けます。
メンバー情報の取り出し・格納
do~while内に以下の部分があります。
users_data.push(...response["members"]);
user_dataは最初は空の配列で、1回APIを呼び出す毎に取得したユーザーデータ(members)を格納しています。
response["members"]の前にある...は、簡単に言うと配列の[]を外すような処理をします。
サンプル
test_dataがresponse["members"]と思ってください。
function sample() { let arr1 = []; let arr2 = []; const test_data = [ { "name": "a" }, { "name": "b" }, { "name": "c" } ]; arr1.push(test_data); arr2.push(...test_data); Logger.log(arr1); Logger.log(arr2); }
実行結果
(参考)responseデータ(JSON)
※users.listから引用
{ "ok": true, "members": [ { "id": "W012A3CDE", "team_id": "T012AB3C4", "name": "spengler", "deleted": false, "color": "9f69e7", "real_name": "spengler", "tz": "America/Los_Angeles", "tz_label": "Pacific Daylight Time", "tz_offset": -25200, "profile": { "avatar_hash": "ge3b51ca72de", "status_text": "Print is dead", "status_emoji": ":books:", "real_name": "Egon Spengler", "display_name": "spengler", "real_name_normalized": "Egon Spengler", "display_name_normalized": "spengler", "email": "spengler@ghostbusters.example.com", "image_24": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "image_32": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "image_48": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "image_72": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "image_192": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "image_512": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg", "team": "T012AB3C4" }, "is_admin": true, "is_owner": false, "is_primary_owner": false, "is_restricted": false, "is_ultra_restricted": false, "is_bot": false, "updated": 1502138686, "is_app_user": false, "has_2fa": false }, { "id": "W07QCRPA4", "team_id": "T0G9PQBBK", "name": "glinda", "deleted": false, "color": "9f69e7", "real_name": "Glinda Southgood", "tz": "America/Los_Angeles", "tz_label": "Pacific Daylight Time", "tz_offset": -25200, "profile": { "avatar_hash": "8fbdd10b41c6", "image_24": "https://a.slack-edge.com...png", "image_32": "https://a.slack-edge.com...png", "image_48": "https://a.slack-edge.com...png", "image_72": "https://a.slack-edge.com...png", "image_192": "https://a.slack-edge.com...png", "image_512": "https://a.slack-edge.com...png", "image_1024": "https://a.slack-edge.com...png", "image_original": "https://a.slack-edge.com...png", "first_name": "Glinda", "last_name": "Southgood", "title": "Glinda the Good", "phone": "", "skype": "", "real_name": "Glinda Southgood", "real_name_normalized": "Glinda Southgood", "display_name": "Glinda the Fairly Good", "display_name_normalized": "Glinda the Fairly Good", "email": "glenda@south.oz.coven" }, "is_admin": true, "is_owner": false, "is_primary_owner": false, "is_restricted": false, "is_ultra_restricted": false, "is_bot": false, "updated": 1480527098, "has_2fa": false } ], "cache_ts": 1498777272, "response_metadata": { "next_cursor": "dXNlcjpVMEc5V0ZYTlo=" } }
スプレッドシートのデータを連想配列化する話
ご無沙汰しております。
相変わらず定期的な更新が続かないカインです。
実は4月に転職しまして、新しいことに挑戦している毎日です。
ブログネタも沢山出来たので少しずつアウトプットしていきたいと思います!
今回はスプレッドシートのデータをGASで取得し、連想配列化する話です。
以前は連想配列とはなんぞやといった感じでしたが、理解が進むにつれてその便利さに魅了されつつあります。
データを弄るのにとても便利なのでかなり汎用的に使えて重宝しています。
機能
↑このようなデータを↓のような連想配列にします。
※JSON文字列化しています。
[ { "entrydate": "2020-04-27T15:00:00.000Z", "userid": "OD77412", "name": "オソミハ", "seibetsu": "men", "age": 32, "totalmoney": 52000, "birthday": "1988-03-04T15:00:00.000Z" }, { "entrydate": "2019-10-26T15:00:00.000Z", "userid": "QS19455", "name": "トムアシテキ", "seibetsu": "men", "age": 70, "totalmoney": 59000, "birthday": "1949-10-12T15:00:00.000Z" }, { "entrydate": "2019-11-30T15:00:00.000Z", "userid": "HY60274", "name": "オンウ", "seibetsu": "men", "age": 54, "totalmoney": 71000, "birthday": "1966-02-02T15:00:00.000Z" }, { "entrydate": "2020-07-17T15:00:00.000Z", "userid": "TA49387", "name": "レコスソニ", "seibetsu": "men", "age": 80, "totalmoney": 63000, "birthday": "1940-05-28T15:00:00.000Z" }, { "entrydate": "2019-09-01T15:00:00.000Z", "userid": "PK56576", "name": "コミトハム", "seibetsu": "men", "age": 81, "totalmoney": 87000, "birthday": "1939-07-23T15:00:00.000Z" } ]
※注
日付はJSON.stringifyを使用した際にタイムゾーン情報が失われてUTCに置き換わっています。
以下のコードをJSON変換前に記述しておくことで無理やり回避できます。
Date.prototype.toJSON = function () { return this.getFullYear() + '/' + ('0' + (this.getMonth() + 1)).slice(-2) + '/' + ('0' + this.getDate()).slice(-2) + ' ' + ('0' + this.getHours()).slice(-2) + ':' + ('0' + this.getMinutes()).slice(-2) + ':' + ('0' + this.getSeconds()).slice(-2); }
コード全体
// こちらを実行 function sample() { const spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // const spreadsheet = SpreadsheetApp.openById("スプレッドシートのID") //スタンドアロン型の場合はこちらを使用 const sheet = spreadsheet.getSheetByName("抽出したいシート名"); const sheet_data = changeAssociativeArray(sheet); Logger.log(JSON.stringify(sheet_data)); // 生成した連想配列をログに表示 } // sheet情報を引数で渡すと連想配列が返される関数 function changeAssociativeArray(sheet){ // スプレッドシートから値を取得し、二次元配列へ格納 let data_array = sheet.getDataRange().getValues(); // 項目名と値を分離 let keys = data_array[0]; //項目名を取得しkeysに格納 data_array.shift(); // 項目名をdata_arrayから削除 // 二次元配列を連想配列に変換 let new_data_array = data_array.map(function(values){ let hash = {}; values.map((value, index)=>hash[keys[index]] = value); return hash; }); return new_data_array; }
連想配列化して何が嬉しいか
一番のメリットはデータの扱いやすさです。
mapやfilter、forEach等の各種配列用の関数も使用できますし、データを項目名で取得できるので可読性が段違いです。
生成した連想配列(sheet_data)から名前(name)の一覧を取り出す例
for (let i = 0; i < sheet_data.length; i++) { Logger.log(sheet_data[i]["name"]); }
↓
配列用の関数(map、filter、forEach等)も使用でき、項目名の指定もドットで区切るだけでも可能なので、コードをより簡潔に書くこともできます。
ただし、項目名が日本語や一部記号を含む場合は ["項目名"] で指定する必要があります。
※↑のfor文と同じ内容を簡潔に書いた場合
sheet_data.forEach((value)=>Logger.log(value.name));
まとめ
このGASはスプレッドシートのデータを取得して活用するケースが増えたので多用しています。
課題や応用もいくつかあるので後日記事にします!
slackのAPI chat.postMessage で投稿したメッセージのURLを取得するGAS
※記事投稿後にもっと簡単なやり方があるよ!と教えていただき修正しました(*´▽`人)
(team.infoを使用→chat.getPermalinkを使用)
ご無沙汰しております。
なかなか書くネタが見つからないカインです。
slackのAPI chat.postMessage で投稿したメッセージのURLを取得するスクリプトを作ったので備忘録的に書いていきます。
- 処理の流れ
- 使用するAPI
- 準備
- コード全体
- chat.postMessageのresponseの中身
- chat.getPermalinkのresponseの中身
- functionの説明
- 実行テスト
- 終わりに
処理の流れ
使用するAPI
- chat.postMessage
- メッセージ投稿に使用します。
- chat.getPermalink
- チャンネルIDとタイムスタンプからURLを取得するために使用します。
準備
コード全体
GASにコードをコピペします。
botトークンと投稿先のチャンネルIDは書き換えてください。
function getSlackUrl(){ const bot_token = "xoxb-*******************************"; //slackアプリ OAuth & PermissionsのBot User OAuth Access Token //メッセージを投稿 const post_channel = "*********"; //投稿先のチャンネルID var message_data = "投稿するメッセージの内容(適当)"; var created_message = createMessage(message_data,bot_token,post_channel); //createMessage関数を呼び出してAPIに必要な情報を整理 var response = UrlFetchApp.fetch("https://slack.com/api/chat.postMessage", created_message); //メッセージを投稿 //responseをcreateSlackUrl関数へ渡してURLを生成 var slack_url = createSlackUrl(response,bot_token); //確認用に生成したURLを投稿してみる var message_data = slack_url; var created_message = createMessage(message_data,bot_token,post_channel); var response = UrlFetchApp.fetch("https://slack.com/api/chat.postMessage", created_message); } //----------------------------------------postMessage作成 function createMessage(message_data,bot_token,channel_id){ var payload = { "token" : bot_token, "channel" : channel_id, "text" : message_data } var options = { "method" : "POST", "payload" : payload, "headers": { "contentType": "x-www-form-urlencoded", } } return options; } //----------------------------------------createSlackUrl function createSlackUrl(response_pm,bot_token){ var r_pm = JSON.parse(response_pm); //JSONを解析 var channel = r_pm.channel; //チャンネルIDを抽出 var ts = r_pm.ts; //タイムスタンプを抽出 var payload = { "token" : bot_token, "channel" : channel, "message_ts" : ts } var options = { "method" : "GET", "payload" : payload, "headers": { "contentType": "x-www-form-urlencoded", } } //チャンネルIDとタイムスタンプを元に、chat.getPermalink APIでURLを取得する var response_ti = UrlFetchApp.fetch("https://slack.com/api/chat.getPermalink", options); var slack_url = JSON.parse(response_ti).permalink; //URLを抽出 return slack_url; }
chat.postMessageのresponseの中身
※一部 *** でボカしています。
channelとtsを取り出して使用します。
{ "ok": true, "channel": "C0*******A8", "ts": "1642******.****00", "message": { "bot_id": "B02******NL", "type": "message", "text": "投稿するメッセージの内容(適当)", "user": "U02******HY", "ts": "1642******.****00", "team": "TR*****F3", "bot_profile": { "id": "B02******NL", "app_id": "A02******CC", "name": "get_slack_url", "icons": { "image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png", "image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png", "image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png" }, "deleted": false, "updated": 1642509170, "team_id": "TR*****F3" } } }
chat.getPermalinkのresponseの中身
permalinkを取り出して使用します。
{ "ok": true, "permalink": "https://s*****t.slack.com/archives/C0*******A8/p1642******.****00", "channel": "C0*******A8" }
functionの説明
getSlackUrl
実行用
createMessage
chat.postMessageメソッドに必要な中身を作る処理
createSlackUrl
chat.postMessageのsuccess responseの中身からURLを作成する処理
実行テスト
function getSlackUrlを実行
メッセージが投稿された後、生成したURLが投稿されました!
終わりに
難しいことは特にしていませんが、今日までpostMessageで投稿したものをOutgoing Webhookで拾ってURLを生成するというアホなことをやっていました。
Outgoing Webhookはプライベートチャンネルでは使えませんが、今回作ったものはプライベートチャンネルでも使えますし、何より仕組みそのものがスッキリしました!
メールアドレスでslackのユーザーIDを取得するGAS
約1年ぶりの更新です(;^ω^)
滞ってるにもかかわらず、毎日10人前後に見ていただけているようで恐縮です!
ありがとうございます!!
この記事はGASとslackでヘルプデスクフォームを作った話(Googleフォーム版 準備編)に関連した記事です。
メールアドレスを元にユーザー情報を取得する便利なAPIを見つけたのでご紹介です!
コード全体
※bot作成やslackとGASの連携等は準備編やコード編をご参照ください。
//検索したいメールアドレスと、botトークンを渡す function findUserByEmails(email,bot_token){ var url="https://slack.com/api/users.lookupByEmail" //APIのURL var payload = { "token" : bot_token, //botトークン ※記事上部の別記事参照 "email" : email //検索したいメールアドレス } var options = { "method" : "GET", "payload" : payload, "headers": { "contentType": "x-www-form-urlencoded", } } var json_data = UrlFetchApp.fetch(url, options); //APIリクエスト実行と結果の格納 json_data = JSON.parse(json_data) //結果はJSONデータで返されるのでデコード if (json_data["ok"]){ //boolean型でtrue or falseが格納されています var user_id = String(json_data["user"]["id"]) //trueの場合返答されたデータからユーザーIDを抽出 } else { var user_id = "null" //falseの場合null(文字列)を格納 } return user_id //ユーザーID(or null)を返却 }
使用API
users.lookupByEmail
メールアドレスを元にユーザー情報をリクエストするAPIです。
メールアドレスの以外にも
- 表示名
- 氏名
- アイコンのURL
- 役割・担当
等々
プロフィールで閲覧できるものは一通り取得できるようです。
(APIのページの「Typical success response」に詳細が書いてあります)
今まで使っていた手法
- users.listを使って、slack上のユーザーを全て取得
- 1件ずつメールアドレスが一致するかチェック
- 一致する場合ユーザーIDを格納する
- 一致しない場合null(文字列)を返す
いつからかユーザーが存在するにも関わらずユーザーIDが見つけられないというトラブルが起き始めたので対策を調べていたところ、users.lookupByEmailを見つけました。
素人考えですが、users.listで取得した結果を見ると4000文字程度あったのでどこかで文字数上限に引っかかったのかなーと考えてます。
今まで使っていた手法のコード(供養)
function findUserByEmails(email,bot_token){ var url="https://slack.com/api/users.list" var payload = { "token" : bot_token, } var options = { "method" : "GET", "payload" : payload, "headers": { "contentType": "x-www-form-urlencoded", } } var json_data = UrlFetchApp.fetch(url, options); json_data = JSON.parse(json_data) for(i in json_data["members"]){ if(String(json_data["members"][i]["profile"]["email"])==email){ var user_id =String(json_data["members"][i]["id"]) } } if(user_id == null){ user_id = "null" } return user_id }