🐙

【GAS】Notionのユーザーとbotのリストを作成する

2023/11/07に公開

背景

情シスでは定期的にアカウントの棚卸しを実施しています。
中には管理画面からアカウント情報をダウンロードできないサービスもあり、管理画面のテキストをスプレッドシートにコピペして体裁を整えるか、それもできない場合は従業員リストを横目に目視でチェックすることになります。
目視チェックはできるだけ減らしたい!と思って調べてみたらいい方法を見つけたので、試してみることにしました。

参考にしたサイト

こちらを参考にさせていただきました。とてもわかりやすかったです。
(ほぼこちらのスクリプトを使ったと言っても過言ではないです・・)
https://blog.cloudnative.co.jp/19031/

抽出する内容

  • アカウントの氏名
  • アカウントのタイプ
  • アカウントのメールアドレス

公式リファレンスを見たらそもそも抽出できる項目が限られていたので、シンプルな構成にしました。
https://developers.notion.com/reference/user

リストの抽出先

Googleスプレッドシートへ抽出します。

リストの対象

  • ユーザー、bot
    ゲストは含まれません。ゲストのデータが必要な場合は、設定画面のサイドバーにある
    [メンバー]>[ゲスト]タブ>[ゲストをCSVにエクスポート]
    からダウンロード可能なので、ゲストの棚卸しではこちらを使っています。
    ちなみにグループですが、少なくともプラスプランでは抽出できないようです。

要件

  • GASの実行は手動で行う
    任意のタイミングで実行したいのでスプレッドシートにカスタムメニューを追加し、そこから処理を開始するようにします。
  • 抽出先のスプレッドシートは抽出専用とする
    抽出したデータは別のファイルで保存するため、抽出先のスプレッドシートは抽出作業専用ファイルという位置付けにしました。
    なので処理の都度、同じシート上でデータを更新するようにします。

処理の流れ

  • Notionのトークンを呼び出し、処理で使うスプレッドシートを指定
  • スプレッドシートを開いた時、メニューと処理を開始するためのサブメニューを追加
  • 確認メッセージの表示
  • シートの既存データ消去
  • 項目をシートへ書き出し、ユーザーデータを書き始める最初の行位置を指定
  • ユーザーデータの書き出し

手順

0. 権限の確認

Notionのワークスペースの管理者権限を付与されたアカウントが必要です。
作業前にログインしておきます。

1. Notionのインテグレーション作成

下記サイトでインテグレーションを作成します。
https://www.notion.so/my-integrations

<設定項目>

  • 基本情報
    以下の情報を選択または入力し、(*ロゴも設定可能ですが任意です)
設定項目 設定内容
関連ワークスペース ユーザーデータを取得したいワークスペースを選択
名前 インテグレーションの名前

設定したら[送信→]をクリックします。


  • 機能
    (シークレットが表示されますがいったんスルー)サイドバーで[機能]をクリックして、

以下の項目で対象にチェックを入れ、[変更を保存]をクリックします。

設定項目 チェックを入れる対象
コンテンツ機能 コンテンツを読み取る
ユーザー機能 メールアドレスを含むユーザー情報を読み取る

2. シークレットをコピー

私のインテグレーションページに戻り、作成したインテグレーションをクリックするとシークレットが表示されるので(表示されない場合はインテグレーションをクリック後、サイドメニューで「シークレット」をクリック)、シークレットの右横にある[表示]をクリックすると

シークレットが表示されるので、右横に表示された[コピー]をクリックしてコピーします。
(この後、手順6で使います)

3. スプレッドシートの準備

抽出用のスプレッドシートを準備します。ファイル名とシート名は適当な名前をつけます。
シート名は後でスクリプトを書くときに使います。
(今回はシート名を「ユーザーリスト」としてみました)

4. GASエディタを開く

スプレッドシートの[拡張機能] - [Apps Script]をクリックしGASエディタを開きます。

5. GASのプロジェクト名を変更する

デフォルトが「無題のプロジェクト」となっているので適当な名前に変更します。
プロジェクト名をクリックすると、

変更できます。

6. Notionのトークンを登録する

サイドバーで[プロジェクトの設定](歯車マーク)をクリックし、

設定画面が表示されたらスクロールして「スクリプト プロパティ」のところにある[スクリプト プロパティを追加]をクリックします。

設定項目が表示されるので、

設定項目 設定内容
プロパティ NOTION_TOKEN
Notionのトークン(手順2でコピーしたトークン)

