'promotional', 'rule' => 'blocked_domain:' . $domain, 'score' => 100 ); } } // Rule 2: Title keyword match (single strong match is enough). foreach ( $title_keywords as $keyword ) { if ( empty( $keyword ) ) { continue; } if ( false !== strpos( $title, $keyword ) ) { return array( 'verdict' => 'promotional', 'rule' => 'title_keyword:' . $keyword, 'score' => 50 ); } } // Rule 3: Body/content keyword accumulation (truncate to 10KB). $body_text = $excerpt . ' ' . mb_substr( $content, 0, 10000 ); $body_matches = 0; $matched_keys = array(); foreach ( $body_keywords as $keyword ) { if ( empty( $keyword ) ) { continue; } if ( false !== strpos( $body_text, $keyword ) ) { ++$body_matches; $matched_keys[] = $keyword; $score += 15; } } if ( $body_matches >= $body_threshold ) { return array( 'verdict' => 'promotional', 'rule' => 'body_keywords:' . implode( ',', array_slice( $matched_keys, 0, 3 ) ), 'score' => $score, ); } // Rule 4: URL path patterns. foreach ( self::$promo_paths as $path ) { if ( false !== strpos( $url, $path ) ) { return array( 'verdict' => 'promotional', 'rule' => 'url_path:' . $path, 'score' => $score + 40 ); } } // Borderline: some body matches but not enough to block outright. if ( $body_matches >= 1 && $body_matches < $body_threshold ) { return array( 'verdict' => 'borderline', 'rule' => 'body_keywords_borderline:' . implode( ',', $matched_keys ), 'score' => $score, ); } return array( 'verdict' => 'clean', 'rule' => '', 'score' => $score ); } /** * Get the body keyword match threshold based on sensitivity. * * @param string $sensitivity 'strict', 'moderate', or 'lenient'. * @return int Number of body keyword matches required. */ public static function get_body_threshold( string $sensitivity ): int { $thresholds = array( 'strict' => 1, 'moderate' => 2, 'lenient' => 3 ); return $thresholds[ $sensitivity ] ?? 2; } /** Get configured title keywords, falling back to defaults. */ public static function get_title_keywords(): array { $custom = get_option( 'peptide_news_filter_title_keywords', '' ); if ( ! empty( $custom ) ) { return array_filter( array_map( 'trim', explode( "\n", $custom ) ) ); } return self::$default_title_keywords; } /** Get configured body keywords, falling back to defaults. */ public static function get_body_keywords(): array { $custom = get_option( 'peptide_news_filter_body_keywords', '' ); if ( ! empty( $custom ) ) { return array_filter( array_map( 'trim', explode( "\n", $custom ) ) ); } return self::$default_body_keywords; } /** Get configured blocked domains, falling back to defaults. */ public static function get_blocked_domains(): array { $custom = get_option( 'peptide_news_filter_blocked_domains', '' ); if ( ! empty( $custom ) ) { return array_filter( array_map( 'trim', explode( "\n", $custom ) ) ); } return self::$default_blocked_domains; } /** Get default title keywords as newline-separated text (for settings UI). */ public static function get_default_title_keywords_text(): string { return implode( "\n", self::$default_title_keywords ); } /** Get default body keywords as newline-separated text (for settings UI). */ public static function get_default_body_keywords_text(): string { return implode( "\n", self::$default_body_keywords ); } /** Get default blocked domains as newline-separated text (for settings UI). */ public static function get_default_blocked_domains_text(): string { return implode( "\n", self::$default_blocked_domains ); } }