Improve mapping tasks to notifications
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { BackendSettings, Config } from "../types/config";
|
||||
import { Notification } from "../types/notification";
|
||||
import { Task } from "../types/task";
|
||||
|
||||
export abstract class Backend<C extends BackendSettings> {
|
||||
public abstract readonly name: string;
|
||||
protected abstract requiredFields: readonly (keyof C)[];
|
||||
protected abstract notify(config: C, notification: Notification): void;
|
||||
protected abstract notify(config: C, task: Task): void;
|
||||
|
||||
#validate(config: Partial<C>): C {
|
||||
for (const field of this.requiredFields) {
|
||||
@@ -16,13 +16,13 @@ export abstract class Backend<C extends BackendSettings> {
|
||||
return config as C;
|
||||
}
|
||||
|
||||
public remind(config: Config, notification: Notification) {
|
||||
public remind(config: Config, task: Task) {
|
||||
const cfg = config?.backend?.[this.name] as Partial<C> | undefined;
|
||||
|
||||
if (cfg?.enable !== true) {
|
||||
return
|
||||
}
|
||||
|
||||
this.notify(this.#validate(cfg), notification);
|
||||
this.notify(this.#validate(cfg), task);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BackendSettings } from "../types/config";
|
||||
import { Notification } from "../types/notification";
|
||||
import { Task } from "../types/task";
|
||||
import { Backend } from "./base";
|
||||
|
||||
type Config = BackendSettings;
|
||||
@@ -9,7 +9,7 @@ export class Debug extends Backend<Config> {
|
||||
|
||||
protected requiredFields = [] as const;
|
||||
|
||||
protected notify(config: Config, notification: Notification): void {
|
||||
console.log(JSON.stringify(notification, undefined, 2));
|
||||
protected notify(config: Config, task: Task): void {
|
||||
console.log(JSON.stringify(task, undefined, 2));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import dayjs from "dayjs";
|
||||
import { Notification, NotificationDatabase } from "../types/notification";
|
||||
import { Task, TaskDatabase } from "../types/task";
|
||||
import { Config } from "../types/config";
|
||||
import { NtfySH } from "./ntfy";
|
||||
import { Debug } from "./debug";
|
||||
@@ -13,7 +13,7 @@ const backends = [
|
||||
* Iterates through all the database notifications for current time
|
||||
* and triggers the notification using specified backends in the config.
|
||||
*/
|
||||
export async function remind(config: Config, db: NotificationDatabase) {
|
||||
export async function remind(config: Config, db: TaskDatabase) {
|
||||
const now = dayjs().format("HH:mm");
|
||||
|
||||
await run(config, db, db[now]);
|
||||
@@ -23,15 +23,15 @@ export async function remind(config: Config, db: NotificationDatabase) {
|
||||
}
|
||||
}
|
||||
|
||||
async function run(config: Config, db: NotificationDatabase, notifications?: Notification[]) {
|
||||
if(!notifications) {
|
||||
async function run(config: Config, db: TaskDatabase, tasks?: Task[]) {
|
||||
if(!tasks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const notification of notifications) {
|
||||
console.info(`Dispatching a notification: [${notification.text}]`)
|
||||
for (const task of tasks) {
|
||||
console.info(`Dispatching a notification: [${task.label}]`)
|
||||
for (const backend of backends) {
|
||||
backend.remind(config, notification);
|
||||
backend.remind(config, task);
|
||||
await snooze(1500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,79 @@
|
||||
import fs from "fs";
|
||||
import { BackendSettings } from "../types/config";
|
||||
import { Notification } from "../types/notification";
|
||||
import { Backend } from "./base";
|
||||
import { TaskPriority } from "../types/task";
|
||||
import { Task, TaskPriority } from "../types/task";
|
||||
import { enhancedStringConfig } from "../util/config";
|
||||
import { jsMapper } from "../util/code";
|
||||
|
||||
type Config = {
|
||||
url: string;
|
||||
token: string;
|
||||
mapper?: string;
|
||||
topic?: string;
|
||||
} & BackendSettings;
|
||||
|
||||
type NtfyDTO = {
|
||||
title?: string;
|
||||
text?: string;
|
||||
priority?: string;
|
||||
tags?: string[];
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
export class NtfySH extends Backend<Config> {
|
||||
public name = "ntfy";
|
||||
|
||||
protected requiredFields = ['url', 'token'] as const;
|
||||
|
||||
protected notify(config: Config, notification: Notification): void {
|
||||
protected notify(config: Config, task: Task): void {
|
||||
const token = enhancedStringConfig(config.token);
|
||||
const mapper = config.mapper
|
||||
? jsMapper<object, NtfyDTO>(config.mapper, { mapPriority })
|
||||
: defaultMapper;
|
||||
|
||||
const dto = mapper(task);
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`
|
||||
};
|
||||
|
||||
buildHeaders(dto, headers);
|
||||
|
||||
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(",") ?? ""
|
||||
}
|
||||
body: dto.text ?? "",
|
||||
headers
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function buildHeaders(dto: NtfyDTO, headers: Record<string, string>) {
|
||||
if (dto.title) {
|
||||
headers.Title = dto.title;
|
||||
}
|
||||
|
||||
if (dto.priority) {
|
||||
headers.Priority = dto.priority;
|
||||
}
|
||||
|
||||
if (dto.tags) {
|
||||
headers.Tags = dto.tags.join(",");
|
||||
}
|
||||
|
||||
if (dto.icon) {
|
||||
headers.Icon = dto.icon;
|
||||
}
|
||||
}
|
||||
|
||||
function defaultMapper(task: Task): NtfyDTO {
|
||||
return {
|
||||
title: "Obsidian Task Reminder",
|
||||
text: task.label,
|
||||
priority: mapPriority(task.priority),
|
||||
tags: task.tags,
|
||||
}
|
||||
}
|
||||
|
||||
function mapPriority(priority?: TaskPriority): string {
|
||||
if (!priority) {
|
||||
return 'default';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import fs from "fs";
|
||||
import { NotificationDatabase } from "../types/notification";
|
||||
import { TaskDatabase } from "../types/task";
|
||||
|
||||
/**
|
||||
* Loads and deserializes database from JSON formatted file.
|
||||
*/
|
||||
export function loadDatabase(file: string): NotificationDatabase {
|
||||
export function loadDatabase(file: string): TaskDatabase {
|
||||
const text = fs.readFileSync(file).toString();
|
||||
return deserializeDatabase(text);
|
||||
}
|
||||
@@ -12,6 +12,6 @@ export function loadDatabase(file: string): NotificationDatabase {
|
||||
/**
|
||||
* Deserializes database from JSON format.
|
||||
*/
|
||||
export function deserializeDatabase(json: string): NotificationDatabase {
|
||||
return JSON.parse(json) as NotificationDatabase;
|
||||
export function deserializeDatabase(json: string): TaskDatabase {
|
||||
return JSON.parse(json) as TaskDatabase;
|
||||
}
|
||||
@@ -1,57 +1,35 @@
|
||||
import fs from "fs";
|
||||
import { Task } from "../types/task";
|
||||
import { Notification, NotificationDatabase } from "../types/notification";
|
||||
import { jsMapper } from "../util/code";
|
||||
import { Task, TaskDatabase } from "../types/task";
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const data = serializeDatabase(tasks, mapper);
|
||||
export function dumpDatabase(file: string, tasks: Task[]) {
|
||||
const data = serializeDatabase(tasks);
|
||||
fs.writeFileSync(file, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
return JSON.stringify(createDatabase(tasks, mapper));
|
||||
export function serializeDatabase(tasks: Task[]): string {
|
||||
|
||||
const x = JSON.stringify(createDatabase(tasks));
|
||||
console.log(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
export function createDatabase(tasks: Task[]): TaskDatabase {
|
||||
|
||||
return tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => {
|
||||
if (n.time) {
|
||||
(acc[n.time] = (acc[n.time] || [])).push(n);
|
||||
return tasks.filter(t => t.reminder).reduce((acc, t) => {
|
||||
if (t.reminder) {
|
||||
(acc[t.reminder] = (acc[t.reminder] || [])).push(t);
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {} as NotificationDatabase);
|
||||
}, {} as TaskDatabase);
|
||||
}
|
||||
|
||||
function wrapWithTimeFiller(mapper: (task: Task) => Notification): (task: Task) => Notification {
|
||||
return (task: Task) => {
|
||||
const notification = mapper(task);
|
||||
|
||||
return {
|
||||
...notification,
|
||||
time: task.reminder ?? notification.time,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function defaultMapper(task: Task): Notification {
|
||||
return {
|
||||
title: "Obsidian Tasks Reminder",
|
||||
text: task.label,
|
||||
priority: task.priority,
|
||||
tags: task.tags,
|
||||
};
|
||||
}
|
||||
@@ -149,4 +149,26 @@ export class LazyTask implements Task {
|
||||
|
||||
return `- [${this.status}] ${this.label} {${items.filter(x => x.length > 0).join(", ")}}`;
|
||||
}
|
||||
|
||||
toJSON(): Task {
|
||||
return {
|
||||
status: this.status,
|
||||
label: this.label,
|
||||
fullLabel: this.fullLabel,
|
||||
tags: this.tags,
|
||||
priority: this.priority,
|
||||
priorityStr: this.priorityStr,
|
||||
createdDate: this.createdDate,
|
||||
startDate: this.startDate,
|
||||
scheduledDate: this.scheduledDate,
|
||||
dueDate: this.dueDate,
|
||||
completedDate: this.completedDate,
|
||||
cancelledDate: this.cancelledDate,
|
||||
recurrenceRule: this.recurrenceRule,
|
||||
onDelete: this.onDelete,
|
||||
id: this.id,
|
||||
dependsOn: this.dependsOn,
|
||||
reminder: this.reminder,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ 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);
|
||||
const db = createDatabase(tasks);
|
||||
|
||||
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(",")}`)
|
||||
for (const task of db[time]) {
|
||||
console.log(task.toString());
|
||||
}
|
||||
|
||||
console.log();
|
||||
@@ -21,7 +21,7 @@ export async function test(config: Config) {
|
||||
|
||||
export async function scan(config: Config) {
|
||||
const tasks = await loadTasks(config.sources, config.query);
|
||||
dumpDatabase(config.databaseFile, tasks, config.mapper);
|
||||
dumpDatabase(config.databaseFile, tasks);
|
||||
}
|
||||
|
||||
export async function notify(config: Config) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export type Config = {
|
||||
sources: string[];
|
||||
query?: string;
|
||||
mapper?: string;
|
||||
query?: string;
|
||||
defaultTime?: string;
|
||||
databaseFile: string;
|
||||
backend: Record<string, unknown>;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { TaskPriority } from "./task";
|
||||
|
||||
export type Notification = {
|
||||
text: string;
|
||||
time?: string;
|
||||
title?: string;
|
||||
priority?: TaskPriority;
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
export type NotificationDatabase = Record<string, Notification[]>;
|
||||
@@ -17,7 +17,8 @@ export type Task = {
|
||||
onDelete?: string;
|
||||
id?: string;
|
||||
dependsOn?: string[];
|
||||
reminder?: string;
|
||||
reminder?: string;
|
||||
|
||||
}
|
||||
|
||||
export enum TaskPriority {
|
||||
@@ -27,4 +28,6 @@ export enum TaskPriority {
|
||||
MEDIUM,
|
||||
HIGH,
|
||||
HIGHEST,
|
||||
};
|
||||
};
|
||||
|
||||
export type TaskDatabase = Record<string, Task[]>;
|
||||
Reference in New Issue
Block a user