を入力し、[スクリプト プロパティを保存]をクリックします。
*プロパティは任意の文字列でOKです。後でスクリプトを書くときに使います。
 ここでは「NOTION_TOKEN」となっています。

7. ファイル名を変更する

サイドバーで[エディタ](< >マーク)をクリックして

エディタに戻ったら、ファイル名の右横をポイントすると表示される三点リーダー→[名前の変更]をクリックして

適当な名前に変更します。(今回は「ユーザーリスト」にしてみました)

8. スクリプトを書く

全体像はこんな感じになります。

const notionToken = PropertiesService.getScriptProperties().getProperty('NOTION_TOKEN');
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName("ユーザーリスト");


function onOpen() {
  SpreadsheetApp.getUi()
  .createMenu("リスト抽出")
  .addItem("ユーザー", "getNotionUsers")
  .addToUi();
}


function getNotionUsers() {

  const ui = SpreadsheetApp.getUi();
  let confirm = ui.alert(
    "処理確認",
    "ユーザーリストを更新します。",
    ui.ButtonSet.OK_CANCEL);

  if (confirm == "OK") { 

    sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).clearContent();
    
    const headers = ["Name", "Type", "Email"];
    const headersColumns = headers.length;
    sheet.getRange(1, 1, 1, headers.length).setValues([headers]);

    let currentRow = 2;
    
    
    const baseUrl = 'https://api.notion.com/v1/users';
    const pageSize = 100;
    let hasNextPage = true;
    let startCursor = null;

    while (hasNextPage) {
      let url = `${baseUrl}?page_size=${pageSize}`;
      if (startCursor) {
        url += `&start_cursor=${startCursor}`;
      }

      const options = {
        headers: {
          'Authorization': `Bearer ${notionToken}`,
          'Notion-Version': '2022-06-28'
        },
        method: 'GET'
      };

      const response = UrlFetchApp.fetch(url, options);
      const data = JSON.parse(response.getContentText());
      const users = data.results;
      hasNextPage = data.has_more;
      startCursor = data.next_cursor;

      users.forEach((user, i) => {
        const row = [user.name, user.type];

        if (user.type === "person" && user.person) {
          row.push(user.person.email);
        } else {
          row.push("");
        }

        sheet.getRange(currentRow + i, 1, 1, row.length).setValues([row]);
      });

      currentRow += users.length;
    }

    sheet.autoResizeColumns(1, headersColumns);
  }
}

大まかにはグローバルスコープとfunction2つの構成です。

  • グローバルスコープ
    前述の「処理の流れ」の1行目
    Notionのトークンを呼び出し、処理で使うスプレッドシートを指定
    がこれになります。
    1行目の 'NOTION_TOKEN'手順6で設定したプロパティ、3行目の 'ユーザーリスト'手順3のスプレッドシートのシート名を入力します。
global scope
const notionToken = PropertiesService.getScriptProperties().getProperty('NOTION_TOKEN');
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName("ユーザーリスト");

1行目でNotionのトークンを呼び出し、2・3行目でスプレッドシートを指定しています。
トークンは手順6でスクリプトプロパティとして設定したので、スクリプトに直書きしなくても呼び出せるようになっています。
PropertiesService についての詳細は公式ガイドをご覧ください。
https://developers.google.com/apps-script/guides/properties?hl=ja


  • onOpen
    前述の「処理の流れ」の2行目
    スプレッドシートを開いた時、メニューと処理を開始するためのサブメニューを追加
    の処理になります。
    シンプルトリガーの関数 onOpen を使い、スプレッドシートを開いたときに[リスト抽出]というメニューと[ユーザー]というサブメニューを作成します。
    (ちょっとしたことですが、実際に作業してみると意外と便利です)
onOpen
function onOpen() {
  SpreadsheetApp.getUi()
  .createMenu("リスト抽出")
  .addItem("ユーザー", "getNotionUsers")
  .addToUi();
}


<参考:スプレッドシートの状況>

  • メニュー作成前

  • 作成後


  • getNotionUsers
    前述の「処理の流れ」の3行目以降をここで処理します。
    まず確認メッセージを表示して、
