GASでYoutubeの動画投稿をDiscordに通知する

**IPFactory Advent Calendar 2023 16日目の記事です。**

https://qiita.com/advent-calendar/2023/ipfactory

# はじめに
今回は **Google App Script (GAS)** を使ったYoutubeの動画投稿をDiscordへ通知するスクリプトを作ってみたので解説していきます。
ソースコードが欲しい方は最後まで飛ばしてください。

:::note warn
筆者はGASやjsの経験が浅いです。参考程度にご覧ください。
:::

# 環境
Google App Script

## 利用サービス
- YouTube Data API v3
- Drive API


# Youtubeから動画を取得する
GASで使えるサービスの**Youtube Data API**を使うことで以下のように動画を取得できます。
@から始まるハンドルではなくIDが必要な点に注意してください。
```js
var results = YouTube.Search.list('id,snippet', {
"maxResults": 1, // 取得する動画の数
"channelId": channel, // チャンネルID
"order": "date", // 並べ替え
"type": "video", // 動画の種類
"eventType": "none", // 配信タイプ
});
```

# 取得した動画IDをGoogle Driveに保存する
GASで使えるサービスの**Google Drive API**を使います。
繰り返し実行すると重複して取得してしまうのでファイルにメモしておきます。

```js
const files = folder.getFilesByName(config.filename)
file = files.next()
const result = getYouTubeData(channel);
const detail = result.items[0]
var text = file.getBlob().getDataAsString("utf8");
var jsonobj = JSON.parse(text);
if (jsonobj.includes(detail.id.videoId)) {
 return
}
jsonobj.push(detail.id.videoId)
file.setContent(JSON.stringify(jsonobj))
```

# Discordに通知を投稿する
Webhookを使って投稿します。

```js
const webhook_url = "https://discord.com/api/webhooks/**********/**********"
const payload = {
 "username": "ユーザー名",
 "avatar_url": "アイコンurl",
 "content": "メッセージ"
}

UrlFetchApp.fetch(webhook_url, {
 "method": "post",
 "contentType": "application/json",
 "payload": JSON.stringify(payload)
})
```

# コード
以上を組み合わせたコードが以下になります。
config.jsonを使って設定が行えるように手を加えています。
トリガーを利用して15分間隔で動かすのがおススメです。

```javascript
const folder_name = "gas_youtube_discord";

const folders = DriveApp.getFoldersByName(folder_name);
let folder;
if (!folders.hasNext()) {
  folder = DriveApp.createFolder("gas_youtube_discord");
} else {
  folder = folders.next()
}

const config_files = folder.getFilesByName("config.json")
let config_file;
if (!config_files.hasNext()) {
  config_file = folder.createFile("config.json",JSON.stringify({
    "channels": ["UC3qJ7qxc3hDnftTYetCkSaw"],
    "webhook_url": "",
    "icon_url": "",
    "message": "新しい動画です！",
    "filename": "videoid.json"
  }))
} else {
  config_file = config_files.next()
}

const config = JSON.parse(config_file.getBlob().getDataAsString("utf8"))

// YouTubeのデータ取得設定
function getYouTubeData(channel) {
  //YouTube Data API　リストのgetリクエスト
  var results = YouTube.Search.list('id,snippet', {
  "maxResults": 1,
  "channelId": channel,
  "order": "date",
  "type": "video",
  "eventType": "none",
  });
  return results
}

function post(payload) {
  UrlFetchApp.fetch(config.webhook_url, {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify(payload)
  })
}

function main() {
  const files = folder.getFilesByName(config.filename)
  let file;
  if (!files.hasNext()) {
    file = folder.createFile(config.filename,"[]")
  } else {
    file = files.next()
  }
  for (var channel of config.channels) {
    const result = getYouTubeData(channel);
    const detail = result.items[0]
    
    var text = file.getBlob().getDataAsString("utf8");
    var jsonobj = JSON.parse(text);
    if (jsonobj.includes(detail.id.videoId)) {
      return
    }
    jsonobj.push(detail.id.videoId)
    file.setContent(JSON.stringify(jsonobj))
    var snippet = detail.getSnippet()
    var payload = {
      "username": snippet.channelTitle,
      "avatar_url": config.icon_url,
      "content": `${config.message}\nhttps://www.youtube.com/watch?v=${detail.id.videoId}`
    }
    post(payload)
  }
}
```

# まとめ
ちゃんとGASに触ったのはこれが初めてでしたが、簡単に作成できました。
APIなんかもGASで作れるらしいので今後やってみたいと思います。

# 参考文献
https://qiita.com/rs_sho/items/942c781575fc4fc6e163