// ======================================== // 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 };