266 lines
5.6 KiB
TypeScript
266 lines
5.6 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
|
|
{
|
|
|
|
/**
|
|
*/
|
|
export function frequency_decode(
|
|
frequency_encoded : string
|
|
) : _munin.enum_frequency
|
|
{
|
|
switch (frequency_encoded)
|
|
{
|
|
case "hourly": return _munin.enum_frequency.hourly;
|
|
case "daily": return _munin.enum_frequency.daily;
|
|
case "weekly": return _munin.enum_frequency.weekly;
|
|
case "monthly": return _munin.enum_frequency.monthly;
|
|
default: {throw new Error("unhandled");}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
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_check(
|
|
reminder : type_reminder,
|
|
events_all : Array<_munin.type_event>,
|
|
{
|
|
"pit": pit = lib_plankton.pit.now(),
|
|
"interval": interval = 1,
|
|
}
|
|
: {
|
|
pit ?: lib_plankton.pit.type_pit;
|
|
interval ?: int;
|
|
}
|
|
= {
|
|
}
|
|
) : (null | Array<_munin.type_event>)
|
|
{
|
|
const anchor : lib_plankton.pit.type_pit = frequency_anchor(
|
|
reminder.frequency,
|
|
{"pit": pit}
|
|
);
|
|
const dueness_window_from : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
|
anchor,
|
|
(reminder.offset + 0)
|
|
);
|
|
const dueness_window_to : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
|
anchor,
|
|
(reminder.offset + interval)
|
|
);
|
|
const due : boolean = lib_plankton.pit.is_between(
|
|
pit,
|
|
dueness_window_from,
|
|
dueness_window_to
|
|
);
|
|
if (! due)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
const events_window_from : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
|
anchor,
|
|
reminder.from
|
|
);
|
|
const events_window_to : lib_plankton.pit.type_pit = lib_plankton.pit.shift_hour(
|
|
anchor,
|
|
reminder.to
|
|
);
|
|
const events : Array<_munin.type_event> = (
|
|
events_all
|
|
.filter(
|
|
(event) => lib_plankton.pit.is_between(
|
|
lib_plankton.pit.from_datetime(event.begin),
|
|
events_window_from,
|
|
events_window_to
|
|
)
|
|
)
|
|
);
|
|
return events;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
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_all : 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 of target.reminders)
|
|
{
|
|
const events : Array<_munin.type_event> = reminder_check(
|
|
reminder,
|
|
events_all,
|
|
{
|
|
"pit": now,
|
|
"interval": conf.settings.interval,
|
|
}
|
|
);
|
|
if (events === null)
|
|
{
|
|
lib_plankton.log._info(
|
|
"munin.run_iteration.reminder_not_due",
|
|
{
|
|
"details": {
|
|
"reminder": reminder,
|
|
}
|
|
}
|
|
);
|
|
}
|
|
else {
|
|
if (events.length <= 0)
|
|
{
|
|
lib_plankton.log._info(
|
|
"munin.run_iteration.no_matching_events",
|
|
{
|
|
"details": {
|
|
"reminder": reminder,
|
|
}
|
|
}
|
|
);
|
|
}
|
|
else {
|
|
lib_plankton.log._info(
|
|
"munin.run_iteration.remind",
|
|
{
|
|
"details": {
|
|
"reminder": reminder,
|
|
"events": events,
|
|
"target": target.show(),
|
|
}
|
|
}
|
|
);
|
|
if (dry_run)
|
|
{
|
|
// do nothing
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
await target.send(events);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|