過去幾週我一直在做一個副業專案,現在終於把它完善到可以和大家分享的程度了。

Hacker News 是我最喜歡的科技資訊平台之一,我常在這裡看到各種精彩的討論。但是上面內容太多了,我經常錯過自己真正感興趣的貼文。

即使每天查看兩次,首頁內容也很少符合我的興趣。我試過幾個 Hacker News 的新聞郵件訂閱,但它們都太千篇一律了——每個人都能看到同樣的熱門新聞。

所以我想,為什麼不讓自己和其他人能夠根據即時資料建立自己的電子郵件摘要,並隨時安排發送時間呢?這就是我建立HN Digest 的原因。

你可以透過聊天建立自己的個人化 Hacker News 新聞簡報,使用真實的 HN 資料進行即時預覽,並將其安排到你的收件匣——每日、每週或每月,70 多個時區,無需密碼,全部免費。

這是GitHub 儲存庫,您可以在hndigest.vercel.app上進行即時試用。

https://youtu.be/HYAsE9ADKfg

以下是它的功能、我的建置方法以及架構。


它的作用

你只需在聊天編輯器中描述你想描述的內容:安全新聞、向 HN 提問、熱門貼文、討論、展示 HN——它就會將它們加入到你的新聞簡報中。

hndigest

共有 10 種版塊類型,每個版塊都有關鍵字、時間範圍(24 小時至 30 天)、最低按讚數和文章數量等篩選條件。

所以你可以說“過去 48 小時的安全新聞以及最近的 Show HN”,它會自動選擇合適的版塊並應用篩選條件。預覽是即時渲染的 React 郵件,所以你看到的就是實際的郵件內容。

迅速的

你也可以直接在聊天視窗提問:

  • 顯示所有可用部分

  • 刪除或重新排序

  • 更改新聞簡報的主題(淺色/深色)。

點擊文章標題即可跳到原文網站。點擊評論即可跳到HN相關文章頁面。

滿意後,只需使用魔法連結啟動即可。您需要一個 Resend API 金鑰——他們的免費套餐每月發送 3000 封郵件,即使您每天都發送,也大約會用到 30 封。

發送操作會使用您的重發金鑰(以及您的配額,而不是我的)。您可以隨時在控制台中暫停或刪除發送。

是的,它每天都會發送到我的郵箱,但我經常忘記查看。我一定要把查看時間改成晚上! 🤣

儀表板

我在自述文件文件中也包含了自託管指南

以下是堆疊資訊:

有趣的是:我之前嘗試過不同的方法來解決這個問題(網路爬蟲、GitHub Actions),我認為這是所有方法中最實用的解決方案。

解決 hndigest 的不同方法


工作原理

以下是整體流程。

Browser (editor + chat + live preview)
 ├→ /api/copilotkit  → LLM returns tool calls
 ├→ /api/preview     → React Email render → iframe
 └→ /api/activate    → encrypt key → Neon → create QStash schedule
                                           ↓ (HTTP cron callback)
                                    /api/send → Neon (fetch schedule)
                                             → User's Resend (send)

聊天記錄不會直接寫入資料庫。 LLM 的工具呼叫只會更新瀏覽器狀態。在您點擊「啟動」按鈕之前,任何資料都不會持久化到 Neon。只有點擊「啟動」按鈕才會寫入資料庫並建立 QStash 調度。

只有 QStash 會觸發/api/send請求。此端點會在每個請求中驗證CRON_SECRET標頭,魔術連結和會話均使用 JWT 簽名,重發金鑰在靜態儲存時始終保持 AES-256-GCM 加密。瀏覽器無法存取任何敏感資訊。

如果您想自行探索,我已經在程式碼庫中新增了專門的架構文件

編輯器和即時預覽

編輯器中的每一次更改都是一個類型化的函數呼叫,而不是一團 JSON 資料。 CopilotKit 的useFrontendTool鉤子接受一個 zod schema 和一個處理程序-LLM 選擇工具,填入參數,處理程序則強制執行允許的操作。

useFrontendTool({
  name: "add_section",
  parameters: z.object({
    type: z.enum(["hn-stories", "topic", "recent-gems", "high-signal", ...]),
    count: z.number().min(1).max(30).optional(),
    query: z.string().optional(),
    hours: z.number().optional(),
    minPoints: z.number().optional(),
  }),
  handler: async ({ type, count, query, hours, minPoints }) => {
    // Block duplicates for singleton types, strip invalid params, etc.
    const section = {
      id: `${type}-${Date.now()}`,
      type,
      props: { count, query, hours, minPoints },
    };
    setConfig(prev => ({ ...prev, sections: [...prev.sections, section] }));
    return `Added ${type} section`;
  },
});

LLM 從不產生 HTML 或輸出需要我驗證的 JSON。安全機制存在於處理程序中-不會出現重複的單例部分,標準部分會自動移除不支援的hours / minPoints

每次配置變更都會呼叫/api/preview ,該 API 會在伺服器端渲染 React Email 範本並傳回 HTML。編輯器會根據配置雜湊值進行緩存,因此重複編輯不會重新取得資料。

const cacheKey = `${PREVIEW_CACHE_VERSION}:${JSON.stringify(config)}`;
const cached = previewCacheRef.current.get(cacheKey);
if (cached) { setPreviewHtml(cached); return; }

const { html } = await (await fetch("/api/preview", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ config }),
})).json();

previewCacheRef.current.set(cacheKey, html);
setPreviewHtml(html);

啟動和密鑰加密

您輸入重發 API 金鑰,新增收件人,然後點選啟動。密鑰在進入資料庫之前會使用 AES-256-GCM 加密。它僅在發送時在記憶體中解密,絕不會記錄在日誌中,也不會返回給瀏覽器。

export function encrypt(text: string): string {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv("aes-256-gcm", getKey(), iv);
  let enc = cipher.update(text, "utf8", "hex") + cipher.final("hex");
  return `${iv.toString("hex")}:${cipher.getAuthTag().toString("hex")}:${enc}`;
}

您的重發金鑰會發送一個魔法連結(已簽署 JWT,30 分鐘後過期)。點擊該連結即可進入您的控制面板。

使用 QStash 進行按用戶調度

我最初嘗試使用 Vercel Hobby 的 cron 任務,但它每天只為整個專案執行一次,這對於每個用戶都希望設定自己的時間和時區來說並不適用。 QStash 允許每次啟動都建立自己的計劃,所以我最終選擇了它。

const cron = toCron(frequency, time, day);  // "0 9 * * 1" for Mon 9am
await client.schedules.create({
  destination: `${baseUrl}/api/send?id=${scheduleId}`,
  cron,
  headers: { "x-cron-secret": process.env.CRON_SECRET },
});

時區轉換在客戶端進行-選擇Asia/Tokyo 8am ,編輯器會將其轉換為23:00 UTC ,然後儲存並安排該時間。

QStash

發送端點

定時任務運作順序為:解密 → 取得 HN → 渲染 → 傳送。受共用頭部金鑰保護,只有 QStash 可以存取。

// /api/send
if (req.headers.get("x-cron-secret") !== process.env.CRON_SECRET) return 401;

const record = await getScheduleById(id);
const data = await fetchNewsletterData(record);
const html = await render(<NewsletterEmail config={record} data={data} ... />);

const resend = new Resend(decrypt(record.encryptedResendKey));
await resend.emails.send({
  from: "HN Digest <[email protected]>",
  to: record.recipients,
  subject,
  html,
});

每次發送都會獲得最新的 Hacker News 資料。無需預渲染,絕無過時內容。發送操作使用您的重發金鑰(以及您的配額,而非我的),只需單擊即可取消訂閱。您也可以在控制面板中暫停發送。

完整流程

這種模式本身就很容易移植。只需將 Hacker News API 替換為 Product Hunt、GitHub Trending、Reddit 或任何 RSS 來源,即可取得適用於該資料來源的個人化摘要產生器。加密方式相同,聊天編輯器相同,使用者自訂發布計畫也相同。


搭建這個東西很有趣。我和幾個朋友過去一個月一直在使用它。

非常感謝您的誠實回饋 :)

點這裡試試: hndigest.vercel.app

GitHub: github.com/Anmol-Baranwal/hndigest

請在GitHubTwitterLinkedIn上與我聯絡。

感謝閱讀!


原文出處:https://dev.to/anmolbaranwal/chat-to-build-and-schedule-your-own-personal-hacker-news-email-digest-42a4


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬4   ❤️1
464
🥈
alicec
📝1   ❤️2
87
#4
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登