diff --git a/includes/class-settings.php b/includes/class-settings.php index 6a340dc..dd3952b 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -10,6 +10,11 @@ final class Settings public const OPT_SITE_TITLE = 'cf7stg_site_title'; public const OPT_LAST_TEST = 'cf7stg_last_test'; + public const OPT_DRY_RUN_DROP = 'cf7stg_dry_run_drop'; + public const OPT_DROP_THRESHOLD = 'cf7stg_drop_score_threshold'; + public const OPT_HARD_DROP_ENABLED = 'cf7stg_hard_drop_enabled'; + public const OPT_DROP_COUNTERS = 'cf7stg_drop_counters'; + public static function get_bot_token(): string { if (defined('SPAM2TG_BOT_TOKEN') && SPAM2TG_BOT_TOKEN !== '') { @@ -76,4 +81,91 @@ final class Settings { return self::get_bot_token() !== '' && self::get_chat_id() !== ''; } + + public static function is_dry_run_drop(): bool + { + return (bool)get_option(self::OPT_DRY_RUN_DROP, true); + } + + public static function set_dry_run_drop(bool $on): void + { + update_option(self::OPT_DRY_RUN_DROP, $on); + } + + public static function get_drop_score_threshold(): int + { + return (int)get_option(self::OPT_DROP_THRESHOLD, -5); + } + + public static function set_drop_score_threshold(int $v): void + { + update_option(self::OPT_DROP_THRESHOLD, $v); + } + + public static function is_hard_drop_enabled(): bool + { + return (bool)get_option(self::OPT_HARD_DROP_ENABLED, true); + } + + public static function set_hard_drop_enabled(bool $on): void + { + update_option(self::OPT_HARD_DROP_ENABLED, $on); + } + + /** + * @return array{total:int,real_today:int,real_yesterday:int,dry_today:int,dry_yesterday:int,today_date:string,reset_at:string} + */ + public static function get_drop_counters(): array + { + $raw = get_option(self::OPT_DROP_COUNTERS, null); + if (!is_array($raw) || !isset($raw['total'])) { + return self::default_drop_counters(); + } + return array_merge(self::default_drop_counters(), $raw); + } + + /** + * Atomically increments the drop counter. `$mode` must be 'real' or 'dry_run'; + * any other value is a no-op (defensive — guards against typos in callers). + * Performs rolling today→yesterday on date change. + */ + public static function increment_drop_counter(string $mode): void + { + if (!in_array($mode, ['real', 'dry_run'], true)) return; + $c = self::get_drop_counters(); + $today = date('Y-m-d'); + if ($c['today_date'] !== $today) { + $c['real_yesterday'] = $c['real_today']; + $c['dry_yesterday'] = $c['dry_today']; + $c['real_today'] = 0; + $c['dry_today'] = 0; + $c['today_date'] = $today; + } + $c['total']++; + if ($mode === 'real') { + $c['real_today']++; + } else { + $c['dry_today']++; + } + update_option(self::OPT_DROP_COUNTERS, $c); + } + + public static function reset_drop_counters(): void + { + update_option(self::OPT_DROP_COUNTERS, self::default_drop_counters()); + } + + private static function default_drop_counters(): array + { + $today = date('Y-m-d'); + return [ + 'total' => 0, + 'real_today' => 0, + 'real_yesterday' => 0, + 'dry_today' => 0, + 'dry_yesterday' => 0, + 'today_date' => $today, + 'reset_at' => $today, + ]; + } } diff --git a/tests/SettingsCounterTest.php b/tests/SettingsCounterTest.php new file mode 100644 index 0000000..eb195d0 --- /dev/null +++ b/tests/SettingsCounterTest.php @@ -0,0 +1,105 @@ + 5, + 'real_today' => 5, + 'real_yesterday' => 0, + 'dry_today' => 0, + 'dry_yesterday' => 0, + 'today_date' => $yesterday, + 'reset_at' => $yesterday, + ]); + + Settings::increment_drop_counter('real'); + $c = Settings::get_drop_counters(); + self::assertSame(date('Y-m-d'), $c['today_date']); + self::assertSame(5, $c['real_yesterday']); + self::assertSame(1, $c['real_today']); + self::assertSame(6, $c['total']); + } + + public function test_reset_counters_zeroes_and_updates_reset_at(): void + { + Settings::increment_drop_counter('real'); + Settings::increment_drop_counter('dry_run'); + Settings::reset_drop_counters(); + $c = Settings::get_drop_counters(); + self::assertSame(0, $c['total']); + self::assertSame(0, $c['real_today']); + self::assertSame(0, $c['dry_today']); + self::assertSame(date('Y-m-d'), $c['reset_at']); + } + + public function test_get_dry_run_drop_default_true_when_unset(): void + { + self::assertTrue(Settings::is_dry_run_drop()); + } + + public function test_get_dry_run_drop_false_when_disabled(): void + { + update_option(Settings::OPT_DRY_RUN_DROP, false); + self::assertFalse(Settings::is_dry_run_drop()); + } + + public function test_get_drop_score_threshold_default_minus_5(): void + { + self::assertSame(-5, Settings::get_drop_score_threshold()); + } + + public function test_get_hard_drop_enabled_default_true(): void + { + self::assertTrue(Settings::is_hard_drop_enabled()); + } + + public function test_invalid_counters_option_resets_to_defaults(): void + { + update_option(Settings::OPT_DROP_COUNTERS, 'broken-string'); + $c = Settings::get_drop_counters(); + self::assertSame(0, $c['total']); + self::assertArrayHasKey('today_date', $c); + } + + public function test_increment_with_invalid_mode_is_noop(): void + { + Settings::increment_drop_counter('garbage'); + $c = Settings::get_drop_counters(); + self::assertSame(0, $c['total']); + } +}