Instead of signing up for a feed-reader-to-email service, you can run the whole thing as one cron-triggered val: fetch the feed daily, diff against the last-run timestamp stored in blob storage, and email yourself the new items with std/email. If nothing new was published, no email is sent.
The digest cron
Section titled “The digest cron”View and run this example on Val Town
import { blob } from "https://esm.town/v/std/blob/main.ts";import { renderDigest } from "./digest.tsx";import { sendWithRetry } from "./email.ts";import { fetchNewItems } from "./rss.ts";
// Change this to the feed you want a daily digest ofconst FEED_URL = "https://blog.val.town/rss.xml";
const LAST_RUN_KEY = "lastRunAt";
export default async function () { const lastRunAt = (await blob.getJSON(LAST_RUN_KEY)) as string | undefined; // First run: look back 24 hours const since = lastRunAt ? new Date(lastRunAt) : new Date(Date.now() - 24 * 60 * 60 * 1000);
const { feedTitle, items } = await fetchNewItems(FEED_URL, since); console.log(`${items.length} new item(s) since ${since.toISOString()}`);
if (items.length > 0) { await sendWithRetry({ subject: `${feedTitle}: ${items.length} new item${items.length === 1 ? "" : "s"}`, html: renderDigest(feedTitle, items), }); }
await blob.setJSON(LAST_RUN_KEY, new Date().toISOString());}The template splits the rest of the work into small files: rss.ts fetches
and parses the feed (any RSS or Atom feed works), digest.tsx renders the
email HTML, and email.ts wraps std/email with a small retry since sends
can fail transiently.
Set it up
Section titled “Set it up”- Remix this template — click the Remix button in the top right corner.
- In
main.tsx, setFEED_URLto the feed you want. - Optional: click Run on
main.tsxto test it immediately. On the first run it looks back 24 hours.
No API keys or environment variables are needed.
- The digest goes to your Val Town account email — that's where std/email delivers by default.
- The schedule is
0 13 * * *— daily at 13:00 UTC (9am Eastern). Cron expressions always run in UTC, so adjust the schedule onmain.tsxfor your timezone. - The last-run timestamp in blob storage is what guarantees you never get the same item twice, even if you change the schedule.
Next steps
Section titled “Next steps”- Cron reference — schedule types and the
Intervalhandler - RSS guide — parsing and generating feeds on Val Town
- Scrape a website daily and email yourself the results — the same cron-plus-email recipe for pages without a feed