見出し画像

[GAS] NotionAPIを使って、作成した日報ページの内容を取得しChatworkでメッセージを送信する2

はじめに

前回、GASで、NotionAPIを使用し、前回作成した日報ページから作業内容を取得し、日報を報告するメッセージをChatworkで送信する、という処理を実装しますの1回目として、処理概要とNotionのページ構成の基礎知識についてご紹介しました。

今回は、GASで、Notionの日報ページから箇条書きリストの内容を取得し、Googleスプレッドシートの特定のセルに出力する処理を実装していきます。

実装

実装時に必要になるのは、下記2つになります。

  • Internal Integration Token

  • 取得するページID

全体の処理は、下記です。

/**
 * Notion 指定ページの箇条書きリストの内容を取得する
 */
function fetchDiaryNotion() {
	const props = PropertiesService.getScriptProperties();
 const token = props.getProperty('NOTION_TOKEN');
  const pageId = props.getProperty('NOTION_PAGE_ID');

  const textLines = getTextBlockChildren_(token, pageId);
  const line = textLines.join('\n');
  
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(DIARY_TEMPLATE_SHEET_NAME);
}

/**
 * 箇条書きリストを取得し、文字列として取得する
 * @param {string} token - トークン
 * @param {string} blockId - ページID or ブロックID
 * @param {string} prefix - 取得した文字列の先頭に設定する文字列
 * @return {Array.<string>} 取得した文字列の配列
 */
function getTextBlockChildren_(token, blockId, prefix=' ・'){
	const notionApiManager = new NotionApiManager(token);
  const response = notionApiManager.getBlockChildren(blockId);
  const json = response.getContentText();
  const data = JSON.parse(json);

  let textLines = [];
  for ( const result of data.results ){
    if ( result.type === 'bulleted_list_item'){
      textLines.push(prefix + result.bulleted_list_item.rich_text[0].text.content);

      if (result.has_children === true){
        const getBlockId = result.id;
        const blockId = getBlockId.replace(/-/g,'');
        const nextPrefix = '\t' + prefix;

        const childTextLines = getTextBlockChildren_(notionManager, blockId, nextPrefix);
        textLines = textLines.concat(childTextLines);
      }
    }
  }
  return textLines;
}

順に処理を説明します。

①ページID(ブロックID)を指定して、箇条書きリストを取得

getTextBlockChildren_関数で、指定されたページID、もしくはブロックIDの作成するページオブジェクトを作成しています。

APIの詳細は、下記をご参照ください。

指定したIDの子要素を取得するHTTPリクエストを実行し、JSON形式をオブジェクトに変換します。

  const notionApiManager = new NotionApiManager(token);
  const response = notionApiManager.getBlockChildren(blockId);
  const json = response.getContentText();
  const data = JSON.parse(json);

指定したIDの子要素が、resultsプロパティに配列として格納されているため、順番に確認、取得していきます。

今回は、箇条書きリストのみを対象としているため、typeプロパティを参照し、箇条書きリストのみを処理するようにします。

  for ( const result of data.results ){
    if ( result.type === 'bulleted_list_item'){

リストに設定されている文字列は、rich_textプロパティの中のtextプロパティから取得します。

      textLines.push(prefix + result.bulleted_list_item.rich_text[0].text.content);

参照中の要素が、子要素を持つかどうかは、has_childrenプロパティで確認することができです。
has_childrenプロパティがtrueの場合、現在参照中のブロックIDを指定して、現在実行しているgetTextBlockChildren_関数を再帰関数として呼び出します。

      if (result.has_children === true){
        const getBlockId = result.id;
        const blockId = getBlockId.replace(/-/g,'');
        const nextPrefix = '\t' + prefix;

        const childTextLines = getTextBlockChildren_(notionManager, blockId, nextPrefix);
        textLines = textLines.concat(childTextLines);
      }

getTextBlockChildren_関数の第3引数は、取得した文字列の先頭に設定する文字を指定しています。
Notionの日報ページに記載されている箇条書きリストと合わせるよう、\t(タブ)と「・」を指定しています。

      if (result.has_children === true){
        const getBlockId = result.id;
        const blockId = getBlockId.replace(/-/g,'');
        const nextPrefix = '\t' + prefix;

        const childTextLines = getTextBlockChildren_(notionManager, blockId, nextPrefix);
        textLines = textLines.concat(childTextLines);
      }

②NotionAPI用のクラスの実装

NotionAPIを呼び出す処理は、下記のように実装しています。

 const notionApiManager = new NotionManager(token);
 const response = notionApiManager.getBlockChildren(blockId);

NotionAPI用のクラスを作成しています。前回ご紹介した実装から、処理を追加しています。

class NotionApiManager{
  /**
   * Notion APIに関するコンストラクタ
   * @constructor
   */
  constructor(token){
    this._token = token;
    this._notionVersion = this._getNotionAPIVersion();
  }

  /**
   * ページを作成するメソッド
   * @param {object} payload - ページオブジェクト
   * @return {HTTPResponse} fetchのresponse
   */
  createPage(payload){
    const endpoint = this._getEndpointPage();
    const options = this._postOption(payload);

    const response = UrlFetchApp.fetch(endpoint, options);
    return response;
  }

  /**
   * 指定した要素の子要素を取得するメソッド
   * @param {string} blockId- ページ、オブジェクトID
   * @return {HTTPResponse} fetchのresponse
   */
	 getBlockChildren(blockId){
    const endpoint = this._getEndpointBlockChildren(blockId);
    const options = this._getOption();

    const response = UrlFetchApp.fetch(endpoint, options);

    if ( response.getResponseCode() === 200 ){
      // 応答を返すまで、待つ
      Utilities.sleep(1000);
    }

    return response;
  }
  /**
   * NotionAPI のVersionを取得するサブメソッド
   * @param {string} guestId - アプリの名前
   * @return {string} version - NotionAPIのバージョン
   */
  _getNotionAPIVersion(){
    const url = "https://developers.notion.com/reference/versioning"
    const reg = /&quot;default&quot;.*?<\/script>/g;
    const htmldata = UrlFetchApp.fetch(url).getContentText("utf-8");
    const version = htmldata.match(reg)[0].match(/([0-9]{4}-[0-9]{2}-[0-9]{2})/g)[0];
    return version;
  }

  /**
   * ページ用エンドポイントを取得するサブメソッド
   * @return {string} endpoint - エンドポイント
   */
  _getEndpointPage() {
    return 'https://api.notion.com/v1/pages/'
  }
  /**
   * ブロックの子要素用エンドポイントを取得する
   * @return {string} endpoint - エンドポイント
   */
  _getEndpointBlockChildren(blockId) {
    return `${this._getEndpointBlock(blockId)}children/`
  }
  /**
   * ブロック用エンドポイントを取得する
   * @return {string} endpoint - エンドポイント
   */
  _getEndpointBlock(blockId) {
    return `https://api.notion.com/v1/blocks/${blockId}/`
  }
  /**
   * GETする時のオプションを作成するサブメソッド
   * @return {Object} option - option
   */
  _getOption() {
    const option = {
      method: "get",
      headers: {
        'Content-Type': 'application/json',
        'Notion-Version': this._notionVersion,
        'Authorization': "Bearer " + this._token
      },
      muteHttpExceptions: true
    };
    return option;
  }
  /**
   * POSTする時のオプションを作成するサブメソッド
   * @param {Object} payload - payload
   * @return {Object} option - option
   */
  _postOption(payload) {
    const options = {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'Notion-Version': this._notionVersion,
        'Authorization': "Bearer " + this._token
      },
      payload: JSON.stringify(payload)
    };
    return options;
  }
}

クラスに追加したメソッドは、getBlockChildrenメソッドになります。

getBlockChildrenメソッドは、ページID、もしくはオブジェクトIDの子要素を取得するのエンドポイント、オプションを指定してHTTPリクエストを実行します。

このgetBlockChildrenメソッドでは、HTTPリクエスト実行後に1000ミリ秒(1秒)間スリープの処理を入れています。

    if ( response.getResponseCode() === 200 ){
      // 応答を返すまで、待つ
      Utilities.sleep(1000);
    }

NotionAPIでは、一定時間あたりに使用できる回数に制限があるため、連続してHTTPリクエストを送信しないため、スリープ処理を行っています。

処理は以上です!

実行すると、Notionの日報ページの箇条書きリストの内容が、Googleスプレッドシートの特定のセルに書き込まれているのが確認できました。


まとめ

今回は、GASで、Notionの日報ページから箇条書きリストの内容を取得し、Googleスプレッドシートの特定のセルに出力する処理を実装について、まとめました。

次回は、今回取得した日報の内容と出社、退社の時間を合わせて、日報報告としてChatworkに送信する処理の実装について、ご紹介していきます。

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