Add support for '--test' CLI option

This commit is contained in:
2025-01-17 17:03:50 +01:00
parent 8490e073f6
commit 3528e65312
8 changed files with 64 additions and 17 deletions

View File

@@ -3,20 +3,21 @@ import { BackendSettings } from "../types/config";
import { Notification } from "../types/notification";
import { Backend } from "./base";
import { TaskPriority } from "../types/task";
import { enhancedStringConfig } from "../util/config";
type Config = {
url: string;
tokenFile: string;
token: string;
topic?: string;
} & BackendSettings;
export class NtfySH extends Backend<Config> {
public name = "ntfy";
protected requiredFields = ['url', 'tokenFile'] as const;
protected requiredFields = ['url', 'token'] as const;
protected notify(config: Config, notification: Notification): void {
const token = fs.readFileSync(config.tokenFile, 'utf-8');
const token = enhancedStringConfig(config.token);
fetch(`https://${config.url}/${config.topic || 'obsidian'}`, {
method: 'POST',

View File

@@ -2,13 +2,14 @@ import { program } from "commander";
import { CLIOptions } from "../types/cli";
import { loadConfig } from "../config";
import { notify, scan } from "../runner";
import { notify, scan, test } from "../runner";
const getOptions = () => program
.name("obsidian-tasks-reminder")
.version("0.0.1")
.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]), [])
.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")
.parse()
@@ -36,12 +37,19 @@ const getOptions = () => program
current = current[segment];
});
}
if (options.test) {
await test(config);
return;
}
if (options.scan) {
scan(config);
await scan(config);
return;
}
if (options.notify) {
notify(config);
await notify(config);
return;
}
}

View File

@@ -1,7 +1,7 @@
import fs from "fs";
import { Task } from "../types/task";
import { Notification, NotificationDatabase } from "../types/notification";
import { jsMapper } from "../util";
import { jsMapper } from "../util/code";
/**
@@ -16,19 +16,24 @@ export function dumpDatabase(file: string, tasks: Task[], mapper?: string) {
* Applies the mapper for each task from list, groups them by time and serializes into JSON format.
*/
export function serializeDatabase(tasks: Task[], mapper?: string): string {
const transformer = mapper
? jsMapper<Task, Notification>(mapper, {})
: defaultMapper;
return JSON.stringify(createDatabase(tasks, mapper));
}
const output = tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => {
/**
* Applies the mapper for each task from list and groups them by time.
*/
export function createDatabase(tasks: Task[], mapper?: string): NotificationDatabase {
const transformer = mapper
? jsMapper<Task, Notification>(mapper, {})
: defaultMapper;
return tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => {
if (n.time) {
(acc[n.time] = (acc[n.time] || [])).push(n);
};
return acc;
}, {} as NotificationDatabase);
return JSON.stringify(output);
}
function wrapWithTimeFiller(mapper: (task: Task) => Notification): (task: Task) => Notification {

View File

@@ -5,7 +5,7 @@ import { parse } from "../generated/grammar/task";
import { Task as DefaultTask } from "../model";
import { ParseResult } from "../types/grammar";
import { Task, TaskPriority } from "../types/task";
import { jsMapper } from "../util";
import { jsMapper } from "../util/code";
/**
* Returns all tasks from specified directory and filters them with optional query.

View File

@@ -1,9 +1,24 @@
import { loadTasks } from "../loader";
import { dumpDatabase } from "../database/serializer";
import { createDatabase, dumpDatabase } from "../database/serializer";
import { loadDatabase } from "../database/deserializer";
import { remind } from "../backend";
import { Config } from "../types/config";
export async function test(config: Config) {
const tasks = await loadTasks(config.sources, config.query);
const db = createDatabase(tasks, config.mapper);
for (const time of Object.keys(db)) {
console.log(time);
for (const notification of db[time]) {
console.log(` - title: ${notification.title}\n text: ${notification.text}\n priority: ${notification.priority}\n tags: ${notification.tags?.join(",")}`)
}
console.log();
}
}
export async function scan(config: Config) {
const tasks = await loadTasks(config.sources, config.query);
dumpDatabase(config.databaseFile, tasks, config.mapper);

View File

@@ -1,5 +1,6 @@
export type CLIOptions = {
config: string;
test: boolean;
scan: boolean;
notify: boolean;
set: string[];

17
src/util/config.ts Normal file
View File

@@ -0,0 +1,17 @@
import { readFileSync } from "fs";
const specialOptions: Record<string, (text: string) => string> = {
$__file: (arg: string) => readFileSync(arg, 'utf8').trim()
};
export const enhancedStringConfig = (value: string) => {
const trimmed = value.trim();
for(const opt of Object.keys(specialOptions)) {
if(trimmed.startsWith(`${opt}:`) && opt in specialOptions) {
return specialOptions[opt](trimmed.slice(opt.length + 1).trim());
}
}
return trimmed;
};