Implement built-in cron worker

This commit is contained in:
2025-06-04 16:49:00 +02:00
parent 9bc036113f
commit 46f09f2e13
9 changed files with 57 additions and 58 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

5
.gitignore vendored
View File

@@ -145,4 +145,7 @@ dist
# End of https://www.toptal.com/developers/gitignore/api/node
src/generated
src/generated
*.yaml
*.json
.direnv

View File

@@ -28,13 +28,6 @@ in
default = "root";
};
scanTimer = mkOption {
type = types.str;
description = "The systemd's timer interval when the app will be performing the scan for new tasks";
example = "*-*-* *:00";
default = "*-*-* *:10";
};
config = mkOption {
type = types.attrs;
description = "The obsidian-tasks-reminder config which will be eventually converted to yaml";
@@ -56,46 +49,17 @@ in
config = mkIf cfg.enable {
environment.systemPackages = [app];
systemd.timers.obsidian-tasks-reminder-scanner = {
description = "Scan for new Obsidian tasks";
wantedBy = ["timers.target"];
timerConfig = {
OnCalendar = cfg.scanTimer;
Unit = "obsidian-tasks-reminder-scanner.service";
};
};
systemd.timers.obsidian-tasks-reminder = {
description = "Notify about Obsidian tasks";
wantedBy = ["timers.target"];
timerConfig = {
OnCalendar = "*-*-* *:*:00";
Unit = "obsidian-tasks-reminder.service";
};
};
systemd.services.obsidian-tasks-reminder-scanner = {
description = "Scan for new Obsidian tasks";
serviceConfig = {
Type = "oneshot";
User = cfg.user;
};
script = "${app}/bin/obsidian-tasks-reminder -s";
};
systemd.services.obsidian-tasks-reminder = {
description = "Notify about Obsidian tasks";
enable = true;
description = "Obsidian Tasks Notifier";
wantedBy = ["network-online.target"];
after = ["network-online.target"];
serviceConfig = {
Type = "oneshot";
ExecStart = "${app}/bin/obsidian-tasks-reminder";
User = cfg.user;
};
script = "${app}/bin/obsidian-tasks-reminder -n";
};
};
}

29
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"commander": "^13.0.0",
"cron": "^4.3.1",
"dayjs": "^1.11.13",
"peggy": "^4.2.0",
"yaml": "^2.7.0"
@@ -482,6 +483,12 @@
"node": ">=18"
}
},
"node_modules/@types/luxon": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz",
"integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.10.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz",
@@ -576,6 +583,19 @@
"node": ">=18"
}
},
"node_modules/cron": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/cron/-/cron-4.3.1.tgz",
"integrity": "sha512-7x7DoEOxV11t3OPWWMjj1xrL1PGkTV5RV+/54IJTZD7gStiaMploY43EkeBSkDZTLRbUwk+OISbQ0TR133oXyA==",
"license": "MIT",
"dependencies": {
"@types/luxon": "~3.6.0",
"luxon": "~3.6.0"
},
"engines": {
"node": ">=18.x"
}
},
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@@ -793,6 +813,15 @@
"node": ">=0.12.0"
}
},
"node_modules/luxon": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",

View File

@@ -21,6 +21,7 @@
},
"dependencies": {
"commander": "^13.0.0",
"cron": "^4.3.1",
"dayjs": "^1.11.13",
"peggy": "^4.2.0",
"yaml": "^2.7.0"

View File

@@ -7,5 +7,5 @@ buildNpmPackage {
pname = "obsidian-tasks-reminder";
version = "0.0.1";
src = ./.;
npmDepsHash = "sha256-ofPAFnHbW+M0uQN/OXDLK+PQ3ls6AtD58/AOmSxn754=";
npmDepsHash = "sha256-K06A/j2GfM0nCiFv4Pho907VWLa2pPHjtV9BgKxwdnE=";
}

View File

@@ -3,6 +3,7 @@ import { CLIOptions } from "../types/cli";
import { loadConfig } from "../config";
import { notify, scan, test } from "../runner";
import { CronJob } from "cron";
const getOptions = () => program
.name("obsidian-tasks-reminder")
@@ -10,9 +11,7 @@ const getOptions = () => program
.requiredOption("-c, --config <file>", "sets the path to the YAML file with configuration")
.option("-x, --set <arg>", "overrides the config option for this specific run (arg: <key>=<name>, i.e. backend.ntfy.enable=false", (v: string, prev: string[]) => prev.concat([v]), [])
.option("-t, --test", "evaluates the query, applies the mapper and prints to stdout the notifications about to be trigger, without actual triggering them")
.option("-s, --scan", "scans new tasks for future notifications and generates the database")
.option("-n, --notify", "reads the generated database and triggers notifications if any")
.option("-p, --profile <name>", "limits the current operation only to specified profile. If missing, all profiles will be affected")
.option("-p, --profile <name>", "(applicable only with '--test' option) limits the current operation only to specified profile. If missing, all profiles will be affected")
.parse()
.opts<CLIOptions>();
@@ -43,14 +42,16 @@ const getOptions = () => program
await test(config, options.profile);
return;
}
if (options.scan) {
await scan(config, options.profile);
return;
}
if (options.notify) {
await notify(config, options.profile);
return;
}
const scanJob = CronJob.from({
cronTime: config.scanCron ?? '0 0 * * * *',
onTick () { scan(config) },
start: true,
});
const notifyJob = CronJob.from({
cronTime: config.notifyCron ?? '0 * * * * *',
onTick() { notify(config) },
start: true,
});
}

View File

@@ -2,7 +2,5 @@ export type CLIOptions = {
config: string;
profile?: string;
test: boolean;
scan: boolean;
notify: boolean;
set: string[];
};

View File

@@ -1,5 +1,7 @@
export type Config = {
profiles: Record<string, ProfileConfig>;
notifyCron?: string;
scanCron?: string;
};
export type ProfileConfig = {