munin/source/logic.ts
2025-06-27 20:07:18 +00:00

301 lines
6.1 KiB
TypeScript

/*
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.logic
{
/**
* @todo Tests schreiben
*/
function shall_remind(
event : _munin.type_event,
reminder : type_reminder,
{
"pit": pit = lib_plankton.pit.now(),
"interval": interval = 1,
} : {
pit ?: lib_plankton.pit.type_pit;
interval ?: int;
} = {
}
) : boolean
{
const window_from : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
pit,
0
);
const window_to : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
pit,
interval
);
const event_begin : lib_plankton.pit.type_pit = lib_plankton.pit.from_datetime(
event.begin
);
const reminder_time : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
event_begin,
(-reminder)
);
return lib_plankton.pit.is_between(
reminder_time,
window_from,
window_to
);
}
/**
*/
function frequency_anchor(
frequency : _munin.enum_frequency,
{
"pit": pit = lib_plankton.pit.now(),
} : {
pit ?: lib_plankton.pit.type_pit,
} = {
}
) : lib_plankton.pit.type_pit
{
switch (frequency)
{
case _munin.enum_frequency.hourly: return lib_plankton.pit.trunc_hour(pit);
case _munin.enum_frequency.daily: return lib_plankton.pit.trunc_day(pit);
case _munin.enum_frequency.weekly: return lib_plankton.pit.trunc_week(pit);
case _munin.enum_frequency.monthly: return lib_plankton.pit.trunc_month(pit);
default:
{
throw (new Error("unhandled frequency: " + frequency));
break;
}
}
}
/**
*/
export function reminder_due(
reminder : type_reminder_new,
{
"pit": pit = lib_plankton.pit.now(),
"interval": interval = 1,
} : {
pit ?: lib_plankton.pit.type_pit;
interval ?: int;
} = {
}
) : boolean
{
const anchor : lib_plankton.pit.type_pit = frequency_anchor(
reminder.frequency,
{"pit": pit}
);
// 0 ≤ ((p - a(p)) - o) < i
const x : float = (
(
(
lib_plankton.pit.to_unix_timestamp(pit)
-
lib_plankton.pit.to_unix_timestamp(anchor)
)
/
(60 * 60)
)
-
reminder.offset
);
return ((0 <= x) && (x < interval));
}
/**
*/
export function reminder_event_in_window(
reminder : type_reminder_new,
event : _munin.type_event,
{
"pit": pit = lib_plankton.pit.now(),
"interval": interval = 1,
} : {
pit ?: lib_plankton.pit.type_pit;
interval ?: int;
} = {
}
) : boolean
{
const anchor : lib_plankton.pit.type_pit = frequency_anchor(
reminder.frequency,
{"pit": pit}
);
const window_from : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
anchor,
reminder.from
);
const window_to : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
anchor,
reminder.to
);
const event_begin : lib_plankton.pit.type_pit = lib_plankton.pit.from_datetime(
event.begin
);
return lib_plankton.pit.is_between(
event_begin,
window_from,
window_to
);
}
/**
*/
function shall_remind_new(
reminder : type_reminder_new,
event : _munin.type_event,
{
"pit": pit = lib_plankton.pit.now(),
"interval": interval = 1,
} : {
pit ?: lib_plankton.pit.type_pit;
interval ?: int;
} = {
}
) : boolean
{
return (
reminder_due(
reminder,
{"pit": pit, "interval": interval}
)
&&
reminder_event_in_window(
reminder,
event,
{"pit": pit, "interval": interval}
)
);
}
/**
*/
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) {
for (const reminder_hours of target.reminders) {
for (const event of events) {
const remind : boolean = shall_remind(
event,
reminder_hours,
{
"pit": now,
"interval": conf.settings.interval,
}
);
if (! remind) {
// do nothing
}
else {
lib_plankton.log._info(
"munin.remind.do",
{
"details": {
"event": event,
"target": target.show(),
}
}
);
if (dry_run) {
// do nothing
}
else {
try {
await target.send(conf.labels, event);
}
catch (error) {
lib_plankton.log.error(
"munin.remind.error",
{
"details": {
"message": String(error),
}
}
);
}
}
}
}
}
}
}
/**
*/
export async function run(
conf : _munin.conf.type_conf,
{
"single_run": single_run = false,
"dry_run": dry_run = false,
} : {
single_run ?: boolean;
dry_run ?: boolean;
} = {
}
) : Promise<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": {
}
}
);
if (single_run) {
await run_iteration(conf, sources, targets, {"dry_run": dry_run});
}
else {
while (true) {
await run_iteration(conf, sources, targets, {"dry_run": dry_run});
await lib_plankton.call.sleep(conf.settings.interval * 60 * 60);
}
}
}
}