## 🎯 Nouveau système d'erreurs graduées (architecture SmartTouch) ### Architecture procédurale intelligente : - **3 niveaux de gravité** : Légère (50%) → Moyenne (30%) → Grave (10%) - **14 types d'erreurs** réalistes et subtiles - **Sélection procédurale** selon contexte (longueur, technique, heure) - **Distribution contrôlée** : max 1 grave, 2 moyennes, 3 légères par article ### 1. Erreurs GRAVES (10% articles max) : - Accord sujet-verbe : "ils sont" → "ils est" - Mot manquant : "pour garantir la qualité" → "pour garantir qualité" - Double mot : "pour garantir" → "pour pour garantir" - Négation oubliée : "n'est pas" → "est pas" ### 2. Erreurs MOYENNES (30% articles) : - Accord pluriel : "plaques résistantes" → "plaques résistant" - Virgule manquante : "Ainsi, il" → "Ainsi il" - Registre inapproprié : "Par conséquent" → "Du coup" - Préposition incorrecte : "résistant aux" → "résistant des" - Connecteur illogique : "cependant" → "donc" ### 3. Erreurs LÉGÈRES (50% articles) : - Double espace : "de votre" → "de votre" - Trait d'union : "c'est-à-dire" → "c'est à dire" - Espace ponctuation : "qualité ?" → "qualité?" - Majuscule : "Toutenplaque" → "toutenplaque" - Apostrophe droite : "l'article" → "l'article" ## ✅ Système anti-répétition complet : ### Corrections critiques : - **HumanSimulationTracker.js** : Tracker centralisé global - **Word boundaries (\b)** sur TOUS les regex → FIX "maison" → "néanmoinson" - **Protection 30+ expressions idiomatiques** françaises - **Anti-répétition** : max 2× même mot, jamais 2× même développement - **Diversification** : 48 variantes (hésitations, développements, connecteurs) ### Nouvelle structure (comme SmartTouch) : ``` lib/human-simulation/ ├── error-profiles/ (NOUVEAU) │ ├── ErrorProfiles.js (définitions + probabilités) │ ├── ErrorGrave.js (10% articles) │ ├── ErrorMoyenne.js (30% articles) │ ├── ErrorLegere.js (50% articles) │ └── ErrorSelector.js (sélection procédurale) ├── HumanSimulationCore.js (orchestrateur) ├── HumanSimulationTracker.js (anti-répétition) └── [autres modules] ``` ## 🔄 Remplace ancien système : - ❌ SpellingErrors.js (basique, répétitif, "et" → "." × 8) - ✅ error-profiles/ (gradué, procédural, intelligent, diversifié) ## 🎲 Fonctionnalités procédurales : - Analyse contexte : longueur texte, complexité technique, heure rédaction - Multiplicateurs adaptatifs selon contexte - Conditions application intelligentes - Tracking global par batch (respecte limites 10%/30%/50%) ## 📊 Résultats validation : Sur 100 articles → ~40-50 avec erreurs subtiles et diverses (plus de spam répétitif) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
631 lines
21 KiB
JavaScript
631 lines
21 KiB
JavaScript
// ========================================
|
|
// FICHIER: TemporalStyles.js
|
|
// RESPONSABILITÉ: Variations temporelles d'écriture
|
|
// Simulation comportement humain selon l'heure
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
|
|
/**
|
|
* STYLES TEMPORELS PAR TRANCHES HORAIRES
|
|
* Simule l'énergie et style d'écriture selon l'heure
|
|
*/
|
|
const TEMPORAL_STYLES = {
|
|
|
|
// ========================================
|
|
// MATIN (6h-11h) - Énergique et Direct
|
|
// ========================================
|
|
morning: {
|
|
period: 'matin',
|
|
timeRange: [6, 11],
|
|
energy: 'high',
|
|
characteristics: {
|
|
sentenceLength: 'short', // Phrases plus courtes
|
|
vocabulary: 'dynamic', // Mots énergiques
|
|
connectors: 'direct', // Connecteurs simples
|
|
rhythm: 'fast' // Rythme soutenu
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['dynamique', 'efficace', 'rapide', 'direct', 'actif', 'performant'],
|
|
connectors: ['donc', 'puis', 'ensuite', 'maintenant', 'immédiatement'],
|
|
modifiers: ['très', 'vraiment', 'particulièrement', 'nettement'],
|
|
actions: ['optimiser', 'accélérer', 'améliorer', 'développer', 'créer']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.7, // 70% chance phrases courtes
|
|
directConnectorsBias: 0.8, // 80% connecteurs simples
|
|
energyWordsBias: 0.6 // 60% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// APRÈS-MIDI (12h-17h) - Équilibré et Professionnel
|
|
// ========================================
|
|
afternoon: {
|
|
period: 'après-midi',
|
|
timeRange: [12, 17],
|
|
energy: 'medium',
|
|
characteristics: {
|
|
sentenceLength: 'medium', // Phrases équilibrées
|
|
vocabulary: 'professional', // Vocabulaire standard
|
|
connectors: 'balanced', // Connecteurs variés
|
|
rhythm: 'steady' // Rythme régulier
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['professionnel', 'efficace', 'qualité', 'standard', 'adapté'],
|
|
connectors: ['par ailleurs', 'de plus', 'également', 'ainsi', 'cependant'],
|
|
modifiers: ['assez', 'plutôt', 'relativement', 'suffisamment'],
|
|
actions: ['réaliser', 'développer', 'analyser', 'étudier', 'concevoir']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.4, // 40% phrases courtes
|
|
directConnectorsBias: 0.5, // 50% connecteurs simples
|
|
energyWordsBias: 0.3 // 30% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// SOIR (18h-23h) - Détendu et Réflexif
|
|
// ========================================
|
|
evening: {
|
|
period: 'soir',
|
|
timeRange: [18, 23],
|
|
energy: 'low',
|
|
characteristics: {
|
|
sentenceLength: 'long', // Phrases plus longues
|
|
vocabulary: 'nuanced', // Vocabulaire nuancé
|
|
connectors: 'complex', // Connecteurs élaborés
|
|
rhythm: 'relaxed' // Rythme posé
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['approfondi', 'réfléchi', 'considéré', 'nuancé', 'détaillé'],
|
|
connectors: ['néanmoins', 'cependant', 'par conséquent', 'en outre', 'toutefois'],
|
|
modifiers: ['quelque peu', 'relativement', 'dans une certaine mesure', 'assez'],
|
|
actions: ['examiner', 'considérer', 'réfléchir', 'approfondir', 'explorer']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.2, // 20% phrases courtes
|
|
directConnectorsBias: 0.2, // 20% connecteurs simples
|
|
energyWordsBias: 0.1 // 10% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// NUIT (0h-5h) - Fatigue et Simplicité
|
|
// ========================================
|
|
night: {
|
|
period: 'nuit',
|
|
timeRange: [0, 5],
|
|
energy: 'very_low',
|
|
characteristics: {
|
|
sentenceLength: 'short', // Phrases courtes par fatigue
|
|
vocabulary: 'simple', // Vocabulaire basique
|
|
connectors: 'minimal', // Connecteurs rares
|
|
rhythm: 'slow' // Rythme lent
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['simple', 'basique', 'standard', 'normal', 'classique'],
|
|
connectors: ['et', 'mais', 'ou', 'donc', 'puis'],
|
|
modifiers: ['assez', 'bien', 'pas mal', 'correct'],
|
|
actions: ['faire', 'utiliser', 'prendre', 'mettre', 'avoir']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.8, // 80% phrases courtes
|
|
directConnectorsBias: 0.9, // 90% connecteurs simples
|
|
energyWordsBias: 0.1 // 10% mots énergiques
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DÉTERMINER STYLE TEMPOREL SELON L'HEURE
|
|
* @param {number} currentHour - Heure actuelle (0-23)
|
|
* @returns {object} - Style temporel correspondant
|
|
*/
|
|
function getTemporalStyle(currentHour) {
|
|
// Validation heure
|
|
const hour = Math.max(0, Math.min(23, Math.floor(currentHour || new Date().getHours())));
|
|
|
|
logSh(`⏰ Détermination style temporel pour ${hour}h`, 'DEBUG');
|
|
|
|
// Déterminer période
|
|
let selectedStyle;
|
|
|
|
if (hour >= 6 && hour <= 11) {
|
|
selectedStyle = TEMPORAL_STYLES.morning;
|
|
} else if (hour >= 12 && hour <= 17) {
|
|
selectedStyle = TEMPORAL_STYLES.afternoon;
|
|
} else if (hour >= 18 && hour <= 23) {
|
|
selectedStyle = TEMPORAL_STYLES.evening;
|
|
} else {
|
|
selectedStyle = TEMPORAL_STYLES.night;
|
|
}
|
|
|
|
logSh(`⏰ Style temporel sélectionné: ${selectedStyle.period} (énergie: ${selectedStyle.energy})`, 'DEBUG');
|
|
|
|
return {
|
|
...selectedStyle,
|
|
currentHour: hour,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* APPLICATION STYLE TEMPOREL
|
|
* @param {string} content - Contenu à modifier
|
|
* @param {object} temporalStyle - Style temporel à appliquer
|
|
* @param {object} options - Options { intensity, tracker }
|
|
* @returns {object} - { content, modifications }
|
|
*/
|
|
function applyTemporalStyle(content, temporalStyle, options = {}) {
|
|
if (!content || !temporalStyle) {
|
|
return { content, modifications: 0 };
|
|
}
|
|
|
|
const intensity = options.intensity || 1.0;
|
|
const tracker = options.tracker || null;
|
|
|
|
logSh(`⏰ Application style temporel: ${temporalStyle.period} (intensité: ${intensity})`, 'DEBUG');
|
|
|
|
let modifiedContent = content;
|
|
let modifications = 0;
|
|
|
|
// ========================================
|
|
// 1. AJUSTEMENT LONGUEUR PHRASES
|
|
// ========================================
|
|
const sentenceResult = adjustSentenceLength(modifiedContent, temporalStyle, intensity, tracker);
|
|
modifiedContent = sentenceResult.content;
|
|
modifications += sentenceResult.count;
|
|
|
|
// ========================================
|
|
// 2. ADAPTATION VOCABULAIRE
|
|
// ========================================
|
|
const vocabularyResult = adaptVocabulary(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = vocabularyResult.content;
|
|
modifications += vocabularyResult.count;
|
|
|
|
// ========================================
|
|
// 3. MODIFICATION CONNECTEURS
|
|
// ========================================
|
|
const connectorResult = adjustConnectors(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = connectorResult.content;
|
|
modifications += connectorResult.count;
|
|
|
|
// ========================================
|
|
// 4. AJUSTEMENT RYTHME
|
|
// ========================================
|
|
const rhythmResult = adjustRhythm(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = rhythmResult.content;
|
|
modifications += rhythmResult.count;
|
|
|
|
logSh(`⏰ Style temporel appliqué: ${modifications} modifications`, 'DEBUG');
|
|
|
|
return {
|
|
content: modifiedContent,
|
|
modifications
|
|
};
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT LONGUEUR PHRASES
|
|
* @param {object} tracker - HumanSimulationTracker instance (optionnel)
|
|
*/
|
|
function adjustSentenceLength(content, temporalStyle, intensity, tracker = null) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const bias = temporalStyle.styleTendencies.shortSentencesBias * intensity;
|
|
const sentences = modified.split('. ');
|
|
|
|
// Probabilité d'appliquer - ÉQUILIBRÉ (25% max)
|
|
if (Math.random() > (intensity * 0.25)) { // FIXÉ V3.1: ÉQUILIBRÉ - 25% max
|
|
return { content: modified, count };
|
|
}
|
|
|
|
const processedSentences = sentences.map(sentence => {
|
|
if (sentence.length < 20) return sentence; // Ignorer phrases très courtes
|
|
|
|
// Style MATIN/NUIT - Raccourcir phrases longues
|
|
if ((temporalStyle.period === 'matin' || temporalStyle.period === 'nuit') &&
|
|
sentence.length > 100 && Math.random() < bias) {
|
|
|
|
// Chercher point de coupe naturel
|
|
const cutPoints = [', qui', ', que', ', dont', ' et ', ' car ', ' mais '];
|
|
for (const cutPoint of cutPoints) {
|
|
const cutIndex = sentence.indexOf(cutPoint);
|
|
if (cutIndex > 30 && cutIndex < sentence.length - 30) {
|
|
count++;
|
|
logSh(` ✂️ Phrase raccourcie (${temporalStyle.period}): ${sentence.length} → ${cutIndex} chars`, 'DEBUG');
|
|
return sentence.substring(0, cutIndex) + '. ' +
|
|
sentence.substring(cutIndex + cutPoint.length);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Style SOIR - Allonger phrases courtes
|
|
if (temporalStyle.period === 'soir' &&
|
|
sentence.length > 30 && sentence.length < 80 &&
|
|
Math.random() < (1 - bias)) {
|
|
|
|
// Ajouter développements - ÉLARGI 20+ variantes
|
|
const developments = [
|
|
// Avantages et bénéfices
|
|
', ce qui constitue un avantage notable',
|
|
', permettant ainsi d\'optimiser les résultats',
|
|
', contribuant à l\'efficacité globale',
|
|
', offrant ainsi des perspectives intéressantes',
|
|
', garantissant une meilleure qualité',
|
|
// Processus et démarches
|
|
', dans une démarche d\'amélioration continue',
|
|
', dans le cadre d\'une stratégie cohérente',
|
|
', selon une approche méthodique',
|
|
', grâce à une mise en œuvre rigoureuse',
|
|
// Contexte et perspective
|
|
', tout en respectant les standards en vigueur',
|
|
', conformément aux attentes du marché',
|
|
', en adéquation avec les besoins identifiés',
|
|
', dans le respect des contraintes établies',
|
|
// Résultats et impacts
|
|
', avec des résultats mesurables',
|
|
', pour un impact significatif',
|
|
', assurant une performance optimale',
|
|
', favorisant ainsi la satisfaction client',
|
|
// Approches techniques
|
|
', en utilisant des méthodes éprouvées',
|
|
', par le biais d\'une expertise reconnue',
|
|
', moyennant une analyse approfondie',
|
|
// Continuité et évolution
|
|
', dans une perspective d\'évolution constante',
|
|
', tout en maintenant un haut niveau d\'exigence',
|
|
', en veillant à la pérennité du système',
|
|
', garantissant une adaptation progressive'
|
|
];
|
|
|
|
// ✅ ANTI-RÉPÉTITION: Filtrer développements déjà utilisés
|
|
let availableDevelopments = developments;
|
|
if (tracker) {
|
|
availableDevelopments = developments.filter(d => tracker.canUseDevelopment(d));
|
|
}
|
|
|
|
// Si aucun développement disponible, skip
|
|
if (availableDevelopments.length === 0) {
|
|
logSh(` 🚫 Aucun développement disponible (tous déjà utilisés)`, 'DEBUG');
|
|
return sentence;
|
|
}
|
|
|
|
const development = availableDevelopments[Math.floor(Math.random() * availableDevelopments.length)];
|
|
|
|
// ✅ Enregistrer dans tracker
|
|
if (tracker) {
|
|
tracker.trackDevelopment(development);
|
|
}
|
|
|
|
count++;
|
|
logSh(` 📝 Phrase allongée (soir): ${sentence.length} → ${sentence.length + development.length} chars`, 'DEBUG');
|
|
return sentence + development;
|
|
}
|
|
|
|
return sentence;
|
|
});
|
|
|
|
modified = processedSentences.join('. ');
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* ADAPTATION VOCABULAIRE
|
|
*/
|
|
function adaptVocabulary(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const vocabularyPrefs = temporalStyle.vocabularyPreferences;
|
|
const energyBias = temporalStyle.styleTendencies.energyWordsBias * intensity;
|
|
|
|
// Probabilité d'appliquer - ÉQUILIBRÉ (20% max)
|
|
if (Math.random() > (intensity * 0.2)) { // FIXÉ V3.1: ÉQUILIBRÉ - 20% max
|
|
return { content: modified, count };
|
|
}
|
|
|
|
// Remplacements selon période
|
|
const replacements = buildVocabularyReplacements(temporalStyle.period, vocabularyPrefs);
|
|
|
|
replacements.forEach(replacement => {
|
|
if (Math.random() < (energyBias * 0.3)) { // FIXÉ V3.1: ÉQUILIBRÉ - 30%
|
|
// ✅ PROTECTION: Vérifier expressions idiomatiques
|
|
if (isInProtectedExpression(modified, replacement.from)) {
|
|
logSh(` 🛡️ Remplacement bloqué: "${replacement.from}" dans expression protégée`, 'DEBUG');
|
|
return; // Skip ce remplacement
|
|
}
|
|
|
|
const regex = new RegExp(`\\b${replacement.from}\\b`, 'gi');
|
|
if (modified.match(regex)) {
|
|
modified = modified.replace(regex, replacement.to);
|
|
count++;
|
|
logSh(` 📚 Vocabulaire adapté (${temporalStyle.period}): "${replacement.from}" → "${replacement.to}"`, 'DEBUG');
|
|
}
|
|
}
|
|
});
|
|
|
|
// FIXÉ V3: PAS de fallback garanti - SUBTILITÉ MAXIMALE
|
|
// Aucune modification forcée
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* EXPRESSIONS IDIOMATIQUES FRANÇAISES PROTÉGÉES
|
|
* Ne JAMAIS remplacer de mots dans ces expressions
|
|
*/
|
|
const PROTECTED_EXPRESSIONS = [
|
|
// Expressions courantes avec "faire"
|
|
'faire toute la différence',
|
|
'faire attention',
|
|
'faire les choses',
|
|
'faire face',
|
|
'faire preuve',
|
|
'faire partie',
|
|
'faire confiance',
|
|
'faire référence',
|
|
'faire appel',
|
|
'faire en sorte',
|
|
// Expressions courantes avec "prendre"
|
|
'prendre en compte',
|
|
'prendre en charge',
|
|
'prendre conscience',
|
|
'prendre part',
|
|
'prendre soin',
|
|
// Expressions courantes avec "mettre"
|
|
'mettre en œuvre',
|
|
'mettre en place',
|
|
'mettre en avant',
|
|
'mettre l\'accent',
|
|
// Autres expressions figées
|
|
'avoir lieu',
|
|
'donner suite',
|
|
'tenir compte',
|
|
'bien entendu',
|
|
'en effet',
|
|
'en fait',
|
|
'tout à fait',
|
|
'par exemple'
|
|
];
|
|
|
|
/**
|
|
* VÉRIFIER SI UN MOT EST DANS UNE EXPRESSION PROTÉGÉE
|
|
* @param {string} content - Contenu complet
|
|
* @param {string} word - Mot à remplacer
|
|
* @returns {boolean} - true si dans expression protégée
|
|
*/
|
|
function isInProtectedExpression(content, word) {
|
|
const lowerContent = content.toLowerCase();
|
|
const lowerWord = word.toLowerCase();
|
|
|
|
return PROTECTED_EXPRESSIONS.some(expr => {
|
|
const exprLower = expr.toLowerCase();
|
|
// Vérifier si l'expression existe ET contient le mot
|
|
return exprLower.includes(lowerWord) && lowerContent.includes(exprLower);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* CONSTRUCTION REMPLACEMENTS VOCABULAIRE
|
|
*/
|
|
function buildVocabularyReplacements(period, vocabPrefs) {
|
|
const replacements = [];
|
|
|
|
switch (period) {
|
|
case 'matin':
|
|
replacements.push(
|
|
{ from: 'bon', to: 'excellent' },
|
|
{ from: 'intéressant', to: 'dynamique' },
|
|
{ from: 'utiliser', to: 'optimiser' },
|
|
{ from: 'faire', to: 'créer' }
|
|
);
|
|
break;
|
|
|
|
case 'soir':
|
|
replacements.push(
|
|
{ from: 'bon', to: 'considérable' },
|
|
{ from: 'faire', to: 'examiner' },
|
|
{ from: 'utiliser', to: 'exploiter' },
|
|
{ from: 'voir', to: 'considérer' }
|
|
);
|
|
break;
|
|
|
|
case 'nuit':
|
|
replacements.push(
|
|
{ from: 'excellent', to: 'bien' },
|
|
{ from: 'optimiser', to: 'utiliser' },
|
|
{ from: 'considérable', to: 'correct' },
|
|
{ from: 'examiner', to: 'regarder' }
|
|
);
|
|
break;
|
|
|
|
default: // après-midi
|
|
// Vocabulaire équilibré - pas de remplacements drastiques
|
|
break;
|
|
}
|
|
|
|
return replacements;
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT CONNECTEURS
|
|
*/
|
|
function adjustConnectors(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const connectorBias = temporalStyle.styleTendencies.directConnectorsBias * intensity;
|
|
const preferredConnectors = temporalStyle.vocabularyPreferences.connectors;
|
|
|
|
if (Math.random() > intensity * 0.5) {
|
|
return { content: modified, count };
|
|
}
|
|
|
|
// Connecteurs selon période - FIXÉ: Word boundaries pour éviter "maison" → "néanmoinson"
|
|
const connectorMappings = {
|
|
matin: [
|
|
{ from: /\bpar conséquent\b/gi, to: 'donc' },
|
|
{ from: /\bnéanmoins\b/gi, to: 'mais' },
|
|
{ from: /\ben outre\b/gi, to: 'aussi' }
|
|
],
|
|
soir: [
|
|
{ from: /\bdonc\b/gi, to: 'par conséquent' },
|
|
{ from: /\bmais\b/gi, to: 'néanmoins' }, // ✅ FIXÉ: \b empêche match dans "maison"
|
|
{ from: /\baussi\b/gi, to: 'en outre' }
|
|
],
|
|
nuit: [
|
|
{ from: /\bpar conséquent\b/gi, to: 'donc' },
|
|
{ from: /\bnéanmoins\b/gi, to: 'mais' },
|
|
{ from: /\bcependant\b/gi, to: 'mais' }
|
|
]
|
|
};
|
|
|
|
const mappings = connectorMappings[temporalStyle.period] || [];
|
|
|
|
mappings.forEach(mapping => {
|
|
if (Math.random() < connectorBias) {
|
|
if (modified.match(mapping.from)) {
|
|
modified = modified.replace(mapping.from, mapping.to);
|
|
count++;
|
|
logSh(` 🔗 Connecteur adapté (${temporalStyle.period}): "${mapping.from}" → "${mapping.to}"`, 'DEBUG');
|
|
}
|
|
}
|
|
});
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT RYTHME
|
|
*/
|
|
function adjustRhythm(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
// Le rythme affecte la ponctuation et les pauses
|
|
if (Math.random() > intensity * 0.3) {
|
|
return { content: modified, count };
|
|
}
|
|
|
|
switch (temporalStyle.characteristics.rhythm) {
|
|
case 'fast': // Matin - moins de virgules, plus direct
|
|
if (Math.random() < 0.4) {
|
|
// Supprimer quelques virgules non essentielles
|
|
const originalCommas = (modified.match(/,/g) || []).length;
|
|
modified = modified.replace(/, qui /gi, ' qui ');
|
|
modified = modified.replace(/, que /gi, ' que ');
|
|
const newCommas = (modified.match(/,/g) || []).length;
|
|
count = originalCommas - newCommas;
|
|
if (count > 0) {
|
|
logSh(` ⚡ Rythme accéléré: ${count} virgules supprimées`, 'DEBUG');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'relaxed': // Soir - plus de pauses
|
|
if (Math.random() < 0.3) {
|
|
// Ajouter quelques pauses réflexives - 15+ variantes
|
|
const reflexivePauses = [
|
|
'Ainsi',
|
|
'Par ailleurs',
|
|
'De plus',
|
|
'En outre',
|
|
'D\'ailleurs',
|
|
'Également',
|
|
'Qui plus est',
|
|
'De surcroît',
|
|
'Par conséquent',
|
|
'Effectivement',
|
|
'En effet',
|
|
'Cela dit',
|
|
'Toutefois',
|
|
'Néanmoins',
|
|
'Pour autant'
|
|
];
|
|
const pause = reflexivePauses[Math.floor(Math.random() * reflexivePauses.length)];
|
|
modified = modified.replace(/\. ([A-Z])/g, `. ${pause}, $1`);
|
|
count++;
|
|
logSh(` 🧘 Rythme ralenti: pause "${pause}" ajoutée`, 'DEBUG');
|
|
}
|
|
break;
|
|
|
|
case 'slow': // Nuit - simplification
|
|
if (Math.random() < 0.5) {
|
|
// Simplifier structures complexes
|
|
modified = modified.replace(/ ; /g, '. ');
|
|
count++;
|
|
logSh(` 😴 Rythme simplifié: structures allégées`, 'DEBUG');
|
|
}
|
|
break;
|
|
}
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* ANALYSE COHÉRENCE TEMPORELLE
|
|
* @param {string} content - Contenu à analyser
|
|
* @param {object} temporalStyle - Style appliqué
|
|
* @returns {object} - Métriques de cohérence
|
|
*/
|
|
function analyzeTemporalCoherence(content, temporalStyle) {
|
|
const sentences = content.split('. ');
|
|
const avgSentenceLength = sentences.reduce((sum, s) => sum + s.length, 0) / sentences.length;
|
|
|
|
const energyWords = temporalStyle.vocabularyPreferences.energy;
|
|
const energyWordCount = energyWords.reduce((count, word) => {
|
|
const regex = new RegExp(`\\b${word}\\b`, 'gi');
|
|
return count + (content.match(regex) || []).length;
|
|
}, 0);
|
|
|
|
return {
|
|
avgSentenceLength,
|
|
energyWordDensity: energyWordCount / sentences.length,
|
|
period: temporalStyle.period,
|
|
coherenceScore: calculateCoherenceScore(avgSentenceLength, temporalStyle),
|
|
expectedCharacteristics: temporalStyle.characteristics
|
|
};
|
|
}
|
|
|
|
/**
|
|
* CALCUL SCORE COHÉRENCE
|
|
*/
|
|
function calculateCoherenceScore(avgLength, temporalStyle) {
|
|
let score = 1.0;
|
|
|
|
// Vérifier cohérence longueur phrases avec période
|
|
const expectedLength = {
|
|
'matin': { min: 40, max: 80 },
|
|
'après-midi': { min: 60, max: 120 },
|
|
'soir': { min: 80, max: 150 },
|
|
'nuit': { min: 30, max: 70 }
|
|
};
|
|
|
|
const expected = expectedLength[temporalStyle.period];
|
|
if (expected) {
|
|
if (avgLength < expected.min || avgLength > expected.max) {
|
|
score *= 0.7;
|
|
}
|
|
}
|
|
|
|
return Math.max(0, Math.min(1, score));
|
|
}
|
|
|
|
// ============= EXPORTS =============
|
|
module.exports = {
|
|
getTemporalStyle,
|
|
applyTemporalStyle,
|
|
adjustSentenceLength,
|
|
adaptVocabulary,
|
|
adjustConnectors,
|
|
adjustRhythm,
|
|
analyzeTemporalCoherence,
|
|
calculateCoherenceScore,
|
|
buildVocabularyReplacements,
|
|
isInProtectedExpression,
|
|
TEMPORAL_STYLES,
|
|
PROTECTED_EXPRESSIONS
|
|
}; |