function getNotionUsers() {

  const ui = SpreadsheetApp.getUi();
  let confirm = ui.alert(
    "処理確認",
    "ユーザーリストを更新します。",
    ui.ButtonSet.OK_CANCEL);

OKがクリックされたら

  if (confirm == "OK") { 

処理を開始します。
まずシートにある既存データを消去し(既存データが無い状態でもOK)、1行目に項目を入力したら
ユーザーデータを書き出す行位置を指定
しておきます。

    sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).clearContent();
    
    const headers = ["Name", "Type", "Email"];
    const headersColumns = headers.length;
    sheet.getRange(1, 1, 1, headers.length).setValues([headers]);

    let currentRow = 2;

次に、Notionのユーザーデータを抽出するために必要なスクリプトを書きます。
NotionAPIを使ってスプレッドシートへデータを抽出する場合は定型的に書く部分かと思います。

    const baseUrl = 'https://api.notion.com/v1/users';
    const pageSize = 100;
    let hasNextPage = true;
    let startCursor = null;

    while (hasNextPage) {
      let url = `${baseUrl}?page_size=${pageSize}`;
      if (startCursor) {
        url += `&start_cursor=${startCursor}`;
      }

詳細については公式リファレンスをご確認ください。
https://developers.notion.com/reference/intro

'Notion-Version' はNotionAPIのバージョンです。(執筆時点では最新バージョン)
NotionAPIを使うときは必ず記載します。

      const options = {
        headers: {
          'Authorization': `Bearer ${notionToken}`,
          'Notion-Version': '2022-06-28'
        },
        method: 'GET'
      };

公式リファレンスはこちらです。
https://developers.notion.com/reference/versioning
こちらではNotionAPIの最新バージョンや各バージョンの変更部分を確認できます。
(旧バージョン対応のスクリプトを最新バージョンに対応させる時、ここで確認したりします)
https://developers.notion.com/reference/changes-by-version

次はユーザーデータ抽出のための前準備のような部分です。
かなりざっくりとしたイメージですが書いてみました。
 1行目:APIを使って抽出してきたデータをいったんそのまま受け止めて変数に格納
 2行目:そのデータをGASで処理できる形に変換して変数に格納
 3行目以降:この後の処理で必要となる変数のうちループ処理で変化する変数の定義または再定義
(少しでも伝わっていたら嬉しいです。。)

      const response = UrlFetchApp.fetch(url, options);
      const data = JSON.parse(response.getContentText());
      const users = data.results;
      hasNextPage = data.has_more;
      startCursor = data.next_cursor;

UrlFetchApp の公式リファレンスです。
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app?hl=ja

ここからはユーザーデータを書き出すループ処理です。
ユーザーの type は、Notionでは人の場合だと person 、ボットの場合だと bot となっているので、ユーザーの typeperson の時のみメールアドレスを追加で抽出してスプレッドシートへ書き出します。
このブロックの最後の行では次のユーザーデータをスプレッドシートへ書き出す時の行位置を指定し直しています。

      users.forEach((user, i) => {
        const row = [user.name, user.type];

        if (user.type === "person" && user.person) {
          row.push(user.person.email);
        } else {
          row.push("");
        }

        sheet.getRange(currentRow + i, 1, 1, row.length).setValues([row]);
      });

      currentRow += users.length;
    }

最後に、ユーザーを抽出し終わったらシートの列幅をデータの幅に合わせて調整します。

    sheet.autoResizeColumns(1, headersColumns);
  }
}

使い方

初回承認

GASを最初に実行するときは承認が必要です。
エディタで onOpen が選択されていることを確認して[実行]をクリックします。

[権限を確認]をクリックします。

使用するGoogleアカウントを選択します。

プロジェクトに付与される権限を確認して[許可]をクリックします。

GASエディタ下部に表示される実行ログで「実行完了」と表示されていれば初回承認完了です。

スプレッドシートの操作方法

メニューバーで[リスト抽出]-[ユーザー]をクリックします。
(メニューバーに「リスト抽出」が表示されない場合は、一度スプレッドシートを閉じて再度開くと表示されます)

確認メッセージが表示されたら[OK]をクリックし、処理が終わったら終了です。

感想

これまでNotionのアカウントの棚卸しは目視でチェックしていたため時間はかかるし目は疲れるしで正直かなり面倒でしたが、この方法を採用してからは作業負荷が大幅に軽減されました。
少し前まで私もそうでしたが、「API」と聞いただけで「私は無理・・」と思われる方の中にもアカウント管理されている方がいらっしゃるかと思います。
一度作ってしまえばその後の手間は雲泥の差なので、この機会にお試しいただけたら嬉しいです。

最後に

参考にさせていただいた記事や公式リファレンス等をまとめました。

レスキューナウテックブログ

Discussion