119 lines
3.5 KiB
PHP
119 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Classifier\Lib\Discriminant;
|
|
|
|
use Classifier\Lib\Utils\StringCleaner;
|
|
|
|
class AlertAnalyzer
|
|
{
|
|
/**
|
|
* Seuil de confiance pour déclencher une alerte (0.0 à 1.0)
|
|
*/
|
|
public const CONFIDENCE_THRESHOLD = 0.75;
|
|
|
|
/**
|
|
* Seuil de similarité pour les mots simples (0.0 à 1.0)
|
|
*/
|
|
private const SIMILARITY_THRESHOLD = 0.85;
|
|
|
|
/** @var WhiteList */
|
|
private $whiteList;
|
|
|
|
/** @var BlackList */
|
|
private $blackList;
|
|
|
|
/** @var StringCleaner */
|
|
private $cleaner;
|
|
|
|
/** @var int */
|
|
private $activationThreshold;
|
|
|
|
public function __construct(
|
|
WhiteList $whiteList,
|
|
BlackList $blackList,
|
|
StringCleaner $cleaner,
|
|
int $activationThreshold = 100
|
|
) {
|
|
$this->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;
|
|
}
|
|
}
|