feat: implement global autoresponder plugin

This commit is contained in:
Marek Miklewicz
2026-06-02 19:19:00 +02:00
commit 6f989af278
62 changed files with 2018 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
final class RuleValidator
{
/** @param array<string,mixed> $input @return array<string,mixed> */
public static function validate(array $input, Settings $settings): array
{
$subject = trim((string)($input['subject'] ?? ''));
if ($subject === '') {
throw new InvalidArgumentException('Temat jest wymagany.');
}
if (str_contains($subject, "\r") || str_contains($subject, "\n")) {
throw new InvalidArgumentException('Temat nie może zawierać nowych linii.');
}
if (strlen($subject) > $settings->maxSubjectBytes()) {
throw new InvalidArgumentException('Temat jest za długi.');
}
$body = str_replace(["\r\n", "\r", "\0"], ["\n", "\n", ''], (string)($input['body'] ?? ''));
$body = trim($body);
if ($body === '') {
throw new InvalidArgumentException('Treść wiadomości jest wymagana.');
}
if (strlen($body) > $settings->maxBodyBytes()) {
throw new InvalidArgumentException('Treść wiadomości jest za długa.');
}
$mode = $settings->scheduleMode();
$tz = new DateTimeZone($settings->timezone());
[$startTs, $startValue] = self::parseScheduleValue((string)($input['start_value'] ?? ''), $mode, $tz, true);
[$endTs, $endValue] = self::parseScheduleValue((string)($input['end_value'] ?? ''), $mode, $tz, false);
if ($endTs <= $startTs) {
throw new InvalidArgumentException('Data zakończenia musi być późniejsza niż data rozpoczęcia.');
}
return [
'subject' => $subject,
'body' => $body,
'schedule_mode' => $mode,
'timezone' => $settings->timezone(),
'start_value' => $startValue,
'end_value' => $endValue,
'start_ts' => $startTs,
'end_ts' => $endTs,
'enabled' => self::truthy($input['enabled'] ?? false),
'dynamic_scope' => $settings->dynamicMailboxScope(),
];
}
/** @return array{0:int,1:string} */
private static function parseScheduleValue(string $value, string $mode, DateTimeZone $tz, bool $start): array
{
$value = trim($value);
if ($mode === 'exact_hour') {
if (!preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:00$/', $value)) {
throw new InvalidArgumentException('Nieprawidłowa data lub godzina.');
}
$dt = DateTimeImmutable::createFromFormat('!Y-m-d\TH:i', $value, $tz);
if (!$dt) {
throw new InvalidArgumentException('Nieprawidłowa data lub godzina.');
}
return [$dt->getTimestamp(), $value];
}
if (!preg_match('/^([0-9]{4}-[0-9]{2}-[0-9]{2})\|(morning|afternoon|evening)$/', $value, $m)) {
throw new InvalidArgumentException('Nieprawidłowa data lub pora dnia.');
}
$hour = ['morning' => 8, 'afternoon' => 13, 'evening' => 18][$m[2]];
if (!$start && $m[2] === 'evening') {
$hour = 23;
}
$dt = DateTimeImmutable::createFromFormat('!Y-m-d H:i', $m[1] . ' ' . sprintf('%02d:00', $hour), $tz);
if (!$dt) {
throw new InvalidArgumentException('Nieprawidłowa data lub pora dnia.');
}
return [$dt->getTimestamp(), $value];
}
private static function truthy(mixed $value): bool
{
return in_array(strtolower((string)$value), ['1', 'true', 'yes', 'on'], true);
}
}