auditor = $auditor; $this->history = $history; $this->registry = $registry; $this->policy = $policy; } /** * Registers hooks. * * @return void */ public function register(): void { add_action( 'aiblackout_prune_logs', array( $this, 'record_daily_check' ) ); add_action( 'aiblackout_crawler_blocked', array( $this, 'record_blocked_request' ) ); } /** * Re-runs the non-loopback checks and records today's state. * * @return void */ public function record_daily_check(): void { $report = $this->auditor->report( false ); $status = isset( $report['summary']['status'] ) ? (string) $report['summary']['status'] : Result::UNKNOWN; $now = current_time( 'mysql' ); $today = substr( $now, 0, 10 ); $history = $this->get_state_history(); $last = count( $history ) - 1; // One entry per day; a second run the same day replaces it. if ( $last >= 0 && isset( $history[ $last ]['date'] ) && $today === $history[ $last ]['date'] ) { $history[ $last ]['status'] = $status; } else { $history[] = array( 'date' => $today, 'status' => $status, ); } if ( count( $history ) > self::MAX_STATE_ENTRIES ) { $history = array_slice( $history, -1 * self::MAX_STATE_ENTRIES ); } update_option( self::STATE_OPTION, $history, false ); } /** * Records one request the PHP gate refused. * * @param string $token The crawler token that matched. Unused. * @return void */ public function record_blocked_request( string $token ): void { unset( $token ); $data = $this->get_total_data(); $data['total'] = $data['total'] + 1; if ( 0 === $data['since'] ) { $data['since'] = time(); } update_option( self::TOTAL_OPTION, $data, false ); } /** * Returns the current consecutive-days-protected streak. * * @return int Days, counting back from the most recent entry. */ public function get_streak(): int { $history = $this->get_state_history(); $streak = 0; for ( $i = count( $history ) - 1; $i >= 0; $i-- ) { if ( ! isset( $history[ $i ]['status'] ) || Result::PROTECTED_STATE !== $history[ $i ]['status'] ) { break; } ++$streak; } return $streak; } /** * Returns the current protection status, computed fresh. * * @return string One of the Result::* status constants. */ public function get_live_status(): string { $report = $this->auditor->report( false ); return isset( $report['summary']['status'] ) ? (string) $report['summary']['status'] : Result::UNKNOWN; } /** * Returns the lifetime total of requests the PHP gate has refused. * * @return int Total requests. */ public function get_lifetime_total(): int { return $this->get_total_data()['total']; } /** * Returns when the lifetime total started counting. * * @return int|null Unix timestamp, or null before the first blocked request. */ public function get_blocking_since(): ?int { $since = $this->get_total_data()['since']; return $since > 0 ? $since : null; } /** * Returns when the reservation of rights first went live. * * @return int|null Unix timestamp, or null when never published. */ public function get_protecting_since(): ?int { return $this->history->first_published( Tdmrep::TYPE ); } /** * Returns the labels of AI companies currently blocked or signal-only. * * @return string[] Company labels. */ public function get_blocked_companies(): array { $blocked = $this->policy->agents_with( array( Policy::BLOCK, Policy::SIGNAL_ONLY ) ); $group_ids = array(); foreach ( $blocked as $agent ) { if ( ! empty( $agent['group'] ) ) { $group_ids[ $agent['group'] ] = true; } } $companies = array(); foreach ( $this->registry->get_groups() as $group ) { if ( isset( $group_ids[ $group['id'] ] ) ) { $companies[] = $group['label']; } } return $companies; } /** * Returns the total number of known AI companies. * * @return int Company count. */ public function get_total_company_count(): int { return count( $this->registry->get_groups() ); } /** * Returns the daily state history, oldest first. * * @return array List of entries. */ private function get_state_history(): array { $stored = get_option( self::STATE_OPTION, array() ); return is_array( $stored ) ? array_values( $stored ) : array(); } /** * Returns the lifetime total data, normalized. * * @return array Array with 'total' and 'since'. */ private function get_total_data(): array { $stored = get_option( self::TOTAL_OPTION, array() ); $stored = is_array( $stored ) ? $stored : array(); return array( 'total' => isset( $stored['total'] ) ? (int) $stored['total'] : 0, 'since' => isset( $stored['since'] ) ? (int) $stored['since'] : 0, ); } }