notebook

都内でWEB系エンジニアやってます。

GoogleAppsScriptで毎日レポートをSlackへ通知する

はじめに

この記事はGoogle Apps Script Advent Calendar 2018 の22日目の記事です

GoogleAppsScriptのドキュメントを眺めていたらChartServiceというものを見つけて使ってみたくなったので使ってみます

Charts Service  |  Apps Script  |  Google Developers

https://developers.google.com/apps-script/reference/charts/developers.google.com

やってることはありがちなやつだし代替手段もいくらでもありそうですが、気軽に実践できるということで!

やりたいこと

たとえばブログのPVなど、GoogleAnalytics見に行けば良いといえば良いのですが、毎日Slackで簡易レポート的なものが送られてきたら楽ですよね?

定期的に(毎日とか)に数値を見ることで変化に気づけるかも?ということでやってみます

手順

  • claspでプロジェクト作成
  • clasp login
  • 実装
    • AnalyticsServiceで数値データを取得
    • ChartServiceでグラフを生成
    • SlackへPost
  • ScriptPropertyへTOKENを登録
    • SlackのAPITOKEN
    • SlackのチャンネルID
    • AnalyticsのID
  • 定期実行トリガーの設定

「プロジェクトの作成からコード書いて実行できるようになるまで」は今回割愛し実装からやってみます

下記の記事で扱っているので気になる方はこちらを参照してください

Google App Script(clasp)でソーシャルカウントを取得して可視化する - notebook

swfz.hatenablog.com

AnalyticsServiceで数値データを取得

Analyticsの数値は下記で確認しながら欲しいデータを選定します

Query Explorer — Google Analytics Demos & Tools

https://ga-dev-tools.appspot.com/query-explorer/ga-dev-tools.appspot.com

こんな感じのコードになりました

  const from = Moment.moment().subtract(1, "days").format("YYYY-MM-DD");
  const to = Moment.moment().subtract(1, "days").format("YYYY-MM-DD");
  const metrics = "ga:pageViews";
  const options = {
    "dimensions": "ga:pageTitle",
    "max-results": 5000,
  };

  const gaReport = Analytics.Data.Ga.get(
    `ga:${PropertiesService.getScriptProperties().getProperty("GA_TABLE_ID")}`,
    from,
    to,
    metrics,
    options,
  );

ChartServiceでグラフ生成

今回いじってみたかった箇所です

  • DataTableを生成して、Analyticsから取得したレポートデータを逐次突っ込む
  • チャートを生成する
  • イメージ化する
  const dataTable = Charts.newDataTable()
    .addColumn(Charts.ColumnType.STRING, "Title")
    .addColumn(Charts.ColumnType.NUMBER, "PV");
  gaReport.rows.sort((a,b) => b[1] - a[1]).filter((_,i) => i < 10).forEach(row => {
    dataTable.addRow(row);
  });
  const chart = Charts.newBarChart()
    .setDataTable(dataTable)
    .setTitle("前日PV上位10記事")
    .build();

  const imageFile = chart.getAs("image/png");

とくに難しいことはないですね

本当は下記を使用して前々日と比較したグラフをポストしたかったのですがよく見たら「GoogleAppsScript」じゃなく「GoogleCharts」のドキュメントだったのでさっと実装できず

Diff Charts  |  Charts  |  Google Developers

https://developers.google.com/chart/interactive/docs/gallery/diffchartdevelopers.google.com

今後の宿題にしておきます。。。

Slackへpost

SlackのファイルアップロードAPIに対してUrlFetchAppを使用してポストするだけです

後述するプロパティサービスを使ってTOKENなどを取得します

  const token = PropertiesService.getScriptProperties().getProperty(
    "SLACK_API_TOKEN"
  );
  const channelId = PropertiesService.getScriptProperties().getProperty(
    "SLACK_CHANNEL_ID"
  );

  UrlFetchApp.fetch("https://slack.com/api/files.upload", {
    method: "post",
    payload: {
      channels: channelId,
      file: imageFile,
      filename: "daily-pv-top-10.png",
      token: token,
    }
  });

ScriptPropertyへデータを登録

APIトークンなどコード上でべた書きしたくないものをこのプロパティに登録してスクリプトから特定の操作をして取り出すことで安全にデータを取得できる仕組みです

登録はスクリプトエディターの画面からFile -> Project Properties -> Script properties

タブまで遷移すると設定できます

