33 <meta name="viewport" content="width=device-width, initial-scale=1.0">
34 <title>Bubbly Hello World Val</title>
35 <link href="https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap" rel="stylesheet">
36 <style>${css}</style>
37 </head>
31
32- [Steel Documentation](https://docs.steel.dev)
33- [API Reference](https://docs.steel.dev/api-reference)
34- [Discord Community](https://discord.gg/gPpvhNvc5R)
8Today, weβre unifying our two primitives β *vals* and *projects* β into a single primitive: the **val**, with the best features of both.
9
10Historically, vals have been simple and lightweight, but limited to a single file. This upgrade will preserve the elegant spirit of our platform while supporting more complex code and collaborative workflows. This upgrade will help you create bigger things on Val Town β APIs, internal tools, fullstack apps, blogs ([like this one](https://www.val.town/x/valdottown/blog)), and much more β without sacrificing the simplicity you've always loved about vals.
11
12*Legacy vals* will temporarily become *projects* during this migration. Post-migration, the concept of *projects* will disappear entirely β leaving only upgraded *vals*. In short: *legacy vals* β *projects* β *vals*.
16For most users, no upgrade action is required. Weβll auto-migrate your vals next week. All existing HTTP endpoints, crons, email handlers, and custom domains will be preserved.
17
18For those with mission-critical vals or who use our API to edit or create vals, you can start upgrading your legacy vals today and integrating with our updated API.
19
20- **April 23, 2025** β Announcement of changes & API deprecations.
21- **April 30, 2025** β All remaining *legacy vals* auto-upgraded. Deprecated API routes become read-only.
22- **May 1, 2025** β The term *projects* will no longer exist β everything will simply be a *val*.
23
35[See more details in our docs.](https://docs.val.town/upgrading/legacy-vals)
36
37## API Changes
38
39Today, we're introducing the following API routes:
40
41```bash
73```
74
75View our [updated API reference here](https://docs.val.town/openapi).
76
77All `v1/vals` API routes become read-only on **April 30, 2025**. If you rely on *writing* to those routes, please upgrade to our new `v2/vals` API. All deprecated API routes will continue serving historical legacy val data.
78
79### SDK changes
85
861. Upgrade your mission-critical vals early.
872. Update your Val Town API & SDK usage to `/v2` routes.
883. All remaining legacy vals migrate on **April 30, 2025**.
894. Migration completes on **May 1, 2025**.
12 type="image/svg+xml"
13 />
14 <link rel="preconnect" href="https://fonts.googleapis.com" />
15 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16 <link
17 href="https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400..700&display=swap"
18 rel="stylesheet"
19 />
45 // --- Constants ---
46 const SUN_CUTOUT_URL =
47 "https://firebasestorage.googleapis.com/v0/b/charlies-projects-adbca.firebasestorage.app/o/suncutout.png?alt=media&token=e9e1f8e3-5f5b-4118-8bd5-0367dd21e383";
48 const TELE_CLIP_RADIUS = 28.5; // Fixed radius for the teletubby circle (%)
49
54 // (Camera setup logic remains the same)
55 if (typeof navigator === "undefined" || !navigator.mediaDevices) {
56 setError("MediaDevices API not available");
57 return;
58 }
97
98export async function sendDailyBriefing(chatId?: string, today?: DateTime) {
99 // Get API keys from environment
100 const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
101 const telegramToken = Deno.env.get("TELEGRAM_TOKEN");
102
106 }
107
108 if (!apiKey) {
109 console.error("Anthropic API key is not configured.");
110 return;
111 }
122
123 // Initialize Anthropic client
124 const anthropic = new Anthropic({ apiKey });
125
126 // Initialize Telegram bot
162
163 // disabled title for now, it seemes unnecessary...
164 // await bot.api.sendMessage(chatId, `*${title}*`, { parse_mode: "Markdown" });
165
166 // Then send the main content
169
170 if (content.length <= MAX_LENGTH) {
171 await bot.api.sendMessage(chatId, content, { parse_mode: "Markdown" });
172 // Store the briefing in chat history
173 await storeChatMessage(
198 // Send each chunk as a separate message and store in chat history
199 for (const chunk of chunks) {
200 await bot.api.sendMessage(chatId, chunk, { parse_mode: "Markdown" });
201 // Store each chunk in chat history
202 await storeChatMessage(
53You'll need to set up some environment variables to make it run.
54
55- `ANTHROPIC_API_KEY` for LLM calls
56- You'll need to follow [these instructions](https://docs.val.town/integrations/telegram/) to make a telegram bot, and set `TELEGRAM_TOKEN`. You'll also need to get a `TELEGRAM_CHAT_ID` in order to have the bot remember chat contents.
57- For the Google Calendar integration you'll need `GOOGLE_CALENDAR_ACCOUNT_ID` and `GOOGLE_CALENDAR_CALENDAR_ID`. See [these instuctions](https://www.val.town/v/stevekrouse/pipedream) for details.
8## Hono
9
10This app uses [Hono](https://hono.dev/) as the API framework. You can think of Hono as a replacement for [ExpressJS](https://expressjs.com/) that works in serverless environments like Val Town or Cloudflare Workers. If you come from Python or Ruby, Hono is also a lot like [Flask](https://github.com/pallets/flask) or [Sinatra](https://github.com/sinatra/sinatra), respectively.
11
12## Serving assets to the frontend
20### `index.html`
21
22The most complicated part of this backend API is serving index.html. In this app (like most apps) we serve it at the root, ie `GET /`.
23
24We *bootstrap* `index.html` with some initial data from the server, so that it gets dynamically injected JSON data without having to make another round-trip request to the server to get that data on the frontend. This is a common pattern for client-side rendered apps.
25
26## CRUD API Routes
27
28This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
29
30## Errors
31
32Hono and other API frameworks have a habit of swallowing up Errors. We turn off this default behavior by re-throwing errors, because we think most of the time you'll want to see the full stack trace instead of merely "Internal Server Error". You can customize how you want errors to appear.
8import { type Memory } from "../../shared/types.ts";
9
10const API_BASE = "/api/memories";
11const MEMORIES_PER_PAGE = 20;
12
71 setError(null);
72 try {
73 const response = await fetch(API_BASE);
74 if (!response.ok) {
75 throw new Error(`HTTP error! status: ${response.status}`);
100
101 try {
102 const response = await fetch(API_BASE, {
103 method: "POST",
104 headers: { "Content-Type": "application/json" },
123
124 try {
125 const response = await fetch(`${API_BASE}/${id}`, {
126 method: "DELETE",
127 });
155
156 try {
157 const response = await fetch(`${API_BASE}/${editingMemory.id}`, {
158 method: "PUT",
159 headers: { "Content-Type": "application/json" },
18});
19
20// --- API Routes for Memories ---
21
22// GET /api/memories - Retrieve all memories
23app.get("/api/memories", async (c) => {
24 const memories = await getAllMemories();
25 return c.json(memories);
26});
27
28// POST /api/memories - Create a new memory
29app.post("/api/memories", async (c) => {
30 const body = await c.req.json<Omit<Memory, "id">>();
31 if (!body.text) {
36});
37
38// PUT /api/memories/:id - Update an existing memory
39app.put("/api/memories/:id", async (c) => {
40 const id = c.req.param("id");
41 const body = await c.req.json<Partial<Omit<Memory, "id">>>();
58});
59
60// DELETE /api/memories/:id - Delete a memory
61app.delete("/api/memories/:id", async (c) => {
62 const id = c.req.param("id");
63 try {
75// --- Blob Image Serving Routes ---
76
77// GET /api/images/:filename - Serve images from blob storage
78app.get("/api/images/:filename", async (c) => {
79 const filename = c.req.param("filename");
80