[mod]
This commit is contained in:
parent
556da302fa
commit
e19be3ae43
|
@ -10,6 +10,7 @@
|
|||
"ical",
|
||||
"telegram",
|
||||
"conf",
|
||||
"log",
|
||||
"args"
|
||||
]
|
||||
}
|
||||
|
@ -19,6 +20,8 @@
|
|||
"types.ts",
|
||||
"sources/kalender_digital.ts",
|
||||
"sources/_functions.ts",
|
||||
"targets/telegram_bot.ts",
|
||||
"targets/_functions.ts",
|
||||
"conf.ts",
|
||||
"main.ts"
|
||||
]
|
||||
|
|
4
libs/plankton/plankton.d.ts
vendored
4
libs/plankton/plankton.d.ts
vendored
|
@ -2258,7 +2258,9 @@ declare namespace lib_plankton.telegram {
|
|||
/**
|
||||
* @see https://core.telegram.org/bots/api#sendmessage
|
||||
*/
|
||||
function bot_call_send_message(token: string, chat_id: (int | string), text: string): Promise<void>;
|
||||
function bot_call_send_message(token: string, chat_id: (int | string), text: string, { "parse_mode": parse_mode, }?: {
|
||||
parse_mode?: (null | string);
|
||||
}): Promise<void>;
|
||||
}
|
||||
declare namespace lib_plankton.file {
|
||||
/**
|
||||
|
|
|
@ -6243,10 +6243,11 @@ var lib_plankton;
|
|||
/**
|
||||
* @see https://core.telegram.org/bots/api#sendmessage
|
||||
*/
|
||||
async function bot_call_send_message(token, chat_id, text) {
|
||||
async function bot_call_send_message(token, chat_id, text, { "parse_mode": parse_mode = null, } = {}) {
|
||||
const output = await bot_call_generic(token, "sendMessage", {
|
||||
"chat_id": chat_id,
|
||||
"text": text,
|
||||
"parse_mode": (parse_mode ?? undefined),
|
||||
});
|
||||
}
|
||||
telegram.bot_call_send_message = bot_call_send_message;
|
||||
|
|
26
readme.md
26
readme.md
|
@ -2,3 +2,29 @@
|
|||
|
||||
Sendet Erinnerungen an Termine
|
||||
|
||||
|
||||
## Erstellung
|
||||
|
||||
### Erfordernisse
|
||||
|
||||
- GNU Make
|
||||
- Typescript Compiler
|
||||
|
||||
|
||||
### Anweisungen
|
||||
|
||||
- `tools/build`
|
||||
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Erfordernisse
|
||||
|
||||
- NodeJS
|
||||
|
||||
|
||||
### Anweisungen
|
||||
|
||||
siehe `munin -h`
|
||||
|
||||
|
||||
|
|
177
source/conf.ts
177
source/conf.ts
|
@ -1,16 +1,38 @@
|
|||
namespace _lixer_event_reminder.conf
|
||||
/*
|
||||
This file is part of »munin«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @todo versioning
|
||||
*/
|
||||
namespace _munin.conf
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_conf = {
|
||||
bot_token : string;
|
||||
sources : Array<
|
||||
(
|
||||
{
|
||||
kind : "kalender_digital",
|
||||
data : {
|
||||
id : string;
|
||||
path : string;
|
||||
filtration : {
|
||||
category_blacklist : Array<string>;
|
||||
title_blacklist : Array<string>;
|
||||
|
@ -20,11 +42,30 @@ namespace _lixer_event_reminder.conf
|
|||
)
|
||||
>;
|
||||
targets : Array<
|
||||
{
|
||||
chat_id : int;
|
||||
interval : Array<int>;
|
||||
}
|
||||
(
|
||||
{
|
||||
kind : "telegram_bot",
|
||||
data : {
|
||||
bot_token : string;
|
||||
chat_id : int;
|
||||
/**
|
||||
* in hours
|
||||
*/
|
||||
interval : Array<int>;
|
||||
}
|
||||
}
|
||||
)
|
||||
>;
|
||||
/**
|
||||
* in hours
|
||||
*/
|
||||
frequency : float;
|
||||
labels : {
|
||||
head : string;
|
||||
title : string;
|
||||
time : string;
|
||||
location : string;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
@ -38,10 +79,6 @@ namespace _lixer_event_reminder.conf
|
|||
"nullable": false,
|
||||
"type": "string",
|
||||
},
|
||||
"bot_token": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
},
|
||||
"sources": {
|
||||
"nullable": false,
|
||||
"type": "array",
|
||||
|
@ -60,7 +97,7 @@ namespace _lixer_event_reminder.conf
|
|||
"nullable": false,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"path": {
|
||||
"nullable": false,
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -74,7 +111,8 @@ namespace _lixer_event_reminder.conf
|
|||
"items": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
}
|
||||
},
|
||||
"default": [],
|
||||
},
|
||||
"title_blacklist": {
|
||||
"nullable": false,
|
||||
|
@ -82,20 +120,19 @@ namespace _lixer_event_reminder.conf
|
|||
"items": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
}
|
||||
},
|
||||
"default": [],
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"category_blacklist",
|
||||
"title_blacklist",
|
||||
]
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"filtration",
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -113,35 +150,95 @@ namespace _lixer_event_reminder.conf
|
|||
"type": "array",
|
||||
"items": {
|
||||
"nullable": false,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chat_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"nullable": false,
|
||||
"type": "integer",
|
||||
},
|
||||
"interval": {
|
||||
"nullable": false,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"nullable": false,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"chat_id",
|
||||
"interval",
|
||||
]
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
"enum": ["telegram_bot"]
|
||||
},
|
||||
"data": {
|
||||
"nullable": false,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bot_token": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
},
|
||||
"chat_id": {
|
||||
"nullable": false,
|
||||
"type": "integer",
|
||||
},
|
||||
"interval": {
|
||||
"nullable": false,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"nullable": false,
|
||||
"type": "integer"
|
||||
},
|
||||
"default": [24.0],
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"bot_token",
|
||||
"chat_id",
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"kind",
|
||||
"data",
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
"frequency": {
|
||||
"nullable": false,
|
||||
"type": "number",
|
||||
"default": 1.0,
|
||||
},
|
||||
"labels": {
|
||||
"nullable": false,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"head": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
"default": "Termin-Erinnerung"
|
||||
},
|
||||
"title": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
"default": "was"
|
||||
},
|
||||
"time": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
"default": "wann"
|
||||
},
|
||||
"location": {
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
"default": "wo"
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"version",
|
||||
"bot_token",
|
||||
"chat_id",
|
||||
"intval",
|
||||
"sources",
|
||||
"targets",
|
||||
],
|
||||
};
|
||||
|
||||
|
|
304
source/main.ts
304
source/main.ts
|
@ -1,6 +1,143 @@
|
|||
namespace _lixer_event_reminder
|
||||
/*
|
||||
This file is part of »munin«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace _munin
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
async function run_iteration(
|
||||
conf : _munin.conf.type_conf,
|
||||
sources : Array<_munin.type_source>,
|
||||
targets : Array<_munin.type_target>,
|
||||
{
|
||||
"dry_run": dry_run = false,
|
||||
} : {
|
||||
dry_run ?: boolean;
|
||||
} = {
|
||||
}
|
||||
) : Promise<void>
|
||||
{
|
||||
const now : lib_plankton.pit.type_pit = lib_plankton.pit.now();
|
||||
const events : Array<_munin.type_event> = (
|
||||
(await Promise.all(sources.map(source => source.fetch())))
|
||||
.reduce((x, y) => x.concat(y), [])
|
||||
);
|
||||
for (const target of targets) {
|
||||
lib_plankton.log._info(
|
||||
"munin.run.iteration",
|
||||
{
|
||||
"details": {
|
||||
"target": target,
|
||||
}
|
||||
}
|
||||
);
|
||||
for (const hours of target.interval) {
|
||||
const window_from : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
||||
now,
|
||||
hours + 0
|
||||
);
|
||||
const window_to : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
||||
now,
|
||||
hours + (conf.frequency / 2)
|
||||
);
|
||||
for (const event of events) {
|
||||
const event_begin : lib_plankton.pit.type_pit = lib_plankton.pit.from_datetime(event.begin);
|
||||
lib_plankton.log._debug(
|
||||
"munin.run.check_dueness",
|
||||
{
|
||||
"details": {
|
||||
"event_begin": lib_plankton.pit.datetime_format(lib_plankton.pit.to_datetime(event_begin)),
|
||||
"window_from": lib_plankton.pit.datetime_format(lib_plankton.pit.to_datetime(window_from)),
|
||||
"window_to": lib_plankton.pit.datetime_format(lib_plankton.pit.to_datetime(window_to)),
|
||||
}
|
||||
}
|
||||
);
|
||||
const remind : boolean = lib_plankton.pit.is_between(
|
||||
event_begin,
|
||||
window_from,
|
||||
window_to
|
||||
);
|
||||
if (! remind) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
lib_plankton.log._info(
|
||||
"munin.remind",
|
||||
{
|
||||
"details": {
|
||||
"event": event,
|
||||
"target": target,
|
||||
}
|
||||
}
|
||||
);
|
||||
if (dry_run) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
await target.send(conf.labels, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function run(
|
||||
conf : _munin.conf.type_conf,
|
||||
{
|
||||
"dry_run": dry_run = false,
|
||||
} : {
|
||||
dry_run ?: boolean;
|
||||
} = {
|
||||
}
|
||||
) : void
|
||||
{
|
||||
const sources : Array<_munin.type_source> = conf.sources.map(
|
||||
source_raw => _munin.sources.factory(source_raw)
|
||||
);
|
||||
const targets : Array<_munin.type_target> = conf.targets.map(
|
||||
target_raw => _munin.targets.factory(target_raw)
|
||||
);
|
||||
lib_plankton.log._info(
|
||||
"munin.run.start",
|
||||
{
|
||||
"details": {
|
||||
}
|
||||
}
|
||||
);
|
||||
run_iteration(conf, sources, targets, {"dry_run": dry_run});
|
||||
/**
|
||||
* outsource setInterval logic
|
||||
*/
|
||||
setInterval(
|
||||
() => {run_iteration(conf, sources, targets, {"dry_run": dry_run});},
|
||||
(conf.frequency * 60 * 60 * 1000)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function main(
|
||||
|
@ -27,14 +164,32 @@ namespace _lixer_event_reminder
|
|||
"info": "path to configuration file",
|
||||
"name": "conf-path",
|
||||
}),
|
||||
"message": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["message"],
|
||||
"indicators_short": ["m"],
|
||||
"conf_expose": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["conf-expose"],
|
||||
"indicators_short": ["e"],
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
"info": "whether to expose the full configuration",
|
||||
"name": "conf-expose",
|
||||
}),
|
||||
"verbosity": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["verbosity"],
|
||||
"indicators_short": ["v"],
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "test",
|
||||
"info": "message to send",
|
||||
"name": "message",
|
||||
"default": "notice",
|
||||
"info": "error | warning | notice | info | debug",
|
||||
"name": "verbosity",
|
||||
}),
|
||||
"dry_run": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["dry-run"],
|
||||
"indicators_short": ["q"],
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
"info": "whether to not skip the sending of reminders (logs will be written)",
|
||||
"name": "dry-run",
|
||||
}),
|
||||
"help": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["help"],
|
||||
|
@ -52,13 +207,6 @@ namespace _lixer_event_reminder
|
|||
args_raw.join(" ")
|
||||
);
|
||||
|
||||
// init
|
||||
const conf : _lixer_event_reminder.conf.type_conf = await lib_plankton.conf.load<_lixer_event_reminder.conf.type_conf>(
|
||||
_lixer_event_reminder.conf.schema,
|
||||
args.conf_path
|
||||
);
|
||||
|
||||
// exec
|
||||
if (args.help || (args.action === "help")) {
|
||||
process.stdout.write(
|
||||
arg_handler.generate_help(
|
||||
|
@ -71,83 +219,69 @@ namespace _lixer_event_reminder
|
|||
);
|
||||
}
|
||||
else {
|
||||
// init
|
||||
const conf : _munin.conf.type_conf = await lib_plankton.conf.load<_munin.conf.type_conf>(
|
||||
_munin.conf.schema,
|
||||
args.conf_path
|
||||
);
|
||||
lib_plankton.log.set_main_logger(
|
||||
[
|
||||
{
|
||||
"kind": "filtered",
|
||||
"data": {
|
||||
"core": {
|
||||
"kind": "std",
|
||||
"data": {
|
||||
"target": "stdout",
|
||||
"format": {
|
||||
"kind": "human_readable",
|
||||
"data": {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"predicate": [
|
||||
[
|
||||
{
|
||||
"item": {
|
||||
"kind": "level",
|
||||
"data": {
|
||||
"threshold": args.verbosity,
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
],
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// exec
|
||||
if (args.conf_expose) {
|
||||
process.stdout.write(
|
||||
lib_plankton.json.encode(
|
||||
conf,
|
||||
{
|
||||
"formatted": true,
|
||||
}
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
switch (args.action) {
|
||||
default: {
|
||||
throw (new Error("unhandled action: " + args.action));
|
||||
break;
|
||||
}
|
||||
case "fetch": {
|
||||
const updates : Array<any> = await lib_plankton.telegram.bot_call_get_updates(
|
||||
conf.bot_token
|
||||
);
|
||||
process.stdout.write(JSON.stringify(updates, undefined, "\t") + "\n");
|
||||
break;
|
||||
}
|
||||
case "send": {
|
||||
for (const target of conf.targets) {
|
||||
const message = await lib_plankton.telegram.bot_call_send_message(
|
||||
conf.bot_token,
|
||||
target.chat_id,
|
||||
args.message
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "run": {
|
||||
const sources : Array<_lixer_event_reminder.type_source> = conf.sources.map(
|
||||
source_raw => _lixer_event_reminder.sources.factory(source_raw)
|
||||
);
|
||||
const targets : Array<_lixer_event_reminder.type_target> = conf.targets.map(
|
||||
target_raw => target_raw
|
||||
);
|
||||
const now : lib_plankton.pit.type_pit = lib_plankton.pit.now();
|
||||
|
||||
const events : Array<_lixer_event_reminder.type_event> = (
|
||||
(await Promise.all(sources.map(source => source.fetch())))
|
||||
.reduce((x, y) => x.concat(y), [])
|
||||
);
|
||||
for (const event of events) {
|
||||
const begin : lib_plankton.pit.type_pit = lib_plankton.pit.from_datetime(event.begin);
|
||||
for (const target of targets) {
|
||||
for (const hours of target.interval) {
|
||||
const remind : boolean = lib_plankton.pit.is_between(
|
||||
lib_plankton.pit.shift_hour(begin, hours),
|
||||
now,
|
||||
/**
|
||||
* @todo parametrize window
|
||||
*/
|
||||
lib_plankton.pit.shift_hour(now, +24)
|
||||
);
|
||||
if (remind) {
|
||||
lib_plankton.log._info(
|
||||
"remind",
|
||||
{
|
||||
"details": {
|
||||
"event": event,
|
||||
"target": target,
|
||||
}
|
||||
}
|
||||
);
|
||||
/**
|
||||
* @todo activate
|
||||
* @todo format better
|
||||
*/
|
||||
const message = await lib_plankton.telegram.bot_call_send_message(
|
||||
conf.bot_token,
|
||||
target.chat_id,
|
||||
lib_plankton.string.coin(
|
||||
"{{title}} | {{begin}}",
|
||||
{
|
||||
"title": event.title,
|
||||
"begin": lib_plankton.pit.datetime_format(event.begin),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
run(
|
||||
conf,
|
||||
{
|
||||
"dry_run": args.dry_run,
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +291,7 @@ namespace _lixer_event_reminder
|
|||
}
|
||||
}
|
||||
|
||||
_lixer_event_reminder.main(process.argv.slice(2))
|
||||
_munin.main(process.argv.slice(2))
|
||||
.then(() => {})
|
||||
.catch((reason) => {process.stderr.write(String(reason) + "\n");})
|
||||
;
|
||||
|
|
|
@ -1,4 +1,24 @@
|
|||
namespace _lixer_event_reminder.sources
|
||||
/*
|
||||
This file is part of »munin«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace _munin.sources
|
||||
{
|
||||
|
||||
/**
|
||||
|
@ -8,7 +28,7 @@ namespace _lixer_event_reminder.sources
|
|||
kind : string,
|
||||
data : any
|
||||
}
|
||||
) : type_source
|
||||
) : _munin.type_source
|
||||
{
|
||||
switch (description.kind) {
|
||||
default: {
|
||||
|
@ -16,8 +36,8 @@ namespace _lixer_event_reminder.sources
|
|||
break;
|
||||
}
|
||||
case "kalender_digital": {
|
||||
return _lixer_event_reminder.sources.kalender_digital.implementation_source(
|
||||
description.data as _lixer_event_reminder.sources.kalender_digital.type_parameters
|
||||
return _munin.sources.kalender_digital.implementation_source(
|
||||
description.data as _munin.sources.kalender_digital.type_parameters
|
||||
);
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
namespace _lixer_event_reminder.sources.kalender_digital
|
||||
namespace _munin.sources.kalender_digital
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_parameters = {
|
||||
id : string;
|
||||
path : string;
|
||||
filtration : {
|
||||
category_blacklist : Array<string>;
|
||||
title_blacklist : Array<string>;
|
||||
|
@ -16,15 +16,15 @@ namespace _lixer_event_reminder.sources.kalender_digital
|
|||
*/
|
||||
async function fetch(
|
||||
parameters : type_parameters
|
||||
) : Promise<Array<_lixer_event_reminder.type_event>>
|
||||
) : Promise<Array<_munin.type_event>>
|
||||
{
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"scheme": "https",
|
||||
"host": "export.kalender.digital",
|
||||
"path": lib_plankton.string.coin(
|
||||
"/ics/0/{{id}}/gesamterkalender.ics",
|
||||
"/ics/{{path}}.ics",
|
||||
{
|
||||
"id": parameters.id,
|
||||
"path": parameters.path,
|
||||
}
|
||||
),
|
||||
"version": "HTTP/2",
|
||||
|
@ -33,7 +33,10 @@ namespace _lixer_event_reminder.sources.kalender_digital
|
|||
"?past_months={{past_months}}&future_months={{future_months}}",
|
||||
{
|
||||
"past_months": (0).toFixed(0),
|
||||
"future_months": (2).toFixed(0),
|
||||
/**
|
||||
* anpassen an frequency?
|
||||
*/
|
||||
"future_months": (1).toFixed(0),
|
||||
}
|
||||
),
|
||||
"headers": {},
|
||||
|
@ -42,13 +45,34 @@ namespace _lixer_event_reminder.sources.kalender_digital
|
|||
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(http_request);
|
||||
const ics : string = http_response.body.toString();
|
||||
const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode(ics);
|
||||
const events : Array<_lixer_event_reminder.type_event> = (
|
||||
const events : Array<_munin.type_event> = (
|
||||
vcalendar.vevents
|
||||
.filter(
|
||||
vevent => (
|
||||
vevent.categories.every(category => ! parameters.filtration.category_blacklist.includes(category))
|
||||
// category
|
||||
(
|
||||
vevent.categories.every(
|
||||
category => (
|
||||
! (
|
||||
parameters.filtration.category_blacklist
|
||||
.map(category_ => category_.toLowerCase())
|
||||
.includes(category.toLowerCase())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
&&
|
||||
parameters.filtration.title_blacklist.every(title => ! vevent.summary.toLowerCase().includes(title.toLowerCase()))
|
||||
// title
|
||||
(
|
||||
parameters.filtration.title_blacklist.every(
|
||||
title => (
|
||||
! (
|
||||
vevent.summary.toLowerCase()
|
||||
.includes(title.toLowerCase())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(
|
||||
|
@ -63,11 +87,11 @@ namespace _lixer_event_reminder.sources.kalender_digital
|
|||
"date": vevent.dtend.value.date,
|
||||
"time": vevent.dtend.value.time,
|
||||
};
|
||||
const event : _lixer_event_reminder.type_event = {
|
||||
const event : _munin.type_event = {
|
||||
"title": vevent.summary,
|
||||
"begin": begin,
|
||||
"end": end,
|
||||
"location": vevent.location,
|
||||
"location": (vevent.location ?? null),
|
||||
};
|
||||
return event;
|
||||
}
|
||||
|
@ -81,7 +105,7 @@ namespace _lixer_event_reminder.sources.kalender_digital
|
|||
*/
|
||||
export function implementation_source(
|
||||
parameters : type_parameters
|
||||
) : _lixer_event_reminder.type_source
|
||||
) : _munin.type_source
|
||||
{
|
||||
return {
|
||||
"fetch": () => fetch(parameters),
|
||||
|
|
48
source/targets/_functions.ts
Normal file
48
source/targets/_functions.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
This file is part of »munin«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace _munin.targets
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function factory(
|
||||
description : {
|
||||
kind : string,
|
||||
data : any
|
||||
}
|
||||
) : _munin.type_target
|
||||
{
|
||||
switch (description.kind) {
|
||||
default: {
|
||||
throw (new Error("unhandled target kind: " + description.kind));
|
||||
break;
|
||||
}
|
||||
case "telegram_bot": {
|
||||
return _munin.targets.telegram_bot.implementation_target(
|
||||
description.data as _munin.targets.telegram_bot.type_parameters
|
||||
);
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
87
source/targets/telegram_bot.ts
Normal file
87
source/targets/telegram_bot.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
This file is part of »munin«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace _munin.targets.telegram_bot
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_parameters = {
|
||||
bot_token : string;
|
||||
chat_id : int;
|
||||
interval : Array<int>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function send(
|
||||
parameters : type_parameters,
|
||||
labels : _munin.type_labels,
|
||||
event : _munin.type_event
|
||||
) : Promise<void>
|
||||
{
|
||||
await lib_plankton.telegram.bot_call_send_message(
|
||||
parameters.bot_token,
|
||||
parameters.chat_id,
|
||||
lib_plankton.string.coin(
|
||||
"*{{head}}*\n\n\{{title_label}} | {{title_value}}\n{{time_label}} | {{time_value}}{{macro_location}}",
|
||||
{
|
||||
"head": labels.head,
|
||||
"title_label": labels.title.toUpperCase(),
|
||||
"title_value": event.title,
|
||||
"time_label": labels.time.toUpperCase(),
|
||||
"time_value": lib_plankton.pit.timespan_format(event.begin, event.end),
|
||||
"macro_location": (
|
||||
(event.location === null)
|
||||
?
|
||||
""
|
||||
:
|
||||
lib_plankton.string.coin(
|
||||
"\n{{location_label}} | {{location_value}}",
|
||||
{
|
||||
"location_label": labels.location.toUpperCase(),
|
||||
"location_value": event.location,
|
||||
}
|
||||
)
|
||||
),
|
||||
}
|
||||
),
|
||||
{
|
||||
"parse_mode": "markdown",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function implementation_target(
|
||||
parameters : type_parameters
|
||||
) : _munin.type_target
|
||||
{
|
||||
return {
|
||||
"interval": parameters.interval,
|
||||
"send": (labels, event) => send(parameters, labels, event),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
namespace _lixer_event_reminder
|
||||
namespace _munin
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_labels = {
|
||||
head : string;
|
||||
title : string;
|
||||
time : string;
|
||||
location : string;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_event = {
|
||||
|
@ -9,20 +19,32 @@ namespace _lixer_event_reminder
|
|||
end : (null | lib_plankton.pit.type_datetime),
|
||||
location : (null | string);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_source = {
|
||||
fetch : (() => Promise<Array<type_event>>);
|
||||
fetch : (
|
||||
(
|
||||
)
|
||||
=>
|
||||
Promise<Array<type_event>>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_target = {
|
||||
chat_id : int;
|
||||
interval : Array<int>;
|
||||
send : (
|
||||
(
|
||||
labels : type_labels,
|
||||
event : type_event
|
||||
)
|
||||
=>
|
||||
Promise<void>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
0
tools/clear
Normal file → Executable file
0
tools/clear
Normal file → Executable file
42
tools/ivaldi
42
tools/ivaldi
|
@ -5076,43 +5076,43 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||
});
|
||||
};
|
||||
/*
|
||||
This file is part of »munin«.
|
||||
This file is part of »ivaldi«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
»ivaldi« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
»ivaldi« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with »ivaldi«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
This file is part of »munin«.
|
||||
This file is part of »ivaldi«.
|
||||
|
||||
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
|
||||
|
||||
»munin« is free software: you can redistribute it and/or modify
|
||||
»ivaldi« is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
»munin« is distributed in the hope that it will be useful,
|
||||
»ivaldi« is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with »ivaldi«. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
var _munin;
|
||||
(function (_munin) {
|
||||
var _ivaldi;
|
||||
(function (_ivaldi) {
|
||||
/**
|
||||
* @todo outsource
|
||||
*/
|
||||
|
@ -5252,9 +5252,9 @@ var _munin;
|
|||
"libs": "libs",
|
||||
"temp": "temp",
|
||||
"build": "build",
|
||||
"makefile": "/tmp",
|
||||
},
|
||||
"command_tsc": "tsc",
|
||||
"makefile_path": "/tmp/ivaldi-makefile",
|
||||
};
|
||||
switch (args.action) {
|
||||
default: {
|
||||
|
@ -5432,11 +5432,17 @@ var _munin;
|
|||
"break_dependencies": true,
|
||||
"silent_actions": true,
|
||||
});
|
||||
yield lib_plankton.file.write(conf.makefile_path, makefile);
|
||||
const command = lib_plankton.string.coin("make -f {{path}}", {
|
||||
"path": conf.makefile_path,
|
||||
const makefile_path = lib_plankton.string.coin("{{directory}}/ivaldi-makefile-{{name}}", {
|
||||
"directory": conf.directories.makefile,
|
||||
"name": data.name,
|
||||
});
|
||||
yield execute(command);
|
||||
yield lib_plankton.file.write(makefile_path, makefile);
|
||||
const command = lib_plankton.string.coin("make -f {{path}}", {
|
||||
"path": makefile_path,
|
||||
});
|
||||
const result = yield execute(command);
|
||||
process.stdout.write(result.stdout + "\n");
|
||||
process.stderr.write(result.stderr + "\n");
|
||||
return Promise.resolve(undefined);
|
||||
break;
|
||||
}
|
||||
|
@ -5448,8 +5454,8 @@ var _munin;
|
|||
}
|
||||
});
|
||||
}
|
||||
_munin.main = main;
|
||||
})(_munin || (_munin = {}));
|
||||
_munin.main(process.argv.slice(2))
|
||||
_ivaldi.main = main;
|
||||
})(_ivaldi || (_ivaldi = {}));
|
||||
_ivaldi.main(process.argv.slice(2))
|
||||
.then(() => { })
|
||||
.catch((reason) => { process.stderr.write(String(reason) + "\n"); });
|
||||
|
|
Loading…
Reference in a new issue