Připomínka standupu, kterou služba každý všední den pošle do Slack webhooku.
Cron služba, která každé všední ráno pošle připomínku standupu do Slack Incoming Webhooku. V naplánovaném čase odešle krátkou zprávu se třemi standupovými otázkami; přes běžné HTTP zobrazí stavovou stránku, na které si ověříte, že je služba nastavená, aniž by kdy prozradila adresu webhooku.
Adresa webhooku je uložená jako secret SLACK_WEBHOOK_URL, který se do prostředí služby vloží až za běhu a nikdy se nezapisuje do kódu. Když Slack zprávu nepřijme, služba výsledek zaznamená a přesto v pořádku doběhne, takže špatný webhook nikdy neoznačí naplánovaný běh jako selhaný.
// A weekday standup reminder for Slack. // // On its cron schedule this service posts a reminder to a Slack Incoming Webhook. // Over plain HTTP it serves a small status page so you can confirm it is wired // up, without ever revealing the webhook URL. // // Configure the SLACK_WEBHOOK_URL secret with your Incoming Webhook: // https://api.slack.com/messaging/webhooks // Read an environment variable, treating an unset or unreadable one as // undefined. Frontback scopes each service's env access to its declared secrets, so // a name that was never configured simply reads as "not set". function readEnv(name: string): string | undefined { try { return Deno.env.get(name); } catch { return undefined; } } // The three classic standup prompts, sent as one reminder message. const QUESTIONS = [ "What did you get done yesterday?", "What are you working on today?", "Anything blocking you?", ]; // Build the Slack message payload. Slack renders the `text` field, so we keep // the reminder to a single readable block with Slack's markdown. function buildMessage(): { text: string } { const lines = ["*Standup time* :coffee:", "", ...QUESTIONS.map((q) => "• " + q)]; return { text: lines.join("\n") }; } // Post the reminder to Slack. A Slack Incoming Webhook answers 200 with the // body "ok" when it accepts the message; anything else means it was not // delivered. This never throws, so a misconfigured webhook cannot fail the run. async function postToSlack(webhookUrl: string): Promise<{ delivered: boolean; status: number }> { const res = await fetch(webhookUrl, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(buildMessage()), }); const body = (await res.text()).trim(); return { delivered: res.ok && body === "ok", status: res.status }; } // The schedule is defined in the code; each firing calls the handler below with // the "X-Fb-Trigger: cron" header. run() powers the Run button for a test post. export const crons = { "standup": "45 8 * * 1-5" }; export async function run(): Promise<string> { const webhookUrl = readEnv("SLACK_WEBHOOK_URL"); if (!webhookUrl) return "SLACK_WEBHOOK_URL is not set"; const result = await postToSlack(webhookUrl); return result.delivered ? "reminder posted to Slack" : `Slack answered ${result.status}`; } export default async (req: Request): Promise<Response> => { const webhookUrl = readEnv("SLACK_WEBHOOK_URL"); // Cron trigger: the platform posts to the service with this header on schedule. if (req.headers.get("x-fb-trigger") === "cron") { if (!webhookUrl) { // Report the misconfiguration but answer 200. A 5xx would mark the run as // failed in the dashboard; a missing secret is an expected state. return Response.json({ ok: false, delivered: false, reason: "SLACK_WEBHOOK_URL is not set" }); } const result = await postToSlack(webhookUrl); return Response.json({ ok: true, delivered: result.delivered, slackStatus: result.status, note: result.delivered ? "reminder posted to Slack" : "Slack did not accept the message", }); } // Plain HTTP: a status page. It shows whether the webhook is configured, but // never the URL itself, so the page is safe to share. const configured = Boolean(webhookUrl); const preview = QUESTIONS.map((q) => "<li>" + q + "</li>").join(""); const html = `<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Slack standup</title> <style> body { font: 16px/1.5 system-ui, sans-serif; max-width: 40rem; margin: 3rem auto; padding: 0 1rem; } code { background: #f4f4f5; padding: 0.1rem 0.3rem; border-radius: 3px; } </style> </head> <body> <h1>Slack standup</h1> <p>Webhook: <strong>${configured ? "configured" : "not configured"}</strong></p> ${ configured ? "<p>A standup reminder is posted to Slack every weekday at 08:45.</p>" : "<p>Set the <code>SLACK_WEBHOOK_URL</code> secret to enable the reminder.</p>" } <h2>Reminder preview</h2> <ul>${preview}</ul> </body> </html>`; return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } }); };
Go beyond what seems possible.