Load ics sources from Space Config

This commit is contained in:
Marek S. Lukasiewicz
2025-01-05 16:56:39 +01:00
parent 968353ce80
commit 9d234428d4
4 changed files with 98 additions and 28 deletions

View File

@@ -23,7 +23,7 @@ icalendar:
sources: sources:
- url: https://example.com/calendar.ics - url: https://example.com/calendar.ics
# override calName for events from this source, otherwise X-WR-CALNAME is used # override calName for events from this source, otherwise X-WR-CALNAME is used
calName: Example calendar name: Example calendar
``` ```
## Usage ## Usage
@@ -44,7 +44,6 @@ select summary, description
## Roadmap ## Roadmap
- Load multiple sources from Space Config, instead of SECRETS
- Decide which properties should be exposed: - Decide which properties should be exposed:
- Location - Location
- Color? - Color?

View File

@@ -1,7 +1,7 @@
{ {
"tasks": { "tasks": {
"build": "silverbullet plug:compile -c deno.jsonc caldav.plug.yaml", "build": "silverbullet plug:compile -c deno.jsonc icalendar.plug.yaml",
"watch": "silverbullet plug:compile -c deno.jsonc caldav.plug.yaml -w" "watch": "silverbullet plug:compile -c deno.jsonc icalendar.plug.yaml -w"
}, },
"lint": { "lint": {
"rules": { "rules": {

View File

@@ -6,3 +6,21 @@ functions:
path: ./icalendar.ts:queryEvents path: ./icalendar.ts:queryEvents
events: events:
- query:ical-event - query:ical-event
config:
schema.config.properties.icalendar:
type: object
required:
- sources
properties:
sources:
type: array
minItems: 1
items:
type: object
required:
- url
properties:
url:
type: string
name:
type: string

View File

@@ -1,6 +1,5 @@
import { editor } from "@silverbulletmd/silverbullet/syscalls"; import { editor, system } from "@silverbulletmd/silverbullet/syscalls";
import { QueryProviderEvent } from "@silverbulletmd/silverbullet/types"; import { QueryProviderEvent } from "@silverbulletmd/silverbullet/types";
import { readYamlPage } from "@silverbulletmd/silverbullet/lib/yaml_page";
import { applyQuery } from "@silverbulletmd/silverbullet/lib/query"; import { applyQuery } from "@silverbulletmd/silverbullet/lib/query";
import { parseIcsCalendar, type VCalendar } from "ts-ics"; import { parseIcsCalendar, type VCalendar } from "ts-ics";
@@ -13,36 +12,90 @@ interface Event {
end: string | undefined; end: string | undefined;
} }
interface Source {
url: string;
name: string | undefined;
}
export async function queryEvents( export async function queryEvents(
{ query }: QueryProviderEvent, { query }: QueryProviderEvent,
): Promise<any[]> { ): Promise<any[]> {
const secrets = await readYamlPage("SECRETS"); const events: Event[] = [];
const icsUrl = secrets.icsUrl;
const result = await fetch(icsUrl);
const icsData = await result.text();
const calendarParsed: VCalendar = parseIcsCalendar(icsData);
if (calendarParsed.events === undefined) { const sources = await getSources();
editor.flashNotification("Did not parse events from iCalendar"); for (const source of sources) {
const identifier = (source.name === undefined || source.name === "")
? source.url
: source.name;
try {
const result = await fetch(source.url);
const icsData = await result.text();
const calendarParsed: VCalendar = parseIcsCalendar(icsData);
if (calendarParsed.events === undefined) {
throw new Error("Didn't parse events from ics data");
}
console.log(JSON.stringify(calendarParsed.events[0], null, 2));
for (const icsEvent of calendarParsed.events) {
events.push({
summary: icsEvent.summary,
description: icsEvent.description,
// Mimic properties of pages
created: icsEvent.created
? localDateString(icsEvent.created.date)
: undefined,
lastModified: icsEvent.lastModified
? localDateString(icsEvent.lastModified.date)
: undefined,
// Consistent with formats above
start: localDateString(icsEvent.start.date),
end: icsEvent.end ? localDateString(icsEvent.end.date) : undefined,
});
}
} catch (err) {
console.error(
`Getting events from ${identifier} failed with:`,
err,
);
}
}
return applyQuery(query, events, {}, {});
}
async function getSources(): Promise<Source[]> {
const config = await system.getSpaceConfig("icalendar", {});
if (!config.sources || !Array.isArray(config.sources)) {
console.error("Configure icalendar.sources");
return []; return [];
} }
const events: Event[] = calendarParsed.events.map((ics) => { const sources = config.sources;
return {
summary: ics.summary,
description: ics.description,
// Mimic properties of pages
created: ics.created ? localDateString(ics.created.date) : undefined,
lastModified: ics.lastModified
? localDateString(ics.lastModified.date)
: undefined,
// Consistent with formats above
start: localDateString(ics.start.date),
end: ics.end ? localDateString(ics.end.date) : undefined,
};
});
return applyQuery(query, events, {}, {}); if (sources.length === 0) {
console.error("Empty icalendar.sources");
return [];
}
const validated: Source[] = [];
for (const src of sources) {
if (typeof src.url !== "string") {
console.error(
`Invalid iCalendar source`,
src,
);
continue;
}
validated.push({
url: src.url,
name: (typeof src.name === "string") ? src.name : undefined,
});
}
return validated;
} }
// Copied from @silverbulletmd/silverbullet/lib/dates.ts which is not exported in the package // Copied from @silverbulletmd/silverbullet/lib/dates.ts which is not exported in the package