無料で実現！Spotify Podcast通知メール送信バッチの作成ガイド - GASを活用してみた

# はじめに
web関連でまだまだ初学者のものです。そこで、新卒で1年も経たずにいると「定期バッチはAWSやGCPを使うものだ！」なんていう偏見にまみれていました。しかし、個人的にいただいた案件でものすごい簡単な定期バッチを作るときに課金するのめんどくさいな〜って考えたときに、GASで何とかならないかなと思い、調べてみて実装したものをちょっと変えたものです。
今回の概要として、

**『Spotify APIを叩いてPodcastの最新のエピソードを取得して、メール通知する』**

を全て無料で楽に実装しただけのお話しです。ちょっとした記録として、書きます。


# 前提

### 準備するもの

- google speadsheet（google アカウントは無料枠でOK）
- [spotify for developer](https://developer.spotify.com/)用のアカウント

https://developer.spotify.com/

### 考え方
- お金をかけたくない！無料がいい！
- 絶対に漏れてはいけない個人情報を扱わない
- ビジネスサイドの人も簡単にさわれたほうがいい
- なるべくネットワーク上でやりたい（ローカル開発しない）
    - DB：スプレッドシートにしちゃえ！
    - コードの記述：GASでよくない？


# 1. Spotify APIでPodcastの情報を取得できる
Spotify APIのドキュメントはすごい丁寧なので、読みながらやってみると簡単なのでぜひいじってみてください！ここでは、Podcastの情報取得のためにSpotify for developerで必要な設定をします。

### Getting started with Web API
https://developer.spotify.com/documentation/web-api/tutorials/getting-started

1. アカウントのダッシュボードで、**"Create App"** を選択
2. App name, App description, Website, Redirect URIを適当に入れて、Web APIを選択し、**Save** します。
3. 今回は、**podcast_info** とか言うテキトーな名前をつけました
4. ダッシュボードに戻り、今作った、作ったアプリ(podcast_info)を開きます。
5. **Setting** を選択すると、clientID、App Status、Client secretを取得できます。![](https://camo.qiitausercontent.com/26a03bfc40e180990c1db84401feecbf616398b9/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f7a656e6e2d757365722d75706c6f61642f3165376533643631363261342d32303233313132312e706e67)
6. これで準備完了！



# 2. GASでPodcastの取得
### 実現したいこと
- PodcastのShowの取得
- Podcastの最新のエピソード取得

今回は **『霜降り明星のオールナイトニッポン』** で試してみたいと思います。

https://open.spotify.com/show/6BSPw7yLnXxb2jFuuCyXpb



### Spotifyのアクセストークンを取得
Spotifyで情報を取得する際には、アクセストークンを取得する必要があります。ドキュメントの方には、curlコマンドで取得する方法が書いてありますが、このトークンは1時間程度の有効期限しかありません。そのため、バッチが回るごとに取得する必要があるので、今回、実装しました。

- clientId: spotifyのダッシュボードから取れるクライアントのID
- clientSecret: spotifyのダッシュボードから取れるクライアントシークレットの値

```js: getAccessToken.gs
function getSpotifyToken(clientId, clientSecret) {
  const endpoint = 'https://accounts.spotify.com/api/token';
  const credentials = Utilities.base64Encode(clientId + ':' + clientSecret);

  const options = {
    'method': 'post',
    'headers': {
      'Authorization': 'Basic ' + credentials
    },
    'payload': {
      'grant_type': 'client_credentials'
    }
  };

  const response = UrlFetchApp.fetch(endpoint, options);
  const jsonResponse = JSON.parse(response.getContentText());
  
  // tokenの出力（出力する必要はないので、コメントアウト）
  // Logger.log(jesonResponse)

  return jsonResponse.access_token;
}
```

出力結果↓
![](https://storage.googleapis.com/zenn-user-upload/5d44954bef39-20231126.png)



### Podcastのタイトルと概要を取得
![](https://storage.googleapis.com/zenn-user-upload/310c819a781d-20231126.png)

上の赤い枠内にある情報を取得してみます。

```js: getPodcast.gs
function getPodcast(showId, token) {
  // APIリクエストを構築
  const urlShow = 'https://api.spotify.com/v1/shows/' + showId;
  const options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + token
    }
  };

  // APIリクエストを送信
  const responseShow = UrlFetchApp.fetch(urlShow, options);

  // レスポンスを取得
  const podcast = JSON.parse(responseShow.getContentText());
  
  // タイトルと概要を出力（実際には出力する必要はないのでコメントアウト）
  // Logger.log(podcast.name);
  // Logger.log(podcast.description);

  return podcast
}
```

出力結果↓
![](https://storage.googleapis.com/zenn-user-upload/8c1cf2ef2ab3-20231126.png)

### 最新のエピソードを取得
参照しているPodcastの最新のエピソードを取得します。
今回はタイトル、概要、リンクを取得します。やっていることは、Podcastそのものをとっているときとほとんど同じです。

```js: getLatestEpisode.gs
function getLatestEpisode(showId, token) {
  // APIリクエストを構築
  const urlLatestEpisode = 'https://api.spotify.com/v1/shows/' + showId + '/episodes?market=ES&limit=1';
  const options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + token
    }
  };

  // APIリクエストを送信
  const responseEpisode = UrlFetchApp.fetch(urlLatestEpisode, options);
  const jsonResponseEpisode = JSON.parse(responseEpisode.getContentText());

  const latestEpisode = jsonResponseEpisode.items[0]; // 最新のエピソード
  
  // タイトルと概要を出力（実際には出力する必要はないのでコメントアウト）
  // Logger.log(latestEpisode.name);
  // Logger.log(latestEpisode.description);
  // Logger.log(latestEpisode.href);

  return latestEpisode
}
```

出力結果↓
![](https://storage.googleapis.com/zenn-user-upload/c3c13fadd9af-20231126.png)


# 3. spreadsheetへ書き込む

これまでSpotifyから情報を取得してきました。
- 作成したspreadsheetのタブを **'Spotify'** とします。
- spreadsheetId: 
    - `https://docs.google.com/spreadsheets/d/<spreadsheet id>/edit`
    - 上記のURLの`<spreadsheet id>`にある文字列

```js: writeSpreadsheet.gs
function writeToSpreadsheet(spreadsheetId, podcast, latestEpisode) {
  // 既存のspreadsheetにアクセス
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  // 'spotify'というタブに記入。もしなければ作成。
  let sheet = spreadsheet.getSheetByName('spotify');
  if (!sheet) {
    sheet = spreadsheet.insertSheet('spotify');
  } else {
    sheet.clear();    // 既存のデータをクリア（今回は蓄積しない）
  }

  // ヘッダーを確認し、必要に応じて追加
  var firstRow = sheet.getRange(1, 1).getValue();
  if (firstRow !== 'Spotifyの取得結果') {
    sheet.insertRowBefore(1).getRange(1, 1).setValue('Spotifyの取得結果');
  }

  // スプレッドシートに保存
  const data = [
    ["---------------------Podcast---------------------"],
    ["タイトル", podcast.name],
    ["概要", podcast.description],
    ["-------------------New Episode-------------------"],
    ["タイトル", latestEpisode.name],
    ["概要", latestEpisode.description], 
    ["リンク", latestEpisode.href]
  ]

  for (var i = 0; i < data.length; i++) {
    sheet.appendRow(data[i]);
  }
}
```

出力結果↓
 ![](https://storage.googleapis.com/zenn-user-upload/ce3d1847e539-20231126.png)


# 4. スプシの書かれた内容をメールで送る

```js: sendEmail.gs
function sendEmail(spreadsheetId, address) {
  var range = 'A1:B8'; // 送信したいデータの範囲を指定

  // スプレッドシートからデータを取得
  var sheet = SpreadsheetApp.openById(spreadsheetId).getActiveSheet();
  var data = sheet.getRange(range).getValues();

  // データを文字列に変換
  var message = 'Podcast 出力結果 \n 最新エピソード';
  data.forEach(function(row) {
    message += row.join('\t') + '\n'; // タブで列を区切り、改行で行を区切る
  });

  // メールの設定
  var email = address; // 受信者のメールアドレス
  var subject = 'Poadcastとれたよ';

  // メール送信
  MailApp.sendEmail(email, subject, message);
}
```

送信結果↓
![](https://storage.googleapis.com/zenn-user-upload/73e1cffa1f6b-20231126.png)

# 5.これまでを一括で実行する
これまでに実装してきたものは以下のものです。

- `getSpotify.gs`: アクセストークンの取得
- `getPodcast.gs`: Podcastのタイトルと概要
- `getLatestEpisode.gs`: 最新のエピソードのタイトルと概要とリンクを取得
- `writeToSpreadsheet.gs`: 取得した情報をスプシへ書き込み
- `sendEmail.gs`: 書き込まれた情報をメールで送信

これらをまとめて実装する `index.gs` を作成します。

- client ID
- client Secret
- show ID
- spreadsheet ID
- e-mail address

を宣言した上で一括で実行します。

```js: index.gs
function index() {
  // 各種変数の宣言
  const clientId = '<spotify client id>'; // SpotifyクライアントID
  const clientSecret = '<spotify client secret>'; // Spotifyクライアントシークレット
  const showId = '<spotify show id>'; // 取得したいShowのIDを入力
  const spreadsheetId = '<spreadsheet id>'; // スプレッドシートのID
  const address = '<e-mail address>'; // 送信先のアドレス

  const token = getSpotifyToken(clientId, clientSecret);
  const podcast = getPodcast(showId, token);
  const latestEpisode = getLatestEpisode(showId, token);

  writeToSpreadsheet(spreadsheetId, podcast, latestEpisode);
  sendEmail(spreadsheetId, address);
}
```





# 6. 定期的に `index.gs` を実行する設定
GASには **トリガー** という機能があります。
下のように `index.gs` に対して、時間主導型で1時間おきに設定することで、定期バッチ的な役割をさせます。`index.gs` で関数の実行順についても心配いらないため、このまま実行できます。

![](https://storage.googleapis.com/zenn-user-upload/88dad9af1504-20231126.png)




# 【ついでに】 GASの変数を隠す（ローカルでいうenv化っぽくする）
今回の記事では、client IDやclient Secret、spreadsheet IDなどは変数宣言するときに、以下のようにしていました

```js: 変数の宣言（もとの）
// 各種変数の宣言
const clientId = '<spotify-client-id>';     // SpotifyクライアントID
const clientSecret = '<spotify-client-secret>'; // Spotifyクライアントシークレット
const spreadsheetId = '<speadsheet-id>'; // スプレッドシートのID
const showId = '<show-id>'; // 取得したいShowのIDを入力
```


個人開発であれば、特に問題はありませんが、本来は直接書き込むことはコード上では隠すべきです。こんなときに使えるプロパティサービス（スクリプト プロパティ）がGASにはあります。プロジェクトの設定から、下に下がると「スクリプト プロパティ」という欄があるので、そこに変数を追加していきます。

![](https://storage.googleapis.com/zenn-user-upload/0bbba5ad5cfa-20231126.png)

こうしたら、下記のように変数をプロパティから呼び出すように変更してあげれば、値を持ってくることができます。`PropertiesService.getScriptProperties().getProperty()`で呼び出すこことができます。

```js: 変数宣言（プロパティサービス使ったもの）
// 各種変数の宣言
const clientId = PropertiesService.getScriptProperties().getProperty("SPOTIFY_CLIENT_ID"); // SpotifyクライアントID
const clientSecret = PropertiesService.getScriptProperties().getProperty("SPOTIFY_CLIENT_SECRET"); // Spotifyクライアントシークレット
const showId = PropertiesService.getScriptProperties().getProperty("SPOTIFY_SHOW_ID"); // 取得したいShowのIDを入力
const spreadsheetId = PropertiesService.getScriptProperties().getProperty("SPREADSHEET_ID"); // スプレッドシートのID
const address = PropertiesService.getScriptProperties().getProperty("EMAIL_ADDRESS"); // アドレス
```


# 感想
コストをおさえて、簡単にできるように自動化だったり、意外と役立つちょっとしたものを作るのが一番楽しい（笑）

ちなみにこれ別のAPI叩けば、いろんなバッチができる〜