今回は GAS x Github GraphQL API を使用して本日の Contribution 数を通知する Discord bot を作成してみます
通知だけなら Webhook を使用して無料かつサーバーレスに Discord bot を導入できます
使用技術
Google App Script (GAS), GitHub GraphQL API, Webhook
Discord 上で Webhook を作成
Webhook はアプリケーション内で発生したイベントをトリガーとして他のアプリケーションへ通知を送る仕組みです.Discord が提供している Webhook を使用すれば,発行したURLにPOSTするだけで Discord のメッセージを送信できます
https://discord.com/developers/docs/resources/webhook
Webhookの作成
Discord で適当なサーバーとチャンネルを作り,テキストチャンネルの編集 > 連携サービス > 新しいウェブフック から Webhook を作成できます
適当な名前の Webhook が作成されるので編集して使います
ウェブフックURLをコピー から Webhook のURLを取得できます.このURLにリクエストボディを{content: "message"}
のようにしてPOSTすればメッセージを送信できます
リクエストボディにusername
とかavatar_url
を設定すれば bot の名前とアバター画像を上書きできます
https://discord.com/developers/docs/resources/webhook#execute-webhook
GitHub GraphQL API を使用して本日の Contribution 数を取得する
GitHubはユーザー情報の取得やリポジトリの操作のためのAPIとして GitHub REST API と GitHub GraphQL API を公開しています
https://docs.github.com/ja/graphql
GitHub GraphQL APIを叩いてみる
Github CLI から GitHub GraphQL API のお試しが可能です.今回必要となるデータは これ です.login: <user_name>
を自分のものに書き換えて試してみてください
Github CLIをインストールしていない方はこちらでも試せるらしいです
https://docs.github.com/ja/graphql/overview/explorer
gh api graphql -f query='
query contributions {
user(login: <user_name>) {
contributionsCollection(to: "2024-04-10T00:00:00", from: "2024-04-09T00:00:00") {
contributionCalendar {
weeks {
contributionDays {
date
contributionCount
}
}
}
}
}
}
'
レスポンス
(略)
"contributionDays": [
{
"date": "2024-04-09",
"contributionCount": 2
},
{
"date": "2024-04-10",
"contributionCount": 1
}
]
(略)
正しく Contribution 数を取得できているみたいです
Github REST API でも試してみましたが,IssueやPRの作成も含めた Contribution 数の取得は難しそうでした
GAS から Github GraphQL API を叩く
Google App Script (GAS) は Google が提供するローコードプラットフォームです.Googleドライブ上でファイルを作成するだけで簡単に実行可能です.
Google サービスとの連携が主な用途ですが,定期実行スクリプトのデプロイのしやすさから Webhook の連携にも適していると思います
Github API アクセストークンの取得
GitHub CLI 以外から GitHub GraphQL API にアクセスするにはトークンが必要なので取得します
GitHub の Settings > Developer Settings > Tokens (classic) から Generate new token > Generate new token (classic) を選択します
Expiration (トークンの有効期限) を適当に設定し,user > read:user の項目のみチェックを入れてトークンを生成します
ghp_<...>
みたいな文字列がトークンです.絶対に公開しないでください
GASでコーディング
GAS でコーディングします.JavaScript をベースとしたプログラミング言語なので,慣れている人は読みやすいと思います
// パラメータ
const USERNAME = "<GitHubのユーザー名>"
const GITHUB_TOKEN = "<作成したGitHub APIのアクセストークン>"
const DISCORD_URL = "<発行したDiscord WebhookのURL>"
const GITHUB_URL = "https://api.github.com/graphql"
let today = new Date(new Date().toLocaleString("ja-JP", {timeZone: "Asia/Tokyo"}))
// GraphQLのクエリ
// 今日のContribution数を取得する
const query = `query contributions {
user(login: "${USERNAME}") {
contributionsCollection(to: "${today.toISOString()}", from: "${today.toISOString()}") {
contributionCalendar {
weeks {
contributionDays {
date
contributionCount
}
}
}
}
}
}
`
// Github APIからContribution数を取得する
function getNumOfContributions () {
// リクエストのオプション
let options = {
"method": "GET",
"headers": {
"Authorization": `Bearer ${GITHUB_TOKEN}`,
"Content-Type": "application/json"
},
"payload": JSON.stringify({ query })
}
// Github APIからデータを取得する
let response = UrlFetchApp.fetch(GITHUB_URL, options)
// Contribution数を取得する
if (response.getResponseCode() === 200) {
// 正しくレスポンスが返ってきた場合
// レスポンスをパース
let datas = JSON.parse(response.getContentText())
// 適当にcontribution数を取り出す
let contribution = datas.data.user.contributionsCollection.contributionCalendar.weeks[0].contributionDays[0].contributionCount
return contribution
} else {
// レスポンスが返ってこなかった場合,エラーを投げる
throw new Error("Github APIにアクセスできませんでした.")
}
}
// DiscordのWebhookにメッセージを登録
function postMessage (message) {
// 登録するメッセージ
let payload = {
"content": message
}
// リクエストのオプション
let options = {
"method": "POST",
"payload": payload
}
// Webhookにリクエストを投げる
UrlFetchApp.fetch(DISCORD_URL, options)
}
// 日付を文字列に変換する関数
function formatDate(date) {
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${month}月${day}日 ${hours}時${minutes}分`
}
// エントリポイント
function main () {
try {
// 今日のContribution数を取得してメッセージを送信する
let contribution = getNumOfContributions()
postMessage(`【${formatDate(today)}】 ---- ${contribution} Contributions`)
} catch (e) {
// エラーが生じた場合,その内容を送信する
console.error(e)
postMessage(`【エラーが発生しました】 ${e}`)
}
}
動作確認
上部タブから main
関数を指定し,実行をクリックします
「デプロイ」というボタンもありますが,クリックしなくて大丈夫です
正しく動作すれば Discord bot からメッセージが送信されます
トリガーを設定
最後に作成した main
関数を定時実行するように設定します
午後10時 ~ 11時のどこかのタイミングで実行されるように設定できます
これ以上詳細に時間を設定できませんが,十分に動作するかと思います
元記事