feat(prayer-times): add Jakarta prayer time plugins and enhance ntfy integration
- Introduced two new plugins: `jakarta-prayer-time-parser-for-ntfy` for formatting prayer times as ntfy Markdown, and `jakarta-prayer-time-today-and-tomorrow` for fetching prayer times from Kemenag for today and tomorrow. - Enhanced `ntfy.js` to support improved Markdown header handling and added a utility function for Markdown configuration validation. - Updated plugin metadata and descriptions for clarity on functionality and usage. - Implemented robust error handling and logging for better debugging and user feedback.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user