Admobの収益をSlackへ通知するシステムを作りましたよ。

未分類
この記事は約16分で読めます。

はじめに

Admobの収益を毎日確認したいのですが、毎回PCを開くのは面倒です。そんな時Slackでスマホに通知がくるシステムがあると便利そうです。調べてみると、Admobのデータが取得できるAdmob APIがあるようなので、そのAPIを使って収益データを毎日Slackで通知するシステムを作成しました。最終的にこのようなレポートがSlackに毎日届くことを目指します。

この記事では、システムの構築方法やその際の注意点等をまとめています。

アーキテクチャ全体

まず大まかな処理の流れを説明します。

  1. 毎朝Apps Scriptが定期実行される
  2. Apps ScriptでAdmob APIを呼ぶ
  3. Apps ScriptでAdmob APIのレスポンスから広告のタイトルと金額を抽出し、Slackで送りたいメッセージを作成
  4. Apps ScriptでSlack APIにそのメッセージを送信

※Admob APIはOAuthで認可するため初回のみ一度認可処理が必要ですが、2回目以降は以上の処理を繰り返します。

Admob API

初期設定

まずは公式のドキュメント通りに「プロジェクトを作成する」の設定を行ってください。

Get started with the AdMob API  |  Google for Developers

同意画面をテストのままにしていると、認可が1週間で期限が切れるみたいです。テストで作成した場合は、最終的に本番環境に変更することを忘れないようにしましょう。

Google API のinvalid_grantエラーの対処方法
PythonとGoogle Gmail APIを使って自動処理をはじめたのですが、作成後にinvalid_grant エラーというものが発生し、対処を行ったため、その記録を残しておきます。 目次 1. invalid_grant エラーの発

動作確認

続いてドキュメント通りに「リクエストを作成する」の通りに簡単にshellで動作確認を行ってください。とりあえず「2. アカウント情報を取得します。」までできたら、設定完了です。実際使いたいのは、「3. ネットワーク レポートを生成する。」で使用しているエンドポイントですが、実はこのコマンドは間違っています。ほんの少しのOAuthの知識とLinuxの知識があれば気づけるはずですが、私は少し時間がかかってしまいました。。

   -H "$(oauth2l header --json path_to_credentials_json \

   -H "$(oauth2l header --json path_to_credentials_json \
          https://www.googleapis.com/auth/admob.readonly)"

リクエスト作成

今回のシステムで実際に取得したい情報は、ある期間での広告ユニット毎の推定収益額です。そのため、エンドポイント「v1/accounts/pub-XXXXXXXXXXXXXXXX/networkReport:generate」にリスクエストを送ります。これがそのエンドポイントのリファレンスです。

(https://developers.google.com/admob/api/v1/reference/rest/v1/accounts.networkReport/generate)

画面右側のペインをポチポチしてRequest bodyを作成します。ディメンションの値等は、このリファレンス(https://developers.google.com/admob/api/v1/report-metrics-dimensions)を見て決めます。今回はディメンションにAD_UNIT、メトリクスにESTIMATED_EARNINGSを使用します。

ディメンションとメトリクスはグラフでいうと、横軸がディメンションで縦軸がメトリクスのグラフのような関係です。言葉で書くと「ディメンション毎のメトリクスを表示する」という関係になります。今回は「広告ユニット(AD_UNIT)」毎の「見積もり収益額(にESTIMATED_EARNINGS)」という関係です。

完成したリクエストをcURLで表すと以下のようになります。この後これをApps Scriptで書き直します。

 curl -X POST \
    https://admob.googleapis.com/v1/accounts/pub-XXXXXXXXXXXXXXXX/networkReport:generate \
       -H "$(oauth2l header --json path_to_credentials_json \
          https://www.googleapis.com/auth/admob.readonly)"
       -H "Content-Type: application/json" \
      --data @- << EOF
{
  "report_spec": {
    "date_range": {
     "start_date": {"year": 2022, "month": 5, "day": 1},
     "end_date": {"year": 2022, "month": 5, "day": 1}
    },
    "dimensions": ["AD_UNIT"],
    "metrics": ["ESTIMATED_EARNINGS"]
  }
}

EOF

cURLやBashが苦手な方向けの解説

  • curl --data @-
    オプションdataを利用することで、post時のデータをkey value形式で渡すことができる。
    オプションdataの引数に @- をつけると標準入力をオプションdataの引数として渡すことができる。
  • << EOF … EOF
    複数行を記述できるヒアドキュメントというbashの機能
    EOFの部分は終了文字列を表し、その終了文字列が単独で書かれている行までの複数行を標準出力する。
    EOFは任意の文字列でよい。
<< 任意の終了文字列
1行目
2行目
任意の終了文字列

参考: https://qiita.com/take4s5i/items/e207cee4fb04385a9952

合わせると、リクエストボディを複数行で記述できるようになる。

Slack API

ここはたくさん記事があるので詳しい説明は割愛します。こういった記事が参考になります。(https://zenn.dev/kou_pg_0131/articles/slack-api-post-message)最終的にAdmobのレポート通知用のチャンネルと、「Bot User OAuth Token」を作成できていればOKです。

Apps Script

Apps Scriptは、次のフローを実装していきます。

  1. Admob APIからデータ取得
  2. Slack通知用のメッセージを作成
  3. Slack APIへ送信

細かい部分は、コード内のコメントを参照いただければと思います。
OAuth周りの処理はライブラリのREADMEを読んでサンプルを改造した形になります。

デプロイ手順

  1. 新しいスプレッドシートを作成し[拡張機能]>[Apps Script]をクリックし、以下のコードを貼り付けてください。
const Properties = PropertiesService.getScriptProperties();
const admobAPIService = getAdmobAPIService();

// OAuthの準備
function getAdmobAPIService() {
  return OAuth2.createService("AdmobAPI")
    .setAuthorizationBaseUrl("https://accounts.google.com/o/oauth2/auth")
    .setTokenUrl("https://oauth2.googleapis.com/token")
    .setClientId(Properties.getProperty("ADMOB_CLIENT_ID"))
    .setClientSecret(Properties.getProperty("ADMOB_CLIENT_SECRET"))
    .setCallbackFunction("authCallback")
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope("https://www.googleapis.com/auth/admob.readonly")
    .setParam("access_type", "offline")
    .setParam('prompt', 'consent');
}

// 認証処理後のコールバックの処理を定義
function authCallback(request) {
  const isAuthorized = admobAPIService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認可処理が正常に終了しました。このタブを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認可処理に失敗し、リソースへのアクセスが拒否されました。設定を確認してください。");
  }
}

// スプレッドシートのメニューにAdmob API認可用のボタンを設置
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("Admob API連携")
    .addItem("認可処理", "initAuth")
    .addToUi();
}

// Admob API認可URLをダイアログに表示
function initAuth() {
  const authorizationUrl = admobAPIService.getAuthorizationUrl();
  const template = HtmlService.createTemplate(
    '<a href="<?= authorizationUrl ?>" target="_blank">認可</a>. Admob APIの認可をします。'
  );
  template.authorizationUrl = authorizationUrl;
  const page = template.evaluate();
  const title = "Admobアプリの認可処理";

  createModelessDialog(page, title);
}

// ダイアログを表示
function createModelessDialog(html, title) {
  const htmlOutput = HtmlService.createHtmlOutput(html)
    .setWidth(360)
    .setHeight(120);
  SpreadsheetApp.getUi().showModelessDialog(htmlOutput, title);
}

// ある期間(startDate ~ endDate)の広告ユニットごとの推定収益をAdmob APIから取得
function getAdmobReport(startDate, endDate) {
  const payload = JSON.stringify({
    "report_spec": {
      "date_range": {
        "start_date": startDate,
        "end_date": endDate
      },
      "dimensions": ["AD_UNIT"],
      "metrics": ["ESTIMATED_EARNINGS"]
    }
  });
  const response = UrlFetchApp.fetch(
    "https://admob.googleapis.com/v1/accounts/pub-2781004360728551/networkReport:generate",
    {
      headers: {
        Authorization: "Bearer " + admobAPIService.getAccessToken(),
      },
      contentType: "Content-Type: application/json",
      payload: payload
    }
  );

  return JSON.parse(response);
}

// 昨日の日付をオブジェクトで返す。
function getYesterdayDate() {
  const objDate = new Date();
  objDate.setDate(objDate.getDate() - 1);

  return {
    year: objDate.getFullYear(),
    month: objDate.getMonth() + 1,
    day: objDate.getDate()
  }
}

// Admob APIのレスポンス(json)をもとに、広告ユニット毎の推定収益のレポートを作成する。
// 日付がundefinedのときは月単位のレポート作成する。
function makeReportMessage(json, year, month, day) {
  let message = `${year}年${month}月${day ? `${day}日` : ''}のAdmob推定収益レポートです。
\`\`\`
`;

  let toalEarnings = 0;
  for (let i = 1; i < json.length - 1; i++) {
    toalEarnings += Number(json[i].row.metricValues.ESTIMATED_EARNINGS.microsValue);

    // Math.trunc(NUMBER).toLocaleString().padStart(6, " ");
    // ↑この処理はNUMBERの小数点以下切り捨てて、3桁ごとにカンマを入れつつ文字列に変換し、6文字を最大値として右揃えにする。
    // 例1) 1234.56 → " 1,234"
    // 例2) 123.456 → "   123"
    message += `${json[i].row.dimensionValues.AD_UNIT.displayLabel}: 
${Math.trunc(json[i].row.metricValues.ESTIMATED_EARNINGS.microsValue / 1000 / 1000).toLocaleString().padStart(6, " ")}円
`;
  }
  message += `---
合計:
${Math.trunc(toalEarnings / 1000 / 1000).toLocaleString().padStart(6, " ")}円
\`\`\``;

  return message;
}

// メッセージ(message)をSlackの特定のチャンネル(channel)へ送信する。
function postToSlack(channel, message) {
  const url = "https://slack.com/api/chat.postMessage";

  const token = Properties.getProperty("SLACKBOT_TOKEN");
  const options = {
    "method": "post",
    "contentType": "application/x-www-form-urlencoded",
    "payload": {
      "token": token,
      "channel": channel,
      "text": message
    }
  };

  UrlFetchApp.fetch(url, options);
}

// エントリーポイント
function main() {
  // トークンがなければ認可処理を促す
  if (!admobAPIService.hasAccess()) {
    let message = "トークンの期限が切れました。スプレッドシートを開いて再認証してください。";
    console.log(message);
    // Slack APIへメッセージを送信
    postToSlack("#admob-report", message);
    return;
  }

  // この変数messageにSlackに通知するメッセージを追加していく
  let message = '<!channel>\n';

  // 昨日の収益レポートを作成する
  const { year, month, day } = getYesterdayDate();
  const yesterdayResponse = getAdmobReport(
    { year, month, day },
    { year, month, day },
  );
  message += makeReportMessage(yesterdayResponse, year, month, day);

  // 体裁を整えるため改行
  message += "\n\n";

  // 今月の収益レポートを作成する
  const thisMonthResponse = getAdmobReport(
    { year, month, "day": 1 },
    { year, month, day },
  );
  message += makeReportMessage(thisMonthResponse, year, month);

  console.log(message);
  // Slack APIへメッセージを送信
  postToSlack("#admob-report", message);
}
  1. 権限を追加
    1. 左側歯車アイコンの[プロジェクトの設定]をクリック [全般設定] > [「appsscript.json」マニフェスト ファイルをエディタで表示する]  をクリック
    2. 左側のボタン[エディタ]をクリックし戻ると、appsscript.jsonが表示されている。
    3. appsscript.json以下のコードを追加
{
  // …
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/spreadsheets"
  ],
  // …
}

  1. ライブラリ(OAuth2 for Apps Script)を追加
    1. こちらのSetupを行う
  2. スクリプト プロパティ(環境変数)の追加
    1. AdmobのClientIdをADMOB_CLIENT_IDという名前で保存
    2. AdmobのClientSecretをADMOB_CLIENT_SECRETという名前で保存
    3. SlackのBot User OAuth TokenをSLACKBOT_TOKENという名前で保存
  3. Admob APIをApps Scriptから利用可能にするよう認可処理を行う。
    1. ファイルを保存し、スプレッドシートをリロードする。
    2. 新たに追加されているメニュー [Admob API連携] > [認可処理]をクリック
    3. ダイアログ内の認可リンクをクリックし認可する。
      このときAdmob APIで許可したGoogleアカウントでログインし認可処理を行ってください。
  4. テスト実行
    1. Apps Scriptに戻って、main関数を実行
  5. 定期実行
    1. 左側目覚まし時計 トリガーをクリック
    2. 右下[トリガーを追加]をクリック
    3. 画像の通りに設定

まとめ

Admob APIを用いてSlackに収益の簡易レポートを定期送信するシステムの構築方法を紹介しました。Admob APIから取得したデータを用いて、グラフを作成しても良いかと思いましたが、Slackの容量を圧迫しそうであることと、時間がかかりそうであるため今回は文字列のみのレポートにしています。ただ実際グラフが必要なときは、リッチなDashboardサービスを使ったほうが良いかもしれないですね!

タイトルとURLをコピーしました