whiteList = $whiteList; $this->blackList = $blackList; $this->cleaner = $cleaner; $this->activationThreshold = $activationThreshold; } public function analyze(string $rawText): AnalyzeResult { $cleanChain = $this->cleaner->getCleanChain($rawText); $words = $this->cleaner->preprocessMessage($rawText); $totalScore = 0.0; $found = []; foreach ($this->whiteList->getKeywords() as $keyword => $weight) { // * Nettoyage rapide du mot-clé lui-même (au cas où il ait des accents) $cleanKeyword = $this->cleaner->getCleanChain($keyword); if (strpos($cleanKeyword, ' ') !== false) { // * RECHERCHE DE MOTS COMPOSÉS (Exacte ou Fuzzy sur chaîne) if (mb_strpos($cleanChain, $cleanKeyword) !== false) { $totalScore += $weight; $found[] = $keyword; } } else { // * RECHERCHE DE MOT SIMPLE (Fuzzy Levenshtein) $similarity = $this->findBestSimilarity($cleanKeyword, $words); if ($similarity > self::SIMILARITY_THRESHOLD) { $totalScore += ($weight * $similarity); $found[] = $keyword; } } } foreach ($this->blackList->getKeywords() as $badWord) { if (mb_strpos($cleanChain, $this->cleaner->getCleanChain($badWord)) !== false) { return new AnalyzeResult( 0.0, 0.0, false, true, "Blacklist : {$badWord}" ); } } // NOTE: Si le score dépasse le seuil, la confiance est de 100% (1.0) $confidence = min(1.0, $totalScore / $this->activationThreshold); return new AnalyzeResult( round($confidence, 2), $totalScore, $confidence >= self::CONFIDENCE_THRESHOLD, false, !empty($found) ? 'Keywords found: ' . implode(', ', $found) : 'No keywords found', $found ); } private function findBestSimilarity(string $keyword, array $words): float { $best = 0.0; foreach ($words as $word) { $dist = levenshtein($keyword, $word); $maxLen = max(strlen($keyword), strlen($word)); if ($maxLen === 0) { continue; } $sim = 1 - ($dist / $maxLen); if ($sim > $best) { $best = $sim; } if ($best == 1.0) { break; } } return $best; } }