f:id:swfz:20181222075833p:plain
プロパティの設定

今回はSlackのAPI TOKEN, チャンネルID, AnalyticsのテーブルIDを登録しました

GASのコード上ではこんな感じで取り出します

const token = PropertiesService.getScriptProperties().getProperty(
  "SLACK_API_TOKEN"
);

タイムアップで調べられなかったのですがプロパティもCLIから登録できたらうれしいですね

定期実行トリガーの設定

Edit -> Current project's triggersから別タブへ遷移し新たに設定します

今回は毎日朝に前日分のデータを受け取るようにしました

f:id:swfz:20181222075951p:plain
定期実行トリガーの設定

ちょっと詰まったとことか後で見返したとき用のメモ的なものを残しておきます

  • clasp login
clasp login --no-localhost

手動でキーを入力するパターン

VMとかの中で開発しているときとかに使って手動でキーを入力してログインするためのオプション

  • Analyticsレポートの有効化

毎度やりかた忘れてるのでリンクだけ残しておきます

GoogleAnalyticsのAPIとGoogleAppsScriptでGoogleAnalyticsのレポートを自動化する方法 - Bowyer Tech Blog

bowyer-app.com

appscript.jsonのdependenciesに記述してpushするだけではGAS経由で実行できず

コンソールから対象プロジェクトからのAPIアクセスを許可する必要があります

実行

最終的なコードはこうなりました

  • src/appscript.json
{
  "timeZone": "America/New_York",
  "dependencies": {
    "enabledAdvancedServices": [{
      "userSymbol": "AnalyticsReporting",
      "serviceId": "analyticsreporting",
      "version": "v4"
    }, {
      "userSymbol": "Analytics",
      "serviceId": "analytics",
      "version": "v3"
    }],
    "libraries": [{
      "userSymbol": "Moment",
      "libraryId": "15hgNOjKHUG4UtyZl9clqBbl23sDvWMS8pfDJOyIapZk5RBqwL3i-rlCo",
      "version": "9"
    }]
  },
  "exceptionLogging": "STACKDRIVER"
}
  • src/Code.ts
function sendReport() {
  const from = Moment.moment()
    .subtract(1, "days")
    .format("YYYY-MM-DD");
  const to = Moment.moment()
    .subtract(1, "days")
    .format("YYYY-MM-DD");
  const metrics = "ga:pageViews";
  const options = {
    dimensions: "ga:pageTitle",
    "max-results": 5000
  };

  const gaReport = Analytics.Data.Ga.get(
    `ga:${PropertiesService.getScriptProperties().getProperty("GA_TABLE_ID")}`,
    from,
    to,
    metrics,
    options
  );

  Logger.log(gaReport);

  const dataTable = Charts.newDataTable()
    .addColumn(Charts.ColumnType.STRING, "Title")
    .addColumn(Charts.ColumnType.NUMBER, "PV");
  gaReport.rows
    .sort((a, b) => b[1] - a[1])
    .filter((_, i) => i < 10)
    .forEach(row => {
      dataTable.addRow(row);
    });
  const chart = Charts.newBarChart()
    .setDataTable(dataTable)
    .setTitle("前日PV上位10記事")
    .build();

  const imageFile = chart.getAs("image/png");

  const token = PropertiesService.getScriptProperties().getProperty(
    "SLACK_API_TOKEN"
  );
  const channelId = PropertiesService.getScriptProperties().getProperty(
    "SLACK_CHANNEL_ID"
  );

  UrlFetchApp.fetch("https://slack.com/api/files.upload", {
    method: "post",
    payload: {
      channels: channelId,
      file: imageFile,
      filename: "daily-pv-top-10.png",
      token: token
    }
  });
}

実行してみるとこんな出力になります

f:id:swfz:20181222080026p:plain
実行結果

まとめ

あれ?PV数が少n。。。っていうのは気にしない方向で。。。w

細かいところだと色変えたかったりタイトルを全部読めるようにしたかったりと色々ありますがいったんやりたかった定期的にレポートを投げるということができるようになりました

投稿が画像ポストになってしまっているのでマウス当てたらツールチップが出るとかそういう挙動はできません

なので意味合い的には速報的な使い方なのであまり凝ったグラフにする必要もなさそうですね

実装自体は表現したいチャートのドキュメント探してそれ見て実装するだけです

結構楽なのでブログのレポート以外にも色々使えると思います

今後活用していきたいと思いました