feat: add cron and manual trigger

This commit is contained in:
2026-07-24 11:29:24 +05:30
parent 7207fcfab3
commit cc7ec85a5d
4 changed files with 169 additions and 128 deletions

160
index.ts
View File

@@ -1,6 +1,9 @@
import { $ } from "bun"
import cron from "node-cron"
import dayjs from "dayjs"
import "dayjs/locale/es"
import { getUSD, getUsage, getWeather } from "./fx"
import { EventSource } from "eventsource"
const TOPIC = "daily-digest"
const TOKEN = process.env.NTFY_TOKEN
@@ -10,130 +13,6 @@ if (!TOKEN) {
process.exit(1)
}
const API =
"https://api.frankfurter.app/latest?from=USD&to=INR"
async function getUSD() {
const res = await fetch(API)
if (!res.ok) {
throw new Error(`Exchange API failed: ${res.status}`)
}
const data = await res.json()
const rate = data.rates.INR
if (typeof rate !== "number") {
throw new Error("Invalid exchange rate")
}
const helions = (rate / 100).toFixed(2)
return `**📈 Exchange Rate**
$1 = ₹${rate.toFixed(2)} = ⚙${helions}
`
}
async function getUsage() {
//
// CPU LOAD
//
const loadavg = await $`cat /proc/loadavg`.text()
const [load1] = loadavg.trim().split(" ")
//
// MEMORY
//
const memRaw = await $`free -b`.text()
const memLine = memRaw
.split("\n")
.find((line) => line.startsWith("Mem:"))
if (!memLine) {
throw new Error("Failed to parse memory info")
}
const [
,
totalMem,
usedMem,
] = memLine.trim().split(/\s+/)
//
// DISK
//
const diskRaw = await $`df -B1 /`.text()
const diskLine = diskRaw
.split("\n")[1]
if (!diskLine) {
throw new Error("Failed to parse disk info")
}
const [
,
totalDisk,
usedDisk,
] = diskLine.trim().split(/\s+/)
const temp = await $`vcgencmd measure_temp`.text()
const tempData = temp.split("=")[1].replace("'", "°")
//
// HELPERS
//
function formatBytesIEC(bytes: number) {
const units = ["B", "KiB", "MiB", "GiB", "TiB"]
let i = 0
let value = bytes
while (value >= 1024 && i < units.length - 1) {
value /= 1024
i++
}
return `${value.toFixed(1)} ${units[i]}`
}
//
// OUTPUT
//
return `**💾 Usage**
• **CPU:** ${load1}
• **Memory:** ${formatBytesIEC(Number(usedMem))} / ${formatBytesIEC(Number(totalMem))}
• **Disk:** ${formatBytesIEC(Number(usedDisk))} / ${formatBytesIEC(Number(totalDisk))}
• **Temperature:** ${tempData}
`
}
async function getWeather() {
const res = await fetch(`https://wttr.in/Bengaluru?format="%c%C| %f | %h | %m | %w | %p"`)
const data = await res.text()
const [weather, temp, humidity, moon, winds, preci] = data.replace(/\"/g, '').split("|")
return `**🌥️ Weather Report**
• **Weather:** ${weather}
• **Temperature:** ${temp}
• **Humidity:** ${humidity}
• **Moon Phase:** ${moon}
• **Winds:** ${winds}
• **Precipitation:** ${preci}
`
console.log(data)
}
async function main() {
@@ -175,7 +54,34 @@ ${usage}
console.log("Notification sent at " + dayjs().format("YYYY-MM-dd HH:mm:ss"))
}
main().catch((err) => {
console.error(err)
process.exit(1)
cron.schedule("0 7 * * *", () => {
main().catch((err) => {
console.error(err)
})
})
const es = new EventSource("https://ntfy.hy-pr.link/daily-digest/sse", {
fetch: (input, init) =>
fetch(input, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${TOKEN}`,
},
}),
});
es.onmessage = (e) => {
const msg = JSON.parse(e.data).message as string
if (msg === "/digest") {
main().catch((err) => {
console.error(err)
})
}
};
es.onerror = (e) => {
console.error(e);
};