Compare commits

..

4 commits

Author SHA1 Message Date
fenris 21c2d9945c [task-340] soweit fertig 2025-06-30 13:02:06 +02:00
fenris 37f7a27e95 [task-340] [mod] testing 2025-06-28 08:34:57 +02:00
Christian Fraß 1ade4dec2a [task-340] [int] 2025-06-28 04:27:46 +00:00
Christian Fraß 358f33b18a [task-340] [int] 2025-06-27 20:07:18 +00:00
14 changed files with 823 additions and 798 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/build/ /build/
/temp/ /temp/
/conf/
/.geany /.geany

View file

@ -1,25 +0,0 @@
{
"meta": {
"identifier": "deu",
"name": "Deutsch"
},
"tree": {
"core.event.event": "Termin",
"core.event.events": "Termine",
"core.event.title.long": "Titel",
"core.event.title.short": "was",
"core.event.time.long": "Datum und Uhrzeit",
"core.event.time.short": "wann",
"core.event.location.long": "Ort",
"core.event.location.short": "wo",
"core.reminder.reminder": "Erinnerung",
"args.action.description": "auszuführende Aktion",
"args.conf_path.description": "Pfad zur Konfigurations-Datei",
"args.conf_schema.description": "nur das Konfigurations-Schema ausgeben",
"args.conf_expose.description": "die vervollständigte Konfiguration ausgeben",
"args.single_run.description": "nur einen Durchlauf ausführen",
"args.dry_run.description": "das Senden von Benachrichtigungen überspringen",
"args.help.description": "nur die Hilfe-Seite anzeigen",
"help.description": "sendet Erinnerungen zu anstehenden Terminen"
}
}

View file

@ -1,25 +0,0 @@
{
"meta": {
"identifier": "eng",
"name": "English"
},
"tree": {
"core.event.event": "event",
"core.event.events": "events",
"core.event.title.long": "title",
"core.event.title.short": "what",
"core.event.time.long": "date and time",
"core.event.time.short": "when",
"core.event.location.long": "location",
"core.event.location.short": "where",
"core.reminder.reminder": "reminder",
"args.action.description": "what to do",
"args.conf_path.description": "path to configuration file",
"args.conf_schema.description": "only print the configuration schema",
"args.conf_expose.description": "whether to expose the full configuration",
"args.single_run.description": "whether to only execute one iteration at run",
"args.dry_run.description": "whether to skip the sending of reminders (logs will be written)",
"args.help.description": "only print the help page",
"help.description": "sends reminders about upcoming events"
}
}

View file

@ -16,7 +16,6 @@
"file", "file",
"conf", "conf",
"log", "log",
"translate",
"args" "args"
] ]
} }

View file

@ -1211,9 +1211,6 @@ declare namespace lib_plankton.pit {
/** /**
*/ */
function is_after(pit: type_pit, reference: type_pit): boolean; function is_after(pit: type_pit, reference: type_pit): boolean;
/**
*/
function is_equal_or_after(pit: type_pit, reference: type_pit): boolean;
/** /**
*/ */
function is_between(pit: type_pit, reference_left: type_pit, reference_right: type_pit): boolean; function is_between(pit: type_pit, reference_left: type_pit, reference_right: type_pit): boolean;
@ -2701,99 +2698,6 @@ declare namespace lib_plankton.conf {
*/ */
function load_versioned(path: string, get_schema: ((version: string) => type_schema), migrations: Record<string, type_migration<any, any>>): Promise<type_sheet<any>>; function load_versioned(path: string, get_schema: ((version: string) => type_schema), migrations: Record<string, type_migration<any, any>>): Promise<type_sheet<any>>;
} }
declare namespace lib_plankton.translate {
/**
* @author fenris
*/
type type_package_meta = {
identifier: string;
name?: string;
};
/**
* @author fenris
*/
type type_package = {
meta: type_package_meta;
tree: {
[id: string]: string;
};
};
/**
* @desc the level of verbosity, specifiying how much output the system shall provide about its actions
* @author fenris
*/
var _verbosity: int;
/**
* @desc moves a language to the top of the order, making it the primary one
* @author fenris
*/
function promote(identifier: string): void;
/**
* @desc adds a package to the sytem
* @author fenris
*/
function add(package_: type_package): void;
/**
* @desc integrates a package to the system, i.e. creates a new one if none existed so far or merges with an existing one
* @author fenris
*/
function feed(package_: type_package): void;
/**
* @desc tries to retrieve a translation for a specific package identifier
* @author fenris
*/
function fetch(identifier: string, path: string, args?: {
[id: string]: string;
}): lib_plankton.pod.type_pod<string>;
/**
* @desc retrieves a string by going through the order and trying to fetch it for the current entry
* @author fenris
* @todo rename to "get"
*/
function get_new(path: string, { "args": args, "preferred_language": preferred_language, "fallback": fallback, }?: {
args?: Record<string, string>;
preferred_language?: (null | string);
fallback?: string;
}): string;
/**
* @desc retrieves a string by going through the order and trying to fetch it for the current entry
* @author fenris
* @deprecated use "get_new"
* @todo remove
*/
function get(path: string, args?: {
[id: string]: string;
}, fallback?: string): string;
/**
* @author fenris
*/
function list(): Array<type_package_meta>;
/**
* @author fenris
* @todo get rid of this; it's currenly needed only for the cdh-internal lib_completion
*/
function paths(): Array<string>;
/**
* @author fenris
*/
function initialize({ "logprefix": logprefix, "verbosity": verbosity, "packages": packages, "order": order, "autopromote": autopromote, }?: {
logprefix?: string;
verbosity?: int;
packages?: Array<type_package>;
order?: Array<string>;
autopromote?: boolean;
}): Promise<void>;
}
declare namespace lib_plankton.translate {
/**
* @author fenris
*/
function iso_639_1_to_iso_639_2(iso6391: string): string;
/**
* @author fenris
*/
function stance(str: string): string;
}
declare namespace lib_plankton.args { declare namespace lib_plankton.args {
/** /**
*/ */

View file

@ -3648,16 +3648,10 @@ var lib_plankton;
return (pit > reference); return (pit > reference);
} }
pit_1.is_after = is_after; pit_1.is_after = is_after;
/**
*/
function is_equal_or_after(pit, reference) {
return (pit >= reference);
}
pit_1.is_equal_or_after = is_equal_or_after;
/** /**
*/ */
function is_between(pit, reference_left, reference_right) { function is_between(pit, reference_left, reference_right) {
return (is_equal_or_after(pit, reference_left) return (is_after(pit, reference_left)
&& &&
is_before(pit, reference_right)); is_before(pit, reference_right));
} }
@ -8060,370 +8054,6 @@ var lib_plankton;
})(conf = lib_plankton.conf || (lib_plankton.conf = {})); })(conf = lib_plankton.conf || (lib_plankton.conf = {}));
})(lib_plankton || (lib_plankton = {})); })(lib_plankton || (lib_plankton = {}));
/* /*
This file is part of »bacterio-plankton:translate«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:translate« 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.
»bacterio-plankton:translate« 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 »bacterio-plankton:translate«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var translate;
(function (translate) {
/**
* @desc contains the sets of strings
* @author fenris
*/
var _packages = {};
/**
* @desc specifies in which order the languages shall be queried; if getting a string from language #0 fails, the
* system tries to get it from language #1, and so on
* @author fenris
*/
var _order = [];
/**
* @desc whether to automatically promote the language of a newly added package
* @author fenris
*/
var _autopromote = false;
/**
* @desc the level of verbosity, specifiying how much output the system shall provide about its actions
* @author fenris
*/
translate._verbosity = 1;
/**
* @desc which initial string to use for log-outputs
* @author fenris
*/
var _logprefix = "[lib_translate]";
/**
* @desc moves a language to the top of the order, making it the primary one
* @author fenris
*/
function promote(identifier) {
if (Object.keys(_packages).indexOf(identifier) < 0) {
if (translate._verbosity >= 1) {
console.warn(`${_logprefix} package '${identifier}' doesn't exist yet`);
}
}
let position = _order.indexOf(identifier);
if (position >= 0) {
if (translate._verbosity >= 2) {
console.info(`${_logprefix} '${identifier}' already in order; will promote it`);
}
_order.splice(position, 1);
}
_order.unshift(identifier);
if (translate._verbosity >= 2) {
console.info(`${_logprefix} order is now ${_order.toString()}`);
}
}
translate.promote = promote;
/**
* @desc adds a package to the sytem
* @author fenris
*/
function add(package_) {
let identifier = package_.meta.identifier;
if (identifier in _packages) {
if (translate._verbosity >= 1) {
console.warn(`${_logprefix} package '${identifier}' has already been added; will overwrite`);
}
}
else {
if (translate._verbosity >= 2) {
console.log(`${_logprefix} got package '${identifier}'`);
}
}
_packages[identifier] = package_;
if (_autopromote) {
promote(identifier);
}
}
translate.add = add;
/**
* @desc integrates a package to the system, i.e. creates a new one if none existed so far or merges with an existing one
* @author fenris
*/
function feed(package_) {
let identifier = package_.meta.identifier;
if (identifier in _packages) {
lib_plankton.object.patch(_packages[identifier].tree, package_.tree, {
"deep": true,
});
}
else {
if (translate._verbosity >= 2) {
console.info(`${_logprefix} package '${identifier}' didn't exist so far; will create it now`);
}
add(package_);
}
}
translate.feed = feed;
/**
* @desc tries to retrieve a translation for a specific package identifier
* @author fenris
*/
function fetch(identifier, path, args = {}) {
if (!(identifier in _packages)) {
if (translate._verbosity >= 1) {
console.warn(`${_logprefix} no package '${identifier}'`);
}
return (lib_plankton.pod.make_empty());
}
else {
// let str : string = lib_plankton.object.path_read<string>(_packages[identifier].tree, path);
let str = _packages[identifier].tree[path];
if (str == undefined) {
if (translate._verbosity >= 1) {
console.warn(`${_logprefix} string '${path}' missing in package '${identifier}'`);
}
return (lib_plankton.pod.make_empty());
}
else {
// resolve references
{
let regexp_reference = new RegExp("#\\(([\\w\\.]*)(?:\\?(\\w+)=(\\w+)(?:&(\\w+)=(\\w+))*)?\\)");
while (true) {
let matching = regexp_reference.exec(str);
if (matching != null) {
let path_ = matching[1];
let args_ = {};
if (translate._verbosity >= 2) {
// console.info(`${_logprefix} found reference to '${path_}' with args ${JSON.stringify(args_)}`);
console.info(`${_logprefix} found reference to '${path_}'`);
}
// parse args
{
for (let index = 2; index <= matching.length - 1; index += 2) {
let id = matching[index + 0];
let value = matching[index + 1];
if (id != undefined) {
args_[id] = value;
}
}
}
// fetch referenced string
{
let result_ = fetch(identifier, path_, args_);
if (lib_plankton.pod.is_filled(result_)) {
let front = str.slice(0, matching.index);
let back = str.slice(matching.index + matching[0].length);
str = (front + lib_plankton.pod.cull(result_) + back);
}
else {
return (lib_plankton.pod.make_empty());
break;
}
}
}
else {
break;
}
}
}
// insert arguments
{
str = lib_plankton.string.coin(str, args);
}
return (lib_plankton.pod.make_filled(str));
}
}
}
translate.fetch = fetch;
/**
* @desc retrieves a string by going through the order and trying to fetch it for the current entry
* @author fenris
* @todo rename to "get"
*/
function get_new(path, { "args": args = {}, "preferred_language": preferred_language = null, "fallback": fallback = null, } = {}) {
if (fallback == null) {
fallback = `{${path}}`;
}
if (translate._verbosity >= 2) {
console.info(`${_logprefix} getting translation for string '${path}' with arguments ${JSON.stringify(args)}`);
}
let result = lib_plankton.pod.make_empty();
const order = ((preferred_language === null)
?
_order
:
([preferred_language].concat(_order.filter(x => (x !== preferred_language)))));
const found = order.some(identifier => {
if (translate._verbosity >= 2) {
console.info(`${_logprefix} trying package '${identifier}' …`);
}
const result_ = fetch(identifier, path, args);
if (lib_plankton.pod.is_filled(result_)) {
result = result_;
return true;
}
else {
return false;
}
});
if (found) {
const str = lib_plankton.pod.cull(result);
if (translate._verbosity >= 3) {
console.info(`${_logprefix} found translation: '${str}'`);
}
return str;
}
else {
const str = fallback;
if (translate._verbosity >= 1) {
console.warn(`${_logprefix} no package provides a translation for string '${path}'; will use the fallback translation '${str}'`);
}
return str;
}
}
translate.get_new = get_new;
/**
* @desc retrieves a string by going through the order and trying to fetch it for the current entry
* @author fenris
* @deprecated use "get_new"
* @todo remove
*/
function get(path, args = {}, fallback = null) {
return get_new(path, {
"args": args,
"fallback": fallback,
});
}
translate.get = get;
/**
* @author fenris
*/
function list() {
return lib_plankton.object.to_array(_packages).map(x => x.value.meta);
}
translate.list = list;
/**
* @author fenris
* @todo get rid of this; it's currenly needed only for the cdh-internal lib_completion
*/
function paths() {
return lib_plankton.object.keys(lib_plankton.object.flatten(_packages[_order[0]].tree));
}
translate.paths = paths;
/**
* @author fenris
*/
function initialize({ "logprefix": logprefix = undefined, "verbosity": verbosity = undefined, "packages": packages = [], "order": order = undefined, "autopromote": autopromote = undefined, } = {}) {
return (Promise.resolve(undefined)
// set variables
.then(_ => {
if (logprefix != undefined)
_logprefix = logprefix;
if (verbosity != undefined)
translate._verbosity = verbosity;
// _packages = {};
if (order != undefined)
_order = order;
if (autopromote != undefined)
_autopromote = autopromote;
return Promise.resolve(undefined);
})
// feed
.then(_ => {
packages.forEach(feed);
return Promise.resolve(undefined);
}));
}
translate.initialize = initialize;
})(translate = lib_plankton.translate || (lib_plankton.translate = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:translate«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:translate« 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.
»bacterio-plankton:translate« 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 »bacterio-plankton:translate«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var translate;
(function (translate) {
/**
* @author fenris
*/
function iso_639_1_to_iso_639_2(iso6391) {
let mapping = {
"af": "afr",
"ar": "ara",
"bg": "bul",
"cs": "ces",
"da": "dan",
"de": "deu",
"el": "ell",
"en": "eng",
"eo": "epo",
"es": "esp",
"fa": "fas",
"fi": "fin",
"fr": "fra",
"hi": "hin",
"hr": "hrv",
"hu": "hun",
"is": "isl",
"it": "ita",
"ja": "jpn",
"ko": "kor",
"nb": "nob",
"nl": "nld",
"nn": "nno",
"pt": "por",
"pl": "pol",
"ro": "ron",
"ru": "rus",
"sk": "slk",
"sv": "swe",
"zh": "zho",
};
return mapping[iso6391];
}
translate.iso_639_1_to_iso_639_2 = iso_639_1_to_iso_639_2;
/**
* @author fenris
*/
function stance(str) {
let regexp = new RegExp("^translate:(.*)$");
let matching = regexp.exec(str);
if (matching != null) {
return translate.get(matching[1]);
}
else {
return str;
}
}
translate.stance = stance;
})(translate = lib_plankton.translate || (lib_plankton.translate = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:args«. This file is part of »bacterio-plankton:args«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'

View file

@ -18,6 +18,9 @@ along with »munin«. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* @todo versioning
*/
namespace _munin.conf namespace _munin.conf
{ {
@ -40,9 +43,154 @@ namespace _munin.conf
/** /**
* @todo rename to "type_conf"
*/ */
type type_conf_v6 = { type type_conf_v1 = {
sources : Array<
(
{
kind : "kalender_digital";
data : {
path : string;
filtration : {
category_blacklist : Array<string>;
title_blacklist : Array<string>;
};
};
}
)
>;
targets : Array<
(
{
kind : "telegram_bot";
data : {
bot_token : string;
chat_id : int;
/**
* in hours
*/
interval : Array<int>;
}
}
)
>;
frequency : float;
labels : {
head : string;
title : string;
time : string;
location : string;
};
};
/**
*/
type type_conf_v2 = {
sources : Array<
(
{
kind : "ical_feed";
data : {
url : string;
filtration : {
category_blacklist : Array<string>;
title_blacklist : Array<string>;
}
}
}
)
>;
targets : Array<
(
{
kind : "telegram_bot";
data : {
bot_token : string;
chat_id : int;
/**
* in hours
*/
reminders : Array<int>;
}
}
)
>;
settings : {
interval : float;
};
labels : {
head : string;
title : string;
time : string;
location : string;
};
};
/**
*/
type type_conf_v3 = {
sources : Array<
(
{
kind : "ical_feed";
data : {
url : string;
filtration : {
category_blacklist : Array<string>;
title_blacklist : Array<string>;
}
}
}
)
>;
targets : Array<
(
{
kind : "email";
data : {
smtp_host : string;
smtp_port : int;
smtp_username : string;
smtp_password : string;
sender : string;
receivers : Array<string>;
/**
* in hours
*/
reminders : Array<int>;
}
}
|
{
kind : "telegram_bot";
data : {
bot_token : string;
chat_id : int;
/**
* in hours
*/
reminders : Array<int>;
}
}
)
>;
settings : {
interval : float;
};
labels : {
head : string;
title : string;
time : string;
location : string;
};
};
/**
*/
type type_conf_v4 = {
sources : Array< sources : Array<
( (
{ {
@ -71,11 +219,10 @@ namespace _munin.conf
sender : string; sender : string;
receivers : Array<string>; receivers : Array<string>;
hide_tags : boolean; hide_tags : boolean;
reminders : Array<type_reminder_raw>; /**
language : string; * in hours
strings : { */
notification_head : string; reminders : Array<int>;
};
} }
} }
| |
@ -85,11 +232,10 @@ namespace _munin.conf
bot_token : string; bot_token : string;
chat_id : int; chat_id : int;
hide_tags : boolean; hide_tags : boolean;
reminders : Array<type_reminder_raw>; /**
language : string; * in hours
strings : { */
notification_head : string; reminders : Array<int>;
};
} }
} }
) )
@ -97,23 +243,371 @@ namespace _munin.conf
settings : { settings : {
interval : float; interval : float;
}; };
labels : {
head : string;
title : string;
time : string;
location : string;
};
}; };
/** /**
*/ */
const current_version : string = "6"; type type_conf_v5 = {
sources : Array<
(
{
kind : "ical_feed";
data : {
url : string;
filtration : {
category_whitelist : (null | Array<string>);
category_blacklist : (null | Array<string>);
title_whitelist : (null | Array<string>);
title_blacklist : (null | Array<string>);
}
}
}
)
>;
targets : Array<
(
{
kind : "email";
data : {
smtp_host : string;
smtp_port : int;
smtp_username : string;
smtp_password : string;
sender : string;
receivers : Array<string>;
hide_tags : boolean;
/**
* in hours
*/
reminders : Array<type_reminder_raw>;
}
}
|
{
kind : "telegram_bot";
data : {
bot_token : string;
chat_id : int;
hide_tags : boolean;
/**
* in hours
*/
reminders : Array<type_reminder_raw>;
}
}
)
>;
settings : {
interval : float;
};
labels : {
head : string;
title : string;
time : string;
location : string;
events : string;
};
};
/** /**
* @todo remove
*/ */
export type type_conf = type_conf_v6; export type type_conf = type_conf_v5;
/**
*/
function convert_from_v1(
conf_v1 : type_conf_v1
) : type_conf_v2
{
return {
"sources": conf_v1.sources.map(
source => {
switch (source.kind) {
case "kalender_digital": {
return {
"kind": "ical_feed",
"data": {
"url": lib_plankton.url.encode(
{
"scheme": "https",
"host": "export.kalender.digital",
"username": null,
"password": null,
"port": null,
"path": ("/ics/" + source.data.path + ".ics"),
"query": "past_months=0&future_months=1",
"hash": null,
}
),
"filtration": source.data.filtration,
}
};
break;
}
default: {
// return source;
throw (new Error("unhandled source kind: " + source.kind));
break;
}
}
}
),
"targets": conf_v1.targets.map(
target => {
switch (target.kind) {
case "telegram_bot": {
return {
"kind": "telegram_bot",
"data": {
"bot_token": target.data.bot_token,
"chat_id": target.data.chat_id,
"reminders": target.data.interval,
},
};
break;
}
default: {
// return target;
throw (new Error("unhandled target kind: " + target.kind));
break;
}
}
}
),
"settings": {
"interval": conf_v1.frequency,
},
"labels": conf_v1.labels,
};
}
/**
*/
function convert_from_v2(
conf_v2 : type_conf_v2
) : type_conf_v3
{
return conf_v2;
}
/**
*/
function convert_from_v3(
conf_v3 : type_conf_v3
) : type_conf_v4
{
return {
"sources": conf_v3.sources.map(
source => {
switch (source.kind) {
case "ical_feed": {
return {
"kind": "ical_feed",
"data": {
"url": source.data.url,
"filtration": {
"category_whitelist": null,
"category_blacklist": source.data.filtration.category_blacklist,
"title_whitelist": null,
"title_blacklist": source.data.filtration.category_blacklist,
},
}
};
break;
}
default: {
// return source;
throw (new Error("unhandled source kind: " + source.kind));
break;
}
}
}
),
"targets": conf_v3.targets.map(
target => {
switch (target.kind) {
case "email": {
return {
"kind": "email",
"data": {
"smtp_host": target.data.smtp_host,
"smtp_port": target.data.smtp_port,
"smtp_username": target.data.smtp_username,
"smtp_password": target.data.smtp_password,
"sender": target.data.sender,
"receivers": target.data.receivers,
"hide_tags": false,
"reminders": target.data.reminders,
},
};
break;
}
case "telegram_bot": {
return {
"kind": "telegram_bot",
"data": {
"bot_token": target.data.bot_token,
"chat_id": target.data.chat_id,
"hide_tags": false,
"reminders": target.data.reminders,
},
};
break;
}
default: {
// return target;
throw (new Error("unhandled target kind: " + String(target)));
break;
}
}
}
),
"settings": conf_v3.settings,
"labels": conf_v3.labels,
};
}
/**
*/
function convert_from_v4(
conf_v4 : type_conf_v4
) : type_conf_v5
{
const map_reminder = hours => ({
"frequency": "hourly",
"offset": 0,
"from": hours,
"to": (hours + 1),
} as type_reminder_raw);
return {
"sources": conf_v4.sources,
"targets": conf_v4.targets.map(
target => {
switch (target.kind) {
case "email": {
return {
"kind": "email",
"data": {
"smtp_host": target.data.smtp_host,
"smtp_port": target.data.smtp_port,
"smtp_username": target.data.smtp_username,
"smtp_password": target.data.smtp_password,
"sender": target.data.sender,
"receivers": target.data.receivers,
"hide_tags": target.data.hide_tags,
"reminders": target.data.reminders.map(map_reminder),
},
};
break;
}
case "telegram_bot": {
return {
"kind": "telegram_bot",
"data": {
"bot_token": target.data.bot_token,
"chat_id": target.data.chat_id,
"hide_tags": target.data.hide_tags,
"reminders": target.data.reminders.map(map_reminder),
},
};
break;
}
default: {
// return target;
throw (new Error("unhandled target kind: " + String(target)));
break;
}
}
}
),
"settings": conf_v4.settings,
"labels": Object.assign({"events": "Termine"}, conf_v4.labels),
};
}
/**
*/
function schema_source_kalender_digital(
version : string
) : lib_plankton.conf.type_schema
{
return {
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["kalender_digital"]
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"path": {
"nullable": false,
"type": "string"
},
"filtration": {
"nullable": false,
"type": "object",
"properties": {
"category_blacklist": {
"nullable": true,
"type": "array",
"items": {
"nullable": false,
"type": "string",
},
"default": [],
},
"title_blacklist": {
"nullable": true,
"type": "array",
"items": {
"nullable": false,
"type": "string",
},
"default": [],
},
},
"additionalProperties": false,
"required": [
],
"default": {}
},
},
"additionalProperties": false,
"required": [
"path",
]
}
},
"additionalProperties": false,
"required": [
"kind",
"data",
]
};
}
/** /**
*/ */
function schema_source_ical_feed( function schema_source_ical_feed(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
@ -197,25 +691,60 @@ namespace _munin.conf
/** /**
*/ */
function schema_sources( function schema_sources(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
switch (version) {
case "1": {
return { return {
"nullable": false, "nullable": false,
"type": "array", "type": "array",
"items": { "items": {
"nullable": false, "nullable": false,
"anyOf": [ "anyOf": [
schema_source_ical_feed(), schema_source_kalender_digital(version),
], ],
} }
}; };
break;
}
default:
case "2": {
return {
"nullable": false,
"type": "array",
"items": {
"nullable": false,
"anyOf": [
schema_source_ical_feed(version),
],
}
};
break;
}
}
} }
/** /**
*/ */
function schema_reminder( function schema_reminder(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{
switch (version)
{
case "1":
case "2":
case "3":
case "4":
{
return {
"type": "integer",
};
}
case "5":
default:
{ {
return { return {
"nullable": false, "nullable": false,
@ -252,13 +781,29 @@ namespace _munin.conf
"to", "to",
] ]
}; };
break;
}
}
} }
/** /**
*/ */
function default_reminder( function default_reminder(
version : string
) : any ) : any
{
switch (version)
{
case "1":
case "2":
case "3":
case "4":
{
return [24];
}
case "5":
default:
{ {
return [ return [
{ {
@ -267,12 +812,16 @@ namespace _munin.conf
"to": 25 "to": 25
} }
]; ];
break;
}
}
} }
/** /**
*/ */
function schema_target_email( function schema_target_email(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
@ -325,28 +874,8 @@ namespace _munin.conf
"reminders": { "reminders": {
"nullable": false, "nullable": false,
"type": "array", "type": "array",
"items": schema_reminder(), "items": schema_reminder(version),
"default": default_reminder(), "default": default_reminder(version),
},
"language": {
"nullable": false,
"type": "string",
"default": "deu"
},
"strings": {
"nullable": false,
"type": "object",
"properties": {
"notification_head": {
"nullable": false,
"type": "string",
"default": "[{{core}}] {{extra}}"
},
},
"additionalProperties": false,
"required": [
],
"default": {},
}, },
}, },
"additionalProperties": false, "additionalProperties": false,
@ -371,6 +900,7 @@ namespace _munin.conf
/** /**
*/ */
function schema_target_telegram_bot( function schema_target_telegram_bot(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
@ -402,28 +932,8 @@ namespace _munin.conf
"reminders": { "reminders": {
"nullable": false, "nullable": false,
"type": "array", "type": "array",
"items": schema_reminder(), "items": schema_reminder(version),
"default": default_reminder(), "default": default_reminder(version),
},
"language": {
"nullable": false,
"type": "string",
"default": "deu"
},
"strings": {
"nullable": false,
"type": "object",
"properties": {
"notification_head": {
"nullable": false,
"type": "string",
"default": "[{{core}}] {{extra}}"
},
},
"additionalProperties": false,
"required": [
],
"default": {},
}, },
}, },
"additionalProperties": false, "additionalProperties": false,
@ -445,6 +955,7 @@ namespace _munin.conf
/** /**
*/ */
function schema_targets( function schema_targets(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
@ -452,10 +963,28 @@ namespace _munin.conf
"type": "array", "type": "array",
"items": { "items": {
"nullable": false, "nullable": false,
"anyOf": [ "anyOf": (() => {
schema_target_email(), switch (version) {
schema_target_telegram_bot(), case "1":
] case "2":
{
return [
schema_target_telegram_bot(version),
];
break;
}
case "4":
case "5":
default:
{
return [
schema_target_email(version),
schema_target_telegram_bot(version),
];
break;
}
}
}) (),
} }
}; };
} }
@ -464,6 +993,7 @@ namespace _munin.conf
/** /**
*/ */
function schema_settings( function schema_settings(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
@ -487,16 +1017,68 @@ namespace _munin.conf
/** /**
*/ */
export function schema( function schema_labels(
version : string
) : lib_plankton.conf.type_schema ) : lib_plankton.conf.type_schema
{ {
return { return {
"nullable": false, "nullable": false,
"type": "object", "type": "object",
"properties": { "properties": {
"sources": schema_sources(), "head": {
"targets": schema_targets(), "nullable": false,
"settings": schema_settings(), "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"
},
"events": {
"nullable": false,
"type": "string",
"default": "Termine"
},
},
"additionalProperties": false,
"required": [
],
"default": {}
};
}
/**
*/
export function schema(
version : string = "5"
) : lib_plankton.conf.type_schema
{
switch (version) {
case "1": {
return {
"nullable": false,
"type": "object",
"properties": {
"sources": schema_sources(version),
"targets": schema_targets(version),
"frequency": {
"nullable": false,
"type": "number",
"default": 1.0,
},
"labels": schema_labels(version),
}, },
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [
@ -504,23 +1086,29 @@ namespace _munin.conf
"targets", "targets",
], ],
}; };
break;
} }
case "2":
case "3":
/** case "4":
*/ case "5":
export function schema_extended(
) : lib_plankton.conf.type_schema
{ {
return { return {
"nullable": false,
"type": "object", "type": "object",
"properties": { "properties": {
"version": { "sources": schema_sources(version),
"nullable": false, "targets": schema_targets(version),
"type": "string", "settings": schema_settings(version),
"enum": [current_version], "labels": schema_labels(version),
}, },
"content": _munin.conf.schema(), "additionalProperties": false,
"required": [
"sources",
"targets",
],
};
break;
} }
} }
} }
@ -536,17 +1124,49 @@ namespace _munin.conf
path, path,
schema, schema,
{ {
"v6": null, "1": {"target": "2", "function": convert_from_v1},
"2": {"target": "3", "function": convert_from_v2},
"3": {"target": "4", "function": convert_from_v3},
"4": {"target": "5", "function": convert_from_v4},
"5": null,
} }
); );
if (conf_raw.version !== current_version)
{ /*
throw (new Error("conf expected in version '" + current_version + "'")); switch (conf_raw.version) {
case "1": {
const conf_v1 : type_conf_v1 = (conf_raw.content as type_conf_v1);
const conf_v2 : type_conf_v2 = convert_from_v1(conf_v1);
const conf_v3 : type_conf_v3 = convert_from_v2(conf_v2);
const conf_v4 : type_conf_v4 = convert_from_v3(conf_v3);
return conf_v4;
break;
} }
else case "2": {
{ const conf_v2 : type_conf_v2 = (conf_raw.content as type_conf_v2);
return (conf_raw.content as type_conf_v6); const conf_v3 : type_conf_v3 = convert_from_v2(conf_v2);
const conf_v4 : type_conf_v4 = convert_from_v3(conf_v3);
return conf_v4;
break;
} }
case "3": {
const conf_v3 : type_conf_v3 = (conf_raw.content as type_conf_v3);
const conf_v4 : type_conf_v4 = convert_from_v3(conf_v3);
return conf_v4;
break;
}
case "4": {
const conf_v4 : type_conf_v4 = (conf_raw.content as type_conf_v4);
return conf_v4;
break;
}
default: {
throw (new Error("invalid version: " + conf_raw.version));
break;
}
}
*/
return (conf_raw.content as type_conf_v5);
} }
} }

View file

@ -203,7 +203,7 @@ namespace _munin.logic
{ {
try try
{ {
await target.send(events); await target.send(conf.labels, events);
} }
catch (error) catch (error)
{ {

View file

@ -27,31 +27,6 @@ namespace _munin
args_raw : Array<string> args_raw : Array<string>
): Promise<void> ): Promise<void>
{ {
// init
const language_codes : Array<string> = [
"deu",
"eng",
];
const packages = await Promise.all<lib_plankton.translate.type_package>(
language_codes
.map<Promise<lib_plankton.translate.type_package>>(
code => (
Promise.resolve<string>(code)
.then<string>(code => Promise.resolve(lib_plankton.string.coin("data/localization/{{code}}.json", {"code": code})))
.then<string>(path => lib_plankton.file.read(path))
.then<lib_plankton.translate.type_package>(content => Promise.resolve(lib_plankton.json.decode(content)))
)
)
);
await lib_plankton.translate.initialize(
{
"verbosity": 1,
"packages": packages,
"order": language_codes,
"autopromote": false,
}
);
// args // args
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler( const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler(
{ {
@ -60,12 +35,7 @@ namespace _munin
"type": lib_plankton.args.enum_type.string, "type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": "run", "default": "run",
"info": lib_plankton.string.coin( "info": "what to do : test | run",
"{{core}} : test | run",
{
"core": lib_plankton.translate.get("args.action.description"),
}
),
"name": "action", "name": "action",
}), }),
"conf_path": lib_plankton.args.class_argument.volatile({ "conf_path": lib_plankton.args.class_argument.volatile({
@ -74,16 +44,16 @@ namespace _munin
"type": lib_plankton.args.enum_type.string, "type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": "munin.json", "default": "munin.json",
"info": lib_plankton.translate.get("args.conf_path.description"), "info": "path to configuration file",
"name": "conf-path", "name": "conf-path",
}), }),
"conf_schema": lib_plankton.args.class_argument.volatile({ "conf_schema": lib_plankton.args.class_argument.volatile({
"indicators_long": ["conf-schema"], "indicators_long": ["conf-schema"],
"indicators_short": ["s"], "indicators_short": ["s"],
"type": lib_plankton.args.enum_type.boolean, "type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": false, "default": "",
"info": lib_plankton.translate.get("args.conf_schema.description"), "info": "only print the configuration schema in a specific version (latest version via argument '_')",
"name": "conf-schema", "name": "conf-schema",
}), }),
"conf_expose": lib_plankton.args.class_argument.volatile({ "conf_expose": lib_plankton.args.class_argument.volatile({
@ -92,7 +62,7 @@ namespace _munin
"type": lib_plankton.args.enum_type.boolean, "type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": false, "default": false,
"info": lib_plankton.translate.get("args.conf_expose.description"), "info": "whether to expose the full configuration",
"name": "conf-expose", "name": "conf-expose",
}), }),
"single_run": lib_plankton.args.class_argument.volatile({ "single_run": lib_plankton.args.class_argument.volatile({
@ -101,7 +71,7 @@ namespace _munin
"type": lib_plankton.args.enum_type.boolean, "type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": false, "default": false,
"info": lib_plankton.translate.get("args.single_run.description"), "info": "whether to only execute one iteration at run",
"name": "single-run", "name": "single-run",
}), }),
"verbosity": lib_plankton.args.class_argument.volatile({ "verbosity": lib_plankton.args.class_argument.volatile({
@ -119,7 +89,7 @@ namespace _munin
"type": lib_plankton.args.enum_type.boolean, "type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": false, "default": false,
"info": lib_plankton.translate.get("args.dry_run.description"), "info": "whether to skip the sending of reminders (logs will be written)",
"name": "dry-run", "name": "dry-run",
}), }),
"help": lib_plankton.args.class_argument.volatile({ "help": lib_plankton.args.class_argument.volatile({
@ -128,7 +98,7 @@ namespace _munin
"type": lib_plankton.args.enum_type.boolean, "type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace, "mode": lib_plankton.args.enum_mode.replace,
"default": false, "default": false,
"info": lib_plankton.translate.get("args.help.description"), "info": "alias for action 'help'",
"name": "help", "name": "help",
}), }),
} }
@ -143,17 +113,17 @@ namespace _munin
arg_handler.generate_help( arg_handler.generate_help(
{ {
"programname": "munin", "programname": "munin",
"description": lib_plankton.translate.get("help.description"), "description": "sends reminders about upcoming events",
"executable": "node build/munin", "executable": "node build/munin",
} }
) )
); );
} }
else { else {
if (args.conf_schema) { if (args.conf_schema !== "") {
process.stdout.write( process.stdout.write(
lib_plankton.json.encode( lib_plankton.json.encode(
_munin.conf.schema_extended(), _munin.conf.schema((args.conf_schema === "_") ? undefined : args.conf_schema),
{ {
"formatted": true, "formatted": true,
} }

View file

@ -32,46 +32,21 @@ namespace _munin.targets.email
receivers : Array<string>; receivers : Array<string>;
hide_tags : boolean; hide_tags : boolean;
reminders : Array<_munin.type_reminder>; reminders : Array<_munin.type_reminder>;
language : string;
strings : {
notification_head : string;
}; };
};
/**
*/
function get_translation(
parameters : type_parameters,
path : string
) : string
{
return lib_plankton.translate.get_new(
path,
{
"preferred_language": parameters.language,
}
);
}
/** /**
*/ */
function summarize_event( function summarize_event(
parameters : type_parameters, parameters : type_parameters,
labels : _munin.type_labels,
event : _munin.type_event event : _munin.type_event
) : string ) : string
{ {
return lib_plankton.string.coin( return lib_plankton.string.coin(
"[{{head}}] {{date}} : {{macro_tags}}{{title}}", "[{{head}}] {{date}} : {{macro_tags}}{{title}}",
{ {
"head": lib_plankton.string.coin( "head": labels.head,
"{{event}}-{{reminder}}",
{
"event": get_translation(parameters, "core.event.event").toLowerCase(),
"reminder": get_translation(parameters, "core.reminder.reminder").toLowerCase(),
}
),
"date": lib_plankton.pit.date_format( "date": lib_plankton.pit.date_format(
event.begin.date event.begin.date
), ),
@ -92,13 +67,14 @@ namespace _munin.targets.email
*/ */
function render_event( function render_event(
parameters : type_parameters, parameters : type_parameters,
labels : _munin.type_labels,
event : _munin.type_event event : _munin.type_event
) : string ) : string
{ {
return lib_plankton.string.coin( return lib_plankton.string.coin(
"{{title_label}} | {{macro_tags}}{{title_value}}\n{{time_label}} | {{time_value}}{{macro_location}}{{macro_description}}", "{{title_label}} | {{macro_tags}}{{title_value}}\n{{time_label}} | {{time_value}}{{macro_location}}{{macro_description}}",
{ {
"title_label": get_translation(parameters, "core.event.title.short").toUpperCase(), "title_label": labels.title.toUpperCase(),
"macro_tags": ( "macro_tags": (
(parameters.hide_tags || (event.tags === null)) (parameters.hide_tags || (event.tags === null))
? ?
@ -107,7 +83,7 @@ namespace _munin.targets.email
(event.tags.map(tag => ("{" + tag + "}")).join(" ") + " ") (event.tags.map(tag => ("{" + tag + "}")).join(" ") + " ")
), ),
"title_value": event.title, "title_value": event.title,
"time_label": get_translation(parameters, "core.event.time.short").toUpperCase(), "time_label": labels.time.toUpperCase(),
"time_value": lib_plankton.pit.timespan_format( "time_value": lib_plankton.pit.timespan_format(
event.begin, event.begin,
event.end, event.end,
@ -123,7 +99,7 @@ namespace _munin.targets.email
lib_plankton.string.coin( lib_plankton.string.coin(
"\n{{location_label}} | {{location_value}}", "\n{{location_label}} | {{location_value}}",
{ {
"location_label": get_translation(parameters, "core.event.location.short").toUpperCase(), "location_label": labels.location.toUpperCase(),
"location_value": event.location, "location_value": event.location,
} }
) )
@ -150,6 +126,7 @@ namespace _munin.targets.email
*/ */
async function send( async function send(
parameters : type_parameters, parameters : type_parameters,
labels : _munin.type_labels,
events : Array<_munin.type_event> events : Array<_munin.type_event>
) : Promise<void> ) : Promise<void>
{ {
@ -165,32 +142,21 @@ namespace _munin.targets.email
( (
(events.length === 1) (events.length === 1)
? ?
summarize_event(parameters, events[0]) summarize_event(parameters, labels, events[0])
: :
lib_plankton.string.coin( lib_plankton.string.coin(
parameters.strings.notification_head, "[{{head}}] {{count}} {{events}}",
{
"core": lib_plankton.string.coin(
"{{event}}-{{reminder}}",
{
"event": get_translation(parameters, "core.event.event").toLowerCase(),
"reminder": get_translation(parameters, "core.reminder.reminder").toLowerCase(),
}
),
"extra": lib_plankton.string.coin(
"{{count}} {{events}}",
{ {
"head": labels.head,
"count": events.length.toFixed(0), "count": events.length.toFixed(0),
"events": get_translation(parameters, "core.event.events"), "events": labels.events,
}
),
} }
) )
), ),
( (
events events
.map(event => render_event(parameters, event)) .map(event => render_event(parameters, labels, event))
.join("\n\n--------\n\n") .join("\n\n---\n\n")
) )
); );
} }
@ -210,7 +176,7 @@ namespace _munin.targets.email
"receivers": parameters.receivers.join(","), "receivers": parameters.receivers.join(","),
} }
), ),
"send": (events) => send(parameters, events), "send": (labels, events) => send(parameters, labels, events),
}; };
} }

View file

@ -28,33 +28,14 @@ namespace _munin.targets.telegram_bot
chat_id : int; chat_id : int;
hide_tags : boolean; hide_tags : boolean;
reminders : Array<_munin.type_reminder>; reminders : Array<_munin.type_reminder>;
language : string;
strings : {
notification_head : string;
}; };
};
/**
*/
function get_translation(
parameters : type_parameters,
path : string
) : string
{
return lib_plankton.translate.get_new(
path,
{
"preferred_language": parameters.language,
}
);
}
/** /**
*/ */
function render_event( function render_event(
parameters : type_parameters, parameters : type_parameters,
labels : _munin.type_labels,
event : _munin.type_event event : _munin.type_event
) : string ) : string
{ {
@ -68,9 +49,9 @@ namespace _munin.targets.telegram_bot
: :
(event.tags.map(tag => ("{" + tag + "}")).join(" ") + " ") (event.tags.map(tag => ("{" + tag + "}")).join(" ") + " ")
), ),
"title_label": get_translation(parameters, "core.event.title.short").toUpperCase(), "title_label": labels.title.toUpperCase(),
"title_value": event.title, "title_value": event.title,
"time_label": get_translation(parameters, "core.event.time.short").toUpperCase(), "time_label": labels.time.toUpperCase(),
"time_value": lib_plankton.pit.timespan_format( "time_value": lib_plankton.pit.timespan_format(
event.begin, event.begin,
event.end, event.end,
@ -86,7 +67,7 @@ namespace _munin.targets.telegram_bot
lib_plankton.string.coin( lib_plankton.string.coin(
"\n{{location_label}} | {{location_value}}", "\n{{location_label}} | {{location_value}}",
{ {
"location_label": get_translation(parameters, "core.event.location.short").toUpperCase(), "location_label": labels.location.toUpperCase(),
"location_value": event.location, "location_value": event.location,
} }
) )
@ -112,6 +93,7 @@ namespace _munin.targets.telegram_bot
*/ */
async function send( async function send(
parameters : type_parameters, parameters : type_parameters,
labels : _munin.type_labels,
events : Array<_munin.type_event> events : Array<_munin.type_event>
) : Promise<void> ) : Promise<void>
{ {
@ -119,19 +101,10 @@ namespace _munin.targets.telegram_bot
parameters.bot_token, parameters.bot_token,
parameters.chat_id, parameters.chat_id,
lib_plankton.string.coin( lib_plankton.string.coin(
"*{{head}}*\n\n{{body}}", "*{{head_core}}{{head_extra}}*\n\n{{events}}",
{ {
"head": lib_plankton.string.coin( "head_core": labels.head,
parameters.strings.notification_head, "head_extra": (
{
"core": lib_plankton.string.coin(
"{{label_event}}-{{label_reminder}}",
{
"label_event": lib_plankton.string.capitalize(get_translation(parameters, "core.event.event")),
"label_reminder": lib_plankton.string.capitalize(get_translation(parameters, "core.reminder.reminder")),
}
),
"extra": (
(events.length <= 1) (events.length <= 1)
? ?
"" ""
@ -140,16 +113,14 @@ namespace _munin.targets.telegram_bot
" ({{count}} {{events}})", " ({{count}} {{events}})",
{ {
"count": events.length.toFixed(0), "count": events.length.toFixed(0),
"events": get_translation(parameters, "core.event.events"), "events": labels.events,
} }
) )
), ),
} "events": (
),
"body": (
events events
.map(event => render_event(parameters, event)) .map(event => render_event(parameters, labels, event))
.join("\n\n--------\n\n") .join("\n\n---\n\n")
), ),
} }
), ),
@ -174,7 +145,7 @@ namespace _munin.targets.telegram_bot
"chat_id": parameters.chat_id.toFixed(0), "chat_id": parameters.chat_id.toFixed(0),
} }
), ),
"send": (events) => send(parameters, events), "send": (labels, events) => send(parameters, labels, events),
}; };
} }

View file

@ -67,7 +67,7 @@ namespace _munin.test
type_output type_output
> >
> = await _munin.helpers.test.get_data<type_input, type_output>( > = await _munin.helpers.test.get_data<type_input, type_output>(
"data/test/reminder_check.testdata.json", "data/reminder_check.testdata.json",
{ {
} }
); );

View file

@ -31,6 +31,17 @@ namespace _munin
} }
/**
*/
export type type_labels = {
head : string;
title : string;
time : string;
location : string;
events : string;
};
/** /**
*/ */
export type type_event = { export type type_event = {
@ -72,7 +83,10 @@ namespace _munin
reminders : Array<type_reminder>; reminders : Array<type_reminder>;
show : (() => string); show : (() => string);
send : ( send : (
(events : Array<type_event>) (
labels : type_labels,
events : Array<type_event>
)
=> =>
Promise<void> Promise<void>
); );