「あれ、新しいPixelの発売日っていつだっけ?」 「このテックカンファレンス、申し込み忘れてた……」
ガジェット好きなら一度はあるこの悩み。情報はTwitter(X)、PR Times、ニュースサイトとあちこちに散らばっていて、追うだけで一苦労です。
自分で全部チェックするのは無理。なら、AIにやらせればいいじゃない。
というわけで、 Web上のイベント情報を勝手に集めてきて、綺麗に分類して、ついでに自分のブログ記事も合わせて表示してくれる「最強のガジェットカレンダー」 を作りました。
今回はその技術的な裏側を、コードを交えてガッツリ解説します。
まずは完成品をご覧ください。サイト内の /calendar ページに実装されています。
今回のこだわりポイントはここ。
このカレンダーを実現するために選んだスタックがこちら。
一番の肝は、「どうやってWebの無秩序なテキストから、正確にイベント情報を抜くか」です。 ここで Vercel AI SDK が火を噴きます。
以下は、実際に使っているスクリプト(scripts/collect-events.ts)の一部です。
// スクレイピングした生のテキストをAIに投げ、構造化データとして返してもらう
const { text } = await generateText({
model: "openai('gpt-4o')",
prompt: "`Extract upcoming tech/gadget events from the following text.
For each event", provide: "title", date (YYYY-MM-DD), category (e.g., "Release", "Conference", "Sale"), description, and sourceUrl.
Return as a JSON array of objects.
Text: "${rawContent"}`,
});
従来の正規表現やCSSセレクタだと、サイトの構造が変わるたびにコード修正が必要でした。しかしLLMなら、「テキストの意味」を理解して日付やタイトルを抽出してくれるので、保守コストが劇的に下がります。
抽出したデータは Astro DB に保存します。スキーマ定義(db/config.ts)は非常に直感的です。
export const Events = defineTable({
columns: {
id: column.text({ primaryKey: true }),
title: column.text(),
date: column.date(),
category: column.text(),
sourceUrl: column.text({ optional: true }),
// ...
},
});
これを await db.insert(Events).values(...) で放り込むだけ。ORMの設定もマイグレーションファイルの管理も不要。Astro最高です。
このカレンダーの真骨頂は 「外部のイベント情報」と「自分のブログ記事」が混ざり合っていること です。
src/pages/calendar/index.astro で、この2つのデータをマージしています。
// 1. スクレイピングしたイベントを取得
const scrapedEvents = await db.select().from(Events);
// 2. ブログ記事を取得
const blogPosts = await getCollection("blog");
// 3. 両者を「Event」型として正規化して結合
const allEvents = [
...scrapedEvents.map((e) => ({ ...e, type: "scraped" })),
...blogPosts.map((p) => ({
title: "p.data.title",
date: "p.data.date",
type: "blog",
})),
];
これをReactコンポーネントの <CalendarContainer /> に渡すことで、シームレスなタイムラインを実現しています。
AI エージェントだけでなく、既存の iCal カレンダー(Google Calendar 等)との統合も行っています。
// iCalパースの簡易実装例
import ical from 'node-ical';
async function fetchExternalEvents(url: string) {
const events = await ical.async.fromURL(url);
return Object.values(events)
.filter(e => e.type === 'VEVENT')
.map(e => ({
title: e.summary,
start: e.start,
description: e.description
}));
}
「AI による自動抽出」と「既存プロトコル(iCal)による確実な取得」を組み合わせるのが、最強のカレンダーを作るコツです。
ただ情報を発信するだけの静的なブログは、もう古いのかもしれません。
バックグラウンドでエージェントが動き回り、ユーザーのために情報を集め、整理して提示する。 Astro DB と AI SDK を組み合わせることで、そんな「生きたWebサイト」が個人でも簡単に作れるようになりました。
次は、気になるイベントを登録しておくと、前日にDiscordに通知してくれる機能を実装予定です。お楽しみに!
このカレンダー機能は、右上のメニュー(または /calendar)からいつでもアクセスできます。ぜひ、あなたのガジェットライフのリズム作りに役立ててください。