Compare commits

...

2 Commits

8 changed files with 77 additions and 48 deletions

View File

@@ -1,10 +1,7 @@
import dayjs from "dayjs";
import { NotificationDatabase } from "../types/notification";
import { Config } from "../types/config";
import { NtfySH } from "./ntfy-sh";
export { Backend } from "./base";
export { NtfySH } from "./ntfy-sh";
import { NtfySH } from "./ntfy";
const backends = [
new NtfySH()

View File

@@ -1,26 +0,0 @@
import { BackendSettings } from "../types/config";
import { Notification } from "../types/notification";
import { Backend } from "./base";
type Config = {
url: string;
token: string;
topic?: string;
} & BackendSettings;
export class NtfySH extends Backend<Config> {
public name = "ntfy.sh";
protected requiredFields = ['url', 'token'] as const;
protected notify(config: Config, notification: Notification): void {
fetch(`https://${config.url}/${config.topic || 'obsidian'}`, {
method: 'POST',
body: notification.text,
headers: {
'Title': notification.title,
'Authorization': `Bearer ${config.token}`
}
})
}
}

47
src/backend/ntfy.ts Normal file
View File

@@ -0,0 +1,47 @@
import fs from "fs";
import { BackendSettings } from "../types/config";
import { Notification } from "../types/notification";
import { Backend } from "./base";
import { TaskPriority } from "../types/task";
type Config = {
url: string;
tokenFile: string;
topic?: string;
} & BackendSettings;
export class NtfySH extends Backend<Config> {
public name = "ntfy";
protected requiredFields = ['url', 'tokenFile'] as const;
protected notify(config: Config, notification: Notification): void {
const token = fs.readFileSync(config.tokenFile, 'utf-8');
fetch(`https://${config.url}/${config.topic || 'obsidian'}`, {
method: 'POST',
body: notification.text,
headers: {
'Authorization': `Bearer ${token}`,
'Title': notification.title ?? "",
'Priority': mapPriority(notification.priority),
'Tags': notification.tags?.join(",") ?? ""
}
})
}
}
function mapPriority(priority?: TaskPriority): string {
if (!priority) {
return 'default';
}
return {
[TaskPriority.LOWEST]: 'min',
[TaskPriority.LOW]: 'low',
[TaskPriority.NORMAL]: 'default',
[TaskPriority.MEDIUM]: 'default',
[TaskPriority.HIGH]: 'high',
[TaskPriority.HIGHEST]: 'max',
}[priority];
}

View File

@@ -1,11 +1,13 @@
import fs from "fs";
import { Task } from "../types/task";
import { Notification, NotificationDatabase } from "../types/notification";
import { jsMapper } from "../util";
/**
* Applies the mapper for each task from list, groups them by time and dumps the data into JSON formatted file.
*/
export function dumpDatabase(file: string, tasks: Task[], mapper: string) {
export function dumpDatabase(file: string, tasks: Task[], mapper?: string) {
const data = serializeDatabase(tasks, mapper);
fs.writeFileSync(file, data);
}
@@ -13,8 +15,10 @@ 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 = new Function("$", `return ${mapper}`) as (task: Task) => Notification;
export function serializeDatabase(tasks: Task[], mapper?: string): string {
const transformer = mapper
? jsMapper<Task, Notification>(mapper, {})
: defaultMapper;
const output = tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => {
if (n.time) {
@@ -36,4 +40,13 @@ function wrapWithTimeFiller(mapper: (task: Task) => Notification): (task: Task)
time: task.reminder ?? notification.time,
}
};
}
function defaultMapper(task: Task): Notification {
return {
title: "Obsidian Tasks Reminder",
text: task.label,
priority: task.priority,
tags: task.tags,
};
}

View File

@@ -5,6 +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";
/**
* Returns all tasks from specified directory and filters them with optional query.
@@ -20,21 +21,12 @@ export async function loadTasks(directories: string[], query?: string): Promise<
HIGHEST: TaskPriority.HIGHEST
};
const filter = query && createFilter(query, ctx);
const filter = query && jsMapper<Task, boolean>(query, ctx);
const tasks = await Promise.all(directories.map(readTasksFromDirectory));
return tasks.flat().filter(t => filter ? filter(t) : true);
}
/**
* Creates a filter function for tasks using a query string and context object.
* All context' properties will be passed as variables to the query string.
*/
function createFilter(query: string, context: Record<string, unknown>): (task: Task) => boolean {
const filter = new Function('$', ...Object.keys(context), `return ${query};`);
return (task: Task) => filter(task, ...Object.values(context));
}
/**
* Read all files in specific directory and returns all tasks from those files.
*/

View File

@@ -1,7 +1,7 @@
export type Config = {
sources: string[];
query: string;
mapper: string;
query?: string;
mapper?: string;
databaseFile: string;
backend: Record<string, unknown>;
};

View File

@@ -1,9 +1,11 @@
import dayjs, { Dayjs } from "dayjs"
import { TaskPriority } from "./task";
export type Notification = {
time?: string;
title: string;
text: string;
time?: string;
title?: string;
priority?: TaskPriority;
tags?: string[];
};
export type NotificationDatabase = Record<string, Notification[]>;

4
src/util/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export function jsMapper<I, O>(code: string, context: Record<string, unknown>): (task: I) => O {
const filter = new Function('$', ...Object.keys(context), `return ${code};`);
return (task: I) => filter(task, ...Object.values(context));
}