JavaScript
GoogleAppsScript
gas
spreadsheet
Slack

Google Apps Script ハンズオン資料 #GoogleAppsScript

この資料の説明

第 1 回 Google Apps Script (GAS) ハンズオン @麹町 のハンズオン用資料です。以下の流れに沿ってハンズオンを進めていきます。

ハンズオンをスムーズに行うために

アジェンダ

Google Apps Script とは

以下 Google Apps Script とは | Google Cloud Next 2018 資料 より抜粋

  • Google Apps ( Spreadsheet, Docs, Gmail, Slide など ) の API を簡単にクラウドで実行できる環境
  • G Suite に強い FaaS
  • GAS と略される

Google Apps Script の言語環境

  • プログラミング言語は JavaScript ( Version 1.6 がベース )
  • Web フロントエンド JavaScript と異なる
    • DOM, Window API はない
  • Node.js とも異なる
    • Node.js の Core API が使えない
    • npm モジュールは標準で非対応

公式チュートリアル

Your first script

Gmail to Spreadsheet

  • Gmail からメールを検索して、該当するメッセージ一覧をスプレッドシートに書き込むサンプル
  • 解説ページ: Gmail to SpreadSheet

作業の流れ

  1. スプレッドシートを新規に作成
  2. https://script.google.com にアクセスして 新規スクリプト を選択
  3. プロジェクト名を入力、保存
  4. 以下のコードを GAS エディタにペーストして保存
  5. 作成したスプレッドシートの ID を spreadSheetId に代入( docs.google.com/spreadsheets/d/ここの文字列/edit )
  6. 同じくタブ名を spreadSheetTabName に代入
  7. 検索ワードを searchText に代入(参考: Gmail で使用できる検索演算子
  8. 関数一覧から main 関数を選んで実行
  9. スクリプトに権限を与える操作を行う
  10. スプレッドシートに件名、送信元メアド、送信先メアドが追加されれば OK

余裕がある方向け

  • Class GmailMessage を参考に日付や本文などの他のデータも取れるようにする
  • 501 スレッド以上の取得
  • 2 回目以降の実行で上書きでなく、重複チェックして行追加されるようにする
  • 定期実行してみる( 5 分おきなど )
  • getConfig 関数でなく、 Properties Service を用いて設定値管理する
// 設定情報
function getConfig() {
  return {
    spreadSheetId: '',// スプレッドシートの ID
    spreadSheetTabName: 'シート1',// スプレッドシートのタブ名
    searchText: ''// Gmail の検索ワード
  };
}

// 実行関数
function main() {
  var config   = getConfig();
  // 書き込み先のスプレッドシートを取得
  var sheet    = SpreadsheetApp.openById(config.spreadSheetId)
                               .getSheetByName(config.spreadSheetTabName);
  // メッセージ一覧の配列を初期化
  // 件名, 送信元, 送信先のヘッダーを 1 行目に入れる
  var messages = [['Subject', 'From', 'To']];

  GmailApp
    // スレッド検索
    .search(config.searchText, 1, 500)
    .forEach(function (thread) {
      // スレッド内のメッセージ一覧を取得
      thread.getMessages().forEach(function (message) {
        // 件名
        var subject = message.getSubject();
        // 送信元
        var to      = message.getTo();
        // 送信先
        var from    = message.getFrom();
        // メッセージ一覧に追加
        messages.push([subject, from, to]);
      });
    });
  // 2次元配列のサイズにあった範囲を指定して、シートに書き込む
  sheet.getRange( 'A1:C' + messages.length ).setValues(messages);
}

Form to Slack

  • Google フォームの回答時に、回答内容を Slack に通知するサンプル
  • Form to Slack

作業の流れ

  1. Google フォームを作成
  2. 適当に質問を 2-3 個作る
  3. > スクリプトエディタ を選択
  4. プロジェクト名を入力、保存
  5. 以下のコードを GAS エディタにペーストして保存
  6. Webhook URL を slackWebhookUrl に代入(別途 Slack で共有)
  7. 編集 > 現在のプロジェクトのトリガー からトリガー追加(以下画像参照)
  8. スクリプトに権限を与える操作を行う
  9. 右上メニューのプレビューを選択して、 Google フォームに回答してみる
  10. 回答内容が Slack に通知されれば OK

スクリーンショット 2018-10-01 18.12.26.png
トリガーの設定

余裕がある方向け

  • メッセージの内容カスタマイズ
  • 普段使っている Slack で incoming webhook を作成 してslackWebhookUrl を更新して投稿してみる
var slackWebhookUrl = '';// Slack の Webhook URL を入力

function onSubmit(e) {
  // Google フォームの回答状況を文章化する
  // [ 例 ]
  // 社名: グロービス
  // Qiita: tanabee
  var answer = e.response.getItemResponses().map(function (itemResponse) {
    return itemResponse.getItem().getTitle() + ': ' + itemResponse.getResponse();
  }).join('\n');

  // インラインコードの書式を回答情報に設定して、通知文言を適宜追記
  var message = [
    '新しい投稿がありました。',
    '```', answer, '```'
  ].join('\n');

  // Slack 通知
  postSlack(slackWebhookUrl, message);
}

function postSlack(webhookUrl, message) {
  // UrlFetchApp.fetch メソッドで外部リソース(Slack)にアクセス
  // 参考: Use your Incoming Webhook URL to post a message
  // https://api.slack.com/incoming-webhooks#posting_with_webhooks
  UrlFetchApp.fetch(slackWebhookUrl, {
    method: 'POST',
    headers: { "Content-Type": 'application/json' },
    payload: JSON.stringify({ text: message })
  });
}

Spreadsheet to JSON

  • スプレッドシートの内容を JSON 変換して API 化するサンプル
  • Spreadsheet to JSON

作業の流れ

  1. API 化したいスプレッドシートを探す。ない場合は、都道府県別人口シート を利用(ソース)。
  2. https://script.google.com にアクセスして 新規スクリプト を選択
  3. プロジェクト名を入力、保存
  4. 以下のコードを GAS エディタにペーストして保存
  5. 1 で選んだスプレッドシートの ID とタブ名を 1-2 行目の変数に代入
  6. 公開 > ウェブアプリケーションとして導入 を選択(以下画像参照)
  7. スクリプトに権限を与える操作を行う
  8. 出力された URL をブラウザ等から叩いてみる

スクリーンショット 2018-10-01 23.03.10.png
上記のように設定して導入。

アウトプット: 都道府県別人口 API

余裕がある方向け

  • JSON の形式を変えてみる(ネストさせる、count を入れるなど)
  • query parameter を受け取って、特定の条件に合うものだけを返す
  • ページング実装
  • Properties Service を用いて設定値管理する
var spreadSheetId = '1gWI98XMLhtI3BRtgk5sMCAgC-mWLb-KA-degLehgjSw';// スプレッドシート ID
var spreadSheetTabName = 'シート1';// スプレッドシートのタブ名

// doGet は GET リクエストに反応するメソッド
// 参考: https://developers.google.com/apps-script/guides/triggers/
function doGet(e) {
  // 該当のシートから全行取得
  var rows = SpreadsheetApp
    .openById(spreadSheetId)
    .getSheetByName(spreadSheetTabName)
    .getDataRange()
    .getValues();
  // 1 行目を key に使う
  var keys = rows.shift();
  // 1 行目以降を Object 形式で配列に格納
  var data = rows.map(function(row) {
    var obj = {};
    row.forEach(function(element, i) {
      obj[keys[i]] = element;
    });
    return obj;
  });
  // JSON 形式で return
  return ContentService.createTextOutput(JSON.stringify(data))
                       .setMimeType(ContentService.MimeType.JSON);
}

Spreadsheet to Calendar

作業の流れ

  1. スプレッドシートの テンプレ を開いて、 ファイル > コピーを作成
  2. ツール > スクリプトエディタ を選択
  3. プロジェクト名を入力、保存
  4. 以下のコードを GAS エディタにペーストして保存
  5. onOpen メソッドを選択して実行
  6. スクリプトに権限を与える操作を行う
  7. スプレッドシートに戻ると、カレンダー連携メニューが表示される
  8. カレンダー連携 > 一括登録 を選択して、Google カレンダーを確認

余裕がある方向け

// Spreadsheet 起動時に呼び出す関数
function onOpen() {
  SpreadsheetApp
    .getActiveSpreadsheet()
    // カレンダー連携メニューをスプレッドシートに追加
    // 一括登録、キャンセルメニューとメソッドの紐付け
    // https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#addMenu(String,Object)
    .addMenu('カレンダー連携', [
      {name: '一括登録', functionName: 'registerAll'},
      {name: '一括キャンセル', functionName: 'cancelAll'},
    ]);
}

// カレンダーにイベントを登録
function registerAll() {
  // 開いているシートを取得
  var sheet = SpreadsheetApp.getActiveSheet();
  var eventIds = sheet
    // データが入っている範囲を取得
    .getDataRange()
    // 2次元配列形式で取得
    .getValues()
    // 一行目と、イベント ID が登録済みの場合は除去
    .filter(function (e, i) {
      return i !== 0 && e[6] === '';
    // イベントを一括登録して、イベント ID の配列を作成
    }).map(function (e) {
      // Google カレンダーに登録
      var calendarEvent = CalendarApp.getDefaultCalendar().createEvent(
        e[0], e[1], e[2],
        {
          description: e[3],
          location: e[4],
          guests: e[5]
        });
      // 作成済みイベントの ID を返す
      return [calendarEvent.getId()];
    });

  // 該当するイベントがなければスプレッドシートに記載しない
  if (eventIds.length === 0) return;

  // イベント一覧をスプレッドシートに記載
  sheet.getRange('G2:G' + (eventIds.length+1) ).setValues(eventIds);
}

// 登録されたイベントをキャンセルして、シートから削除
function cancelAll() {
  // イベント一覧の G 列データ範囲
  var range = 'G2:G1000';
  // 開いているシートを取得
  var sheet = SpreadsheetApp.getActiveSheet();

  sheet
    // range で指定した範囲を取得
    .getRange(range)
    .getValues()
    // イベント ID 欄が空のものを除く
    .filter(function (eventId) {
      return eventId[0] !== '';
    // イベントの削除
    }).forEach(function (id) {
      CalendarApp.getDefaultCalendar().getEventById(id).deleteEvent();
    });
  // G 列のデータをクリア
  sheet.getRange(range).clear();
}

ハマりどころ

Drive 操作時のアカウントと Apps Script の実行アカウントが異なってエラー

複数アカウントでログインしている時にたまに起こるので、作業(ハンズオン)前に一度 Google アカウントのログアウト後にログインしてから作業するとズレが起こらない。もしくは Chrome のシークレットブラウザで実行する。

スクリプトに権限を与える操作を行う流れ

@gmail.com で GAS 実行時にスクリプトに権限を与えようとすると、「このアプリは確認されていません」ワーニングが出ます。以下のフローで権限を与えることができます。

スクリーンショット 2018-10-03 22.28.58.png

スクリーンショット 2018-10-03 22.33.16.png

スクリーンショット 2018-10-03 22.29.17.png

スクリーンショット 2018-10-03 22.29.32.png

スクリーンショット 2018-10-03 22.29.45.png