12});
13
14async function main() {
15 let session;
16 let browser;
31
32// ------------
33// Functions
34// ------------
35
36async function execute(statement: InStatement, args?: InArgs): Promise<ResultSet> {
37 const res = await fetch(`${API_URL}/v1/sqlite/execute`, {
38 method: "POST",
49}
50
51async function batch(statements: InStatement[], mode?: TransactionMode): Promise<ResultSet[]> {
52 if (mode && !["write", "read", "deferred"].includes(mode)) {
53 throw new Error("Invalid transaction mode provided to sqlite.batch. Must be 'write', 'read', or 'deferred.");
67}
68
69function createResError(body: string) {
70 try {
71 const e = zLibsqlError.parse(JSON.parse(body));
88}
89
90function normalizeStatement(statement: InStatement, args?: InArgs) {
91 if (Array.isArray(statement)) {
92 // for the case of an array of arrays
110}
111
112function upgradeResultSet(results: ImpoverishedResultSet): ResultSet {
113 return {
114 ...results,
119// adapted from
120// https://github.com/tursodatabase/libsql-client-ts/blob/17dd996b840c950dd22b871adfe4ba0eb4a5ead3/packages/libsql-client/src/sqlite3.ts#L314C1-L337C2
121function rowFromSql(
122 sqlRow: Array<unknown>,
123 columns: Array<string>,
15};
16
17async function createSessionTable(sessionTableName: string) {
18 await sqlite.execute(`CREATE TABLE ${sessionTableName} (
19 id TEXT NOT NULL PRIMARY KEY,
24}
25
26async function createCodeTable(tableName: string) {
27 await sqlite.execute(`CREATE TABLE ${tableName} (
28 id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
33}
34
35async function generateEmailVerificationCode(tableName, email: string): Promise<string> {
36 try {
37 await sqlite.execute({ sql: `DELETE FROM ${tableName} WHERE email = ?`, args: [email] });
53}
54
55async function createSession(tableName: string, valSlug: string, email: string): Promise<Session> {
56 try {
57 const expiresAt = new Date();
78}
79
80async function getSession(tableName: string, sessionID: string, valSlug: string): Promise<Session> {
81 try {
82 const { rows, columns } = await sqlite.execute({
105};
106
107async function getEmailVerificationCode(tableName: string, email: string) {
108 try {
109 const { rows, columns } = await sqlite.execute({
169</html>`;
170
171export function redirect(location: string): Response {
172 return new Response(null, {
173 headers: {
188const cookieName = "auth_session";
189
190export function emailAuth(
191 next: Handler,
192 options?: PasswordAuthOptions,
5const { author, name } = extractValInfo(import.meta.url);
6
7export async function forwarder(e: Email) {
8 let attachments: AttachmentData[] = [];
9 for (const f of e.attachments) {
1export default async function (req: Request): Promise<Response> {
2 return Response.json({ ok: true })
3}
4import { DateTime } from "https://esm.sh/luxon@3.4.4";
5
6export async function testDailyBrief() {
7 try {
8 const testChatId = Deno.env.get("TEST_TELEGRAM_CHAT_ID");
2// Run this script manually to create the database table
3
4export default async function setupTelegramChatDb() {
5 try {
6 // Import SQLite module
13} from "../memoryUtils.ts";
14
15async function generateBriefingContent(anthropic, memories, today, isSunday) {
16 try {
17 const weekdaysHelp = generateWeekDays(today);
96}
97
98export async function sendDailyBriefing(chatId?: string, today?: DateTime) {
99 // Get API keys from environment
100 const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
135 const lastSunday = today.startOf("week").minus({ days: 1 });
136
137 // Fetch relevant memories using the utility function
138 const memories = await getRelevantMemories();
139
216}
217
218function generateWeekDays(today) {
219 let output = [];
220
239// console.log(weekDays);
240
241// Export a function that calls sendDailyBriefing with no parameters
242// This maintains backward compatibility with existing cron jobs
243export default async function (overrideToday?: DateTime) {
244 return await sendDailyBriefing(undefined, overrideToday);
245}
16In a normal server environment, you would likely use a middleware [like this one](https://hono.dev/docs/getting-started/nodejs#serve-static-files) to serve static files. Some frameworks or deployment platforms automatically make any content inside a `public/` folder public.
17
18However in Val Town you need to handle this yourself, and it can be suprisingly difficult to read and serve files in a Val Town Project. This template uses helper functions from [stevekrouse/utils/serve-public](https://www.val.town/x/stevekrouse/utils/branch/main/code/serve-public/README.md), which handle reading project files in a way that will work across branches and forks, automatically transpiles typescript to javascript, and assigns content-types based on the file's extension.
19
20### `index.html`
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
4
5* `migrations.ts` - code to set up the database tables the app needs
6* `queries.ts` - functions to run queries against those tables, which are imported and used in the main Hono server in `/backend/index.ts`
7
8## Migrations
18The queries file is where running the migrations happen in this app. It'd also be reasonable for that to happen in index.ts, or as is said above, for that line to be commented out, and only run when actual changes are made to your database schema.
19
20The queries file exports functions to get and write data. It relies on shared types and data imported from the `/shared` directory.