const PRAYERS = [ ["imsak", "Imsak"], ["subuh", "Subuh"], ["terbit", "Terbit"], ["dhuha", "Dhuha"], ["dzuhur", "Dzuhur"], ["ashar", "Ashar"], ["maghrib", "Maghrib"], ["isya", "Isya"], ]; function passContext(ctx) { if (ctx?.context != null && typeof ctx.context === "object" && !Array.isArray(ctx.context)) { return { ...ctx.context }; } return {}; } function unwrapScalar(value) { if (value == null) return ""; if (typeof value === "string") return value.trim(); if (typeof value === "number" || typeof value === "boolean") return String(value); return ""; } function requireDay(day, label) { if (day == null || typeof day !== "object" || Array.isArray(day)) { throw new Error( `parser: missing ${label} prayer-time data (run plugin/jakarta-prayer-time-today-and-tomorrow first)`, ); } return day; } function dayCaption(day, fallbackDate) { const tanggal = unwrapScalar(day.tanggal); if (tanggal) return tanggal; return unwrapScalar(fallbackDate); } function formatDayTable(heading, day) { const lines = [ `### ${heading}`, "", "| Prayer | Time |", "| --- | --- |", ]; for (const [key, label] of PRAYERS) { const time = unwrapScalar(day[key]) || "—"; lines.push(`| ${label} | ${time} |`); } return lines.join("\n"); } async function parsePrayerTimesForNtfy(ctx) { const data = ctx?.data && typeof ctx.data === "object" ? ctx.data : {}; const today = requireDay(data.today, "today"); const tomorrow = requireDay(data.tomorrow, "tomorrow"); const location = unwrapScalar(ctx.config?.location) || "Jakarta"; const todayCaption = dayCaption(today, data.todayDate); const tomorrowCaption = dayCaption(tomorrow, data.tomorrowDate); const todayHeading = todayCaption ? `Today — ${todayCaption}` : "Today"; const tomorrowHeading = tomorrowCaption ? `Tomorrow — ${tomorrowCaption}` : "Tomorrow"; const message = [ formatDayTable(todayHeading, today), "", "---", "", formatDayTable(tomorrowHeading, tomorrow), ].join("\n"); const title = todayCaption ? `Prayer Times (${location}): ${todayCaption}` : `Prayer Times (${location})`; log.info( { location, todayCaption, tomorrowCaption }, "jakarta-prayer-time-parser-for-ntfy: formatted", ); const output = { ok: true, title, message }; return { output, context: { ...passContext(ctx), ...output } }; } parsePrayerTimesForNtfy.meta = { description: "Format today/tomorrow prayer times as ntfy Markdown (headings + tables). Set ntfy.js config.markdown to true.", previewConfigKey: "location", tags: ["ntfy", "Markdown", "Prayer Times"], config: { location: { type: "string", default: "Jakarta", description: "City name used in the ntfy title", }, }, input: { today: { type: "any", required: true, description: "Today's prayer-time record from the fetcher", }, tomorrow: { type: "any", required: true, description: "Tomorrow's prayer-time record from the fetcher", }, todayDate: { type: "string", required: false, description: "YYYY-MM-DD fallback when today.tanggal is missing", }, tomorrowDate: { type: "string", required: false, description: "YYYY-MM-DD fallback when tomorrow.tanggal is missing", }, }, output: { ok: { type: "boolean" }, title: { type: "string", description: "ntfy Title header" }, message: { type: "string", description: "Markdown body (headings + tables)" }, }, context: { ok: { type: "boolean" }, title: { type: "string" }, message: { type: "string" }, }, example: { data: { todayDate: "2026-08-30", tomorrowDate: "2026-08-31", today: { tanggal: "Minggu, 30/08/2026", imsak: "04:32", subuh: "04:42", terbit: "05:57", dhuha: "06:26", dzuhur: "11:58", ashar: "15:18", maghrib: "17:58", isya: "19:08", }, tomorrow: { tanggal: "Senin, 31/08/2026", imsak: "04:32", subuh: "04:42", terbit: "05:57", dhuha: "06:26", dzuhur: "11:58", ashar: "15:17", maghrib: "17:58", isya: "19:08", }, }, config: { location: "Jakarta" }, }, }; export default parsePrayerTimesForNtfy;