📺
🔥Hono + Cloudflare Workersで、テレビ番組を探してSlackに通知するbotを作った
Hono + CloudFlare Workersを試したく、1日1度テレビの番組表をスクレイピングして気になるワードに関する番組を探してくれるボットを作ってみました。
takara1356/tvshow-title-watcher: 当日のテレビ番組を特定のキーワードから検索し、Slack ワークフローに通知するアプリ
このような感じで通知してくれます。
あまりにもサクッと実装できて感動したので、簡単な実装の流れをメモがてら残しておきます。
事前準備
SlackワークフローのWebhook設定
Slackのワークフローを新規作成し、任意の文字列(本記事ではdetail
)を受け取れるWebhookのエンドポイントを生やしておきます。
実装手順
Init Hono
- Init
npm create hono@latest tvshow-title-watcher
- テンプレートを聞かれるので、Cloudflare Workersを選択
ロジック実装
- ChatGPTを使って最低限のコードでサクッと実装しました
import { Hono } from "hono";
import * as cheerio from "cheerio";
const app = new Hono();
// 作成したSlackワークフローのWebhook URLを設定する
const SLACK_WORKFLOW_URL = "";
// 検索キーワードを設定する
const KEYWORD = "テクノロジー";
const formatStartTime = (startTime: string): string => {
const year = startTime.slice(0, 4);
const month = startTime.slice(4, 6);
const day = startTime.slice(6, 8);
const hour = startTime.slice(8, 10);
const minute = startTime.slice(10, 12);
return `${year}/${month}/${day} ${hour}:${minute}`;
};
const sendSlackNotification = async (message: string) => {
try {
await fetch(SLACK_WORKFLOW_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ detail: message }),
});
} catch (error) {
console.error("Slackへのリクエスト送信に失敗しました:", error);
}
};
const scrapeTVProgram = async (keyword: string): Promise<string[]> => {
const today = new Date().toISOString().split("T")[0].replace(/-/g, "");
const URL = `https://bangumi.org/epg/td?broad_cast_date=${today}&ggm_group_id=42`;
try {
const response = await fetch(URL);
const html = await response.text();
const $ = cheerio.load(html);
const matchedPrograms: string[] = [];
$("li").each((_, el) => {
const title = $(el).find(".program_title").text();
const detail = $(el).find(".program_detail").text();
const startTimeRaw = $(el).attr("s");
if (startTimeRaw) {
const startTime = formatStartTime(startTimeRaw);
if (title.includes(keyword) || detail.includes(keyword)) {
matchedPrograms.push(
`🕒 開始時間: ${startTime} 🕒\n🎬 タイトル: ${title} 🎬\n📄 内容: ${detail} 📄\n`
);
}
}
});
return matchedPrograms;
} catch (error) {
console.error("番組表のスクレイピングに失敗しました:", error);
return [];
}
};
// サーバーとして起動する場合はこちらを有効化する
// app.fire();
export default {
async scheduled(event: ScheduledEvent) {
const matchedPrograms = await scrapeTVProgram(KEYWORD);
if (matchedPrograms.length > 0) {
const message = `🔥 本日、${KEYWORD}に関連する番組が放送されます! 🔥\n\n${matchedPrograms.join(
"\n"
)}\nお見逃しなく!🚀`;
await sendSlackNotification(message);
} else {
const message = `😞 本日は「${KEYWORD}」に関連する番組が見つかりませんでした。`;
await sendSlackNotification(message);
}
},
};
実装のポイント
Cloudflare Workersにデプロイしたアプリをcronで動かす場合、2つ設定が必要です。
1.scheduledイベントリスナーの定義
コード内でcronトリガーの発火を検知するリスナー用のメソッドを追加します。
export default {
async scheduled(event, env, ctx) {
ctx.waitUntil(doSomeTaskOnASchedule());
},
};
下記のように追記すると、デプロイ時にトリガーの設定が反映されます。
[triggers]
crons = ["0 0 * * *"]
デプロイ
-
npm run deploy
コマンドでデプロイが走ります。 - CLIからブラウザ経由でログインだけしたらすぐにデプロイ可能です。
感想
-
メインで使っている言語がRubyのためJS/TSは詳しくなかったのですが、Hono+Cloudflare functionsの環境構築がとにかく楽!
-
おかげで環境構築やデプロイ周りで時間を浪費せず、メインのロジック実装に集中でき、ChatGPTも使うことでトータル10時間以下で実装できました
-
次回は同じくエッジの環境で動作するCloudflare PagesやD1も試してみたいです🔥
Discussion