このチュートリアルでは、GitHub リポジトリの更新を受け取る SMS 通知システムを Workers 上で構築します。リポジトリに新しいアクティビティがあると、Worker が Twilio でテキストメッセージを送信します。
次の内容を学びます。
- Workers で Webhook を構築する
- Workers を GitHub と Twilio に連携する
- Wrangler で Worker の secrets を使う
すべてのチュートリアルは、Cloudflare Workers アカウント、C3 ↗、および Wrangler のセットアップが完了している前提です。セットアップは Get started ガイド で行います。
まず、コマンドラインで npm create cloudflare@latest を使い、Worker プロジェクトを作成します。
npm create cloudflare@latest -- github-twilio-notificationsyarn create cloudflare github-twilio-notificationspnpm create cloudflare@latest github-twilio-notificationsセットアップでは、次のオプションを選びます。
- What would you like to start with? では、
Hello World exampleを選びます。 - Which template would you like to use? では、
Worker onlyを選びます。 - Which language do you want to use? では、
JavaScriptを選びます。 - Do you want to use git for version control? では、
Yesを選びます。 - Do you want to deploy your application? では、
Noを選びます(デプロイ前にいくつか変更します)。
アプリケーションがデプロイされた URL を控えておきます。GitHub Webhook の設定で使います。
cd github-twilio-notifications新しい github-sms-notifications ディレクトリでは、src/index.js が Cloudflare Workers アプリケーションのエントリポイントです。このチュートリアルの大半は、このファイルを設定します。
このチュートリアルには、GitHub アカウントとリポジトリも必要です。どちらも未設定の場合は、新しい GitHub アカウントを作成 ↗ し、新しいリポジトリを作成 ↗ してから続けます。
まず、更新を Worker へ POST する Webhook をリポジトリに作成します。Worker 内で更新をパースします。最後に、Twilio へ POST リクエストを送り、テキストメッセージを送信します。
完成したコードは、この GitHub リポジトリ ↗ で確認できます。
まず、リポジトリに更新があったときに Worker へ POST する GitHub Webhook を設定します。
-
GitHub リポジトリの Settings > Webhooks > Add webhook を開きます。
-
Payload URL に、最初のデプロイ時に控えた Worker URL の
/webhookパスを設定します。 -
Content type のドロップダウンで、application/json を選びます。
-
Secret 欄に、任意のシークレットキーを入力します。
-
Which events would you like to trigger this webhook? で Let me select individual events を選びます。通知を受け取りたいイベント(Pull requests、Pushes、Branch or tag creation など)を選びます。
-
Add webhook を選び、設定を完了します。
ローカル環境が整ったら、Worker でリポジトリの更新をパースします。
生成された index.js は、最初は次のようになっています。
export default {
async fetch(request, env, ctx) {
return new Response("Hello World!");
},
};Request の request.method プロパティで、アプリケーションへのリクエストが POST かどうかを確認します。POST でない場合は、エラーレスポンスを返します。
export default {
async fetch(request, env, ctx) {
if (request.method !== "POST") {
return new Response("Please send a POST request!");
}
},
};次に、正しいシークレットキーでリクエストが送られているかを検証します。GitHub は シークレットキーを使って各ペイロードにハッシュ署名 ↗ を付けます。checkSignature というヘルパー関数でリクエストを確認し、ハッシュが正しいことを保証します。そのあと、リクエストを JSON としてパースし、Webhook のデータを取得できます。
async fetch(request, env, ctx) {
if(request.method !== 'POST') {
return new Response('Please send a POST request!');
}
try {
const rawBody = await request.text();
if (!checkSignature(rawBody, request.headers, env.GITHUB_SECRET_TOKEN)) {
return new Response("Wrong password, try again", {status: 403});
}
} catch (e) {
return new Response(`Error: ${e}`);
}
},checkSignature 関数は、Node.js の crypto ライブラリで既知のシークレットキーを使って受信ペイロードをハッシュし、リクエストのハッシュと一致することを確認します。GitHub は HMAC hexdigest を使い、SHA-256 形式でハッシュを計算します。この関数は、export の前、index.js の先頭に置きます。
import { createHmac, timingSafeEqual } from "node:crypto";
import { Buffer } from "node:buffer";
function checkSignature(text, headers, githubSecretToken) {
const hmac = createHmac("sha256", githubSecretToken);
hmac.update(text);
const expectedSignature = hmac.digest("hex");
const actualSignature = headers.get("x-hub-signature-256");
const trusted = Buffer.from(`sha256=${expectedSignature}`, "ascii");
const untrusted = Buffer.from(actualSignature, "ascii");
return (
trusted.byteLength == untrusted.byteLength &&
timingSafeEqual(trusted, untrusted)
);
}これを動かすには、wrangler secret put で GITHUB_SECRET_TOKEN を設定します。このトークンは、GitHub Webhook を設定したときに選んだシークレットです。
npx wrangler secret put GITHUB_SECRET_TOKENWrangler ファイルに nodejs_compat フラグを追加します。
{
"compatibility_flags": [
"nodejs_compat"
]
}compatibility_flags = [ "nodejs_compat" ]Twilio を使い、リポジトリのアクティビティについてテキストメッセージを送ります。Twilio アカウントと、テキストメッセージを受信できる電話番号が必要です。Twilio のガイド ↗ を参照してセットアップします。(Twilio が初めての場合は、インタラクティブなゲーム ↗ でプラットフォームの使い方を学び、初心者向けの無料クレジットを受け取れます。)
次に、Twilio API エンドポイントへ POST リクエストを送るヘルパー関数を作り、テキストメッセージを送信します。このエンドポイントの詳細は Twilio のリファレンス ↗ を参照してください。
Twilio へのリクエストを扱う新しい関数 sendText() を作成します。
async function sendText(accountSid, authToken, message) {
const endpoint = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`;
const encoded = new URLSearchParams({
To: "%YOUR_PHONE_NUMBER%",
From: "%YOUR_TWILIO_NUMBER%",
Body: message,
});
const token = btoa(`${accountSid}:${authToken}`);
const request = {
body: encoded,
method: "POST",
headers: {
Authorization: `Basic ${token}`,
"Content-Type": "application/x-www-form-urlencoded",
},
};
const response = await fetch(endpoint, request);
const result = await response.json();
return Response.json(result);
}これを動かすには、ソースコードから ACCOUNT_SID と AUTH_TOKEN を隠す secrets を設定します。コマンドラインで wrangler secret put を使って設定できます。
npx wrangler secret put TWILIO_ACCOUNT_SID
npx wrangler secret put TWILIO_AUTH_TOKEN先ほど作った sendText 関数でテキストメッセージを送るよう、githubWebhookHandler を変更します。
async fetch(request, env, ctx) {
if(request.method !== 'POST') {
return new Response('Please send a POST request!');
}
try {
const rawBody = await request.text();
if (!checkSignature(rawBody, request.headers, env.GITHUB_SECRET_TOKEN)) {
return new Response('Wrong password, try again', {status: 403});
}
const action = request.headers.get('X-GitHub-Event');
const json = JSON.parse(rawBody);
const repoName = json.repository.full_name;
const senderName = json.sender.login;
return await sendText(
env.TWILIO_ACCOUNT_SID,
env.TWILIO_AUTH_TOKEN,
`${senderName} completed ${action} onto your repo ${repoName}`
);
} catch (e) {
return new Response(`Error: ${e}`);
}
};npx wrangler deploy コマンドを実行し、Worker プロジェクトを再デプロイします。
npx wrangler deploy
これで、GitHub の Webhook 設定で選んだ更新をリポジトリに行うと、まもなくテキストが届きます。Git を使ったことがない場合は、リポジトリへのプッシュについて GIT Push and Pull Tutorial ↗ を参照してください。
完成したコードは GitHub ↗ で確認できます。
このチュートリアルを完了すると、Workers で Webhook を構築する方法、Workers を GitHub と Twilio に連携する方法、Wrangler で Worker の secrets を使う方法を学べます。