import { serve } from "bun"; import { Expo } from "expo-server-sdk"; const expo = new Expo(); const events = []; const html = ` Timed Event Scheduler

Schedule Timed Event









`; serve({ port: 7860, fetch: async (req) => { const url = new URL(req.url); if (req.method === "GET" && url.pathname === "/") { return new Response(html, { headers: { "Content-Type": "text/html" } }); } // POST /events - Create Event if (req.method === "POST" && url.pathname === "/events") { try { const body = await req.json(); const { id, delay, message, type, repeat, expoTokens, webhookUrl, payload } = body; if (!id || !delay || !type) { return new Response("Missing 'id', 'delay', or 'type'", { status: 400 }); } if (events.find(e => e.id === id)) { return new Response("Event ID already exists", { status: 409 }); } events.push({ id, timestamp: Date.now() + delay * 1000, type, repeat: type === "interval" && repeat ? repeat * 1000 : undefined, data: { message, expoTokens: Array.isArray(expoTokens) ? expoTokens : [], webhookUrl, payload } }); return new Response("Event scheduled", { status: 200 }); } catch (err) { return new Response("Invalid JSON", { status: 400 }); } } // DELETE /events/:id - Remove Event if (req.method === "DELETE" && url.pathname.startsWith("/events/")) { const id = url.pathname.split("/")[2]; const index = events.findIndex(e => e.id === id); if (index === -1) return new Response("Event not found", { status: 404 }); events.splice(index, 1); return new Response("Event deleted", { status: 200 }); } return new Response("Not Found", { status: 404 }); } }); // Event Loop setInterval(async () => { const now = Date.now(); const due = events.filter(e => e.timestamp <= now); for (const e of due) { console.log(`⏰ Executing event: ${e.id} (${e.type})`); // Expo Notifications if (e.data.expoTokens?.length && e.data.message) { console.log("found a notifications events") const messages = e.data.expoTokens .filter(t => Expo.isExpoPushToken(t)) .map(token => ({ to: token, sound: 'default', title: '⏰ Scheduled Event', body: e.data.message, data: {} })); for (const chunk of expo.chunkPushNotifications(messages)) { try { await expo.sendPushNotificationsAsync(chunk); } catch (err) { console.error("🔥 Expo error:", err); } } } // Webhook Trigger if (e.type === "webhook" && e.data.webhookUrl) { console.log("found a webhook event") try { await fetch(e.data.webhookUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(e.data.payload || {}) }); console.log(`📡 Webhook fired for ${e.id}`); } catch (err) { console.error(`❌ Webhook failed for ${e.id}`, err); } } // Repeat or remove if (e.type === "interval" && e.repeat) { e.timestamp = now + e.repeat; } else { const i = events.findIndex(ev => ev.id === e.id); if (i !== -1) events.splice(i, 1); } } }, 1000);