Greasy Fork

Infornia FR Skribbl Optimisé

Script optimisé avec mots classiques et interface compacte

// ==UserScript==
// @name         Infornia FR Skribbl Optimisé
// @namespace    https://greasyfork.org/en/users/1084087-fermion
// @version      0.2.0
// @description  Script optimisé avec mots classiques et interface compacte
// @author       fermion
// @match        http*://www.skribbl.io/*
// @match        http*://skribbl.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=skribbl.io
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    class OptimizedWordSleuth {
        constructor() {
            // Liste de mots classiques français distincts et courants
            this.baseWords = [
                // Animaux
                'chat', 'chien', 'oiseau', 'poisson', 'lapin', 'souris', 'lion', 'tigre', 'éléphant', 
                'girafe', 'singe', 'ours', 'loup', 'renard', 'cerf', 'cochon', 'vache', 'mouton', 
                'cheval', 'âne', 'coq', 'poule', 'canard', 'cygne', 'aigle', 'hibou', 'serpent',
                
                // Objets du quotidien
                'table', 'chaise', 'lit', 'porte', 'fenêtre', 'lampe', 'livre', 'stylo', 'téléphone',
                'ordinateur', 'télévision', 'voiture', 'vélo', 'train', 'avion', 'bateau', 'maison',
                'école', 'hôpital', 'église', 'magasin', 'restaurant', 'cinéma', 'théâtre', 'musée',
                
                // Nourriture
                'pain', 'fromage', 'pomme', 'banane', 'orange', 'fraise', 'cerise', 'raisin', 'carotte',
                'tomate', 'salade', 'poulet', 'bœuf', 'porc', 'poisson', 'œuf', 'lait', 'eau', 'café',
                'thé', 'chocolat', 'gâteau', 'biscuit', 'bonbon', 'glace', 'pizza', 'hamburger',
                
                // Actions
                'courir', 'marcher', 'sauter', 'danser', 'chanter', 'dessiner', 'écrire', 'lire',
                'dormir', 'manger', 'boire', 'jouer', 'travailler', 'étudier', 'rire', 'pleurer',
                'parler', 'écouter', 'regarder', 'toucher', 'sentir', 'conduire', 'voler', 'nager',
                
                // Couleurs et formes
                'rouge', 'bleu', 'vert', 'jaune', 'noir', 'blanc', 'orange', 'violet', 'rose', 'gris',
                'rond', 'carré', 'triangle', 'rectangle', 'cercle', 'étoile', 'cœur', 'losange',
                
                // Nature
                'soleil', 'lune', 'étoile', 'nuage', 'pluie', 'neige', 'vent', 'orage', 'arc-en-ciel',
                'montagne', 'mer', 'océan', 'rivière', 'lac', 'forêt', 'arbre', 'fleur', 'herbe',
                
                // Corps humain
                'tête', 'cheveux', 'yeux', 'nez', 'bouche', 'oreille', 'main', 'pied', 'bras', 'jambe',
                'dos', 'ventre', 'cœur', 'cerveau', 'dent', 'langue', 'doigt', 'ongle',
                
                // Vêtements
                'chemise', 'pantalon', 'robe', 'jupe', 'veste', 'manteau', 'chaussure', 'chaussette',
                'chapeau', 'écharpe', 'gant', 'ceinture', 'lunettes', 'montre', 'bijou', 'bague',
                
                // Sports et loisirs
                'football', 'tennis', 'basketball', 'volleyball', 'natation', 'course', 'vélo',
                'ski', 'pêche', 'chasse', 'musique', 'guitare', 'piano', 'violon', 'tambour'
            ];
            
            this.learnedWords = GM_getValue('learnedWords', []);
            this.allWords = [...this.baseWords, ...this.learnedWords];
            this.possibleWords = [];
            this.alreadyGuessed = [];
            this.closeWord = '';
            this.myName = '';
            
            this.createCompactInterface();
            this.fetchLatestWordlist();
            this.observeHintsAndInput();
            this.observePlayers();

            this.visibilityState = GM_getValue('interfaceVisible', true);
            this.updateInterfaceVisibility();

            document.addEventListener('keydown', (e) => {
                if (e.key === 'F2') {
                    this.toggleInterfaceVisibility();
                }
            });
        }

        createCompactInterface() {
            // Interface compacte positionnée en bas à droite
            this.parentElement = document.createElement('div');
            this.parentElement.style.cssText = `
                position: fixed;
                bottom: 10px;
                right: 10px;
                width: 300px;
                max-height: 150px;
                background: linear-gradient(135deg, rgba(0,0,0,0.8), rgba(30,30,30,0.9));
                border-radius: 12px;
                border: 2px solid rgba(100,200,255,0.3);
                box-shadow: 0 8px 32px rgba(0,0,0,0.3);
                backdrop-filter: blur(10px);
                z-index: 9999;
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            `;
            
            // En-tête compact
            const header = document.createElement('div');
            header.style.cssText = `
                background: linear-gradient(90deg, #4f46e5, #7c3aed);
                color: white;
                padding: 8px 12px;
                border-radius: 10px 10px 0 0;
                font-size: 12px;
                font-weight: bold;
                display: flex;
                justify-content: space-between;
                align-items: center;
            `;
            header.innerHTML = `
                🎨 Infornia FR
                📁
            `;
            
            // Zone des mots
            this.guessElement = document.createElement('div');
            this.guessElement.style.cssText = `
                padding: 8px;
                max-height: 100px;
                overflow-y: auto;
                overflow-x: hidden;
                display: flex;
                flex-wrap: wrap;
                gap: 4px;
                font-size: 11px;
            `;
            
            this.parentElement.appendChild(header);
            this.parentElement.appendChild(this.guessElement);
            document.body.appendChild(this.parentElement);
            
            // Event listener pour l'export
            document.getElementById('exportBtn').addEventListener('click', () => this.exportLearnedWords());
        }

        updateInterfaceVisibility() {
            this.parentElement.style.display = this.visibilityState ? 'block' : 'none';
            GM_setValue('interfaceVisible', this.visibilityState);
        }

        toggleInterfaceVisibility() {
            this.visibilityState = !this.visibilityState;
            this.updateInterfaceVisibility();
        }

        exportLearnedWords() {
            if (this.learnedWords.length === 0) {
                alert('Aucun nouveau mot appris pour le moment !');
                return;
            }
            
            const blob = new Blob([this.learnedWords.join('\n')], { type: 'text/plain;charset=utf-8' });
            const a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = `mots_appris_${new Date().toISOString().split('T')[0]}.txt`;
            a.style.display = 'none';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
        }

        fetchLatestWordlist() {
            // Récupération optionnelle de mots supplémentaires
            fetch('https://raw.githubusercontent.com/words/an-array-of-french-words/master/index.json')
                .then(response => response.json())
                .then(words => {
                    // Filtre pour ne garder que les mots courants (3-12 lettres, sans caractères spéciaux)
                    const filteredWords = words.filter(word => 
                        word.length >= 3 && 
                        word.length <= 12 && 
                        /^[a-zà-ÿ\s-]+$/i.test(word) &&
                        !this.allWords.includes(word)
                    ).slice(0, 1000); // Limite à 1000 mots supplémentaires
                    
                    this.allWords.push(...filteredWords);
                })
                .catch(() => {
                    console.log('Utilisation de la liste de base uniquement');
                });
        }

        observePlayers() {
            const playersContainer = document.querySelector(".players-list");
            if (playersContainer) {
                const observer = new MutationObserver(() => this.updatePlayersList());
                observer.observe(playersContainer, { childList: true, subtree: true });
            }
        }

        updatePlayersList() {
            const playerElems = document.querySelectorAll(".player");
            playerElems.forEach(playerElem => {
                const playerNameElem = playerElem.querySelector(".player-name");
                if (playerNameElem) {
                    let playerName = playerNameElem.textContent;
                    const isMe = playerNameElem.classList.contains("me");
                    if (isMe) {
                        this.myName = playerName.replace(" (You)", "").trim();
                    }
                }
            });
        }

        observeHintsAndInput() {
            this.observeHints();
            this.observeInput();
            this.observeChat();
        }

        observeHints() {
            const targetNodes = [
                document.querySelector('.hints .container'),
                document.querySelector('.words'),
                document.querySelector('#game-word'),
            ];
            
            const observer = new MutationObserver(() => this.hintObserverCallback());
            targetNodes.forEach(targetNode => {
                if (targetNode) {
                    observer.observe(targetNode, { childList: true, subtree: true });
                }
            });
        }

        hintObserverCallback() {
            const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
            if (inputElem && inputElem.value) return;

            this.checkIfAllHintsRevealed();
            this.checkWordsElement();
            this.generateGuesses();
        }

        checkIfAllHintsRevealed() {
            const hintElems = Array.from(document.querySelectorAll('.hints .hint'));
            
            if (hintElems.length > 0 && hintElems.every(elem => elem.classList.contains('uncover'))) {
                const correctAnswer = hintElems.map(elem => elem.textContent).join('').trim().toLowerCase();
                
                if (correctAnswer && /^[a-zà-ÿ\s-]+$/i.test(correctAnswer)) {
                    this.addLearnedWord(correctAnswer);
                }
            }
        }

        checkWordsElement() {
            const wordElems = Array.from(document.querySelectorAll('.words.show .word'));
            wordElems.forEach(elem => {
                const word = elem.textContent.trim().toLowerCase();
                if (word && /^[a-zà-ÿ\s-]+$/i.test(word)) {  
                    this.addLearnedWord(word);
                }
            });
        }

        addLearnedWord(word) {
            if (!this.allWords.includes(word) && !this.learnedWords.includes(word)) {
                this.learnedWords.push(word);
                this.allWords.push(word);
                GM_setValue('learnedWords', this.learnedWords);
            }
        }

        observeChat() {
            const chatContainer = document.querySelector('.chat-content');
            if (chatContainer) {
                const observer = new MutationObserver((mutationsList) => this.chatObserverCallback(mutationsList));
                observer.observe(chatContainer, { childList: true });
            }
        }

        chatObserverCallback(mutationsList) {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    let messageNode = mutation.addedNodes[0];
                    let message = messageNode.textContent;
                    let computedStyle = window.getComputedStyle(messageNode);

                    if (computedStyle.color === 'rgb(226, 203, 0)' && message.includes('is close!')) {
                        this.closeWord = message.split(' ')[0];
                    }

                    if (computedStyle.color === 'rgb(57, 117, 206)') {
                        this.alreadyGuessed = [];
                        this.closeWord = '';
                    }

                    if (message.includes(': ')) {
                        let guess = message.split(': ')[1];
                        if (!this.alreadyGuessed.includes(guess)) {
                            this.alreadyGuessed.push(guess);
                        }
                    }

                    this.generateGuesses();
                }
            }
        }

        observeInput() {
            const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
            if (inputElem) {
                inputElem.addEventListener('input', () => this.generateGuesses());
                inputElem.addEventListener('keydown', (event) => this.handleKeyDown(event));
            }

            const formElem = document.querySelector('#game-chat form');
            if (formElem) {
                formElem.addEventListener('submit', () => this.generateGuesses());
            }
        }

        handleKeyDown(event) {
            if (event.key === 'Tab' && this.possibleWords.length > 0) {
                event.preventDefault();
                const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
                inputElem.value = this.possibleWords[0];
                inputElem.focus();
                this.generateGuesses();
            }
        }

        levenshteinDistance(a, b) {
            const matrix = Array(b.length + 1).fill().map(() => Array(a.length + 1).fill(0));
            
            for (let i = 0; i <= a.length; i++) matrix[0][i] = i;
            for (let j = 0; j <= b.length; j++) matrix[j][0] = j;
            
            for (let j = 1; j <= b.length; j++) {
                for (let i = 1; i <= a.length; i++) {
                    if (a[i-1] === b[j-1]) {
                        matrix[j][i] = matrix[j-1][i-1];
                    } else {
                        matrix[j][i] = Math.min(
                            matrix[j-1][i-1] + 1,
                            matrix[j][i-1] + 1,
                            matrix[j-1][i] + 1
                        );
                    }
                }
            }
            
            return matrix[b.length][a.length];
        }

        generateGuesses() {
            const hintElems = Array.from(document.querySelectorAll('.hints .hint'));
            const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
            
            if (hintElems.length === 0) return;
            
            const hintParts = hintElems.map(elem => elem.textContent === '_' ? '.' : elem.textContent).join('');
            const inputText = inputElem ? inputElem.value || '' : '';

            // Filtre les mots possibles
            this.possibleWords = this.allWords.filter(word => {
                // Exclure les mots déjà devinés
                if (this.alreadyGuessed.includes(word)) return false;
                
                // Vérifier la proximité si un mot proche est indiqué
                if (this.closeWord.length > 0 && this.levenshteinDistance(word, this.closeWord) > 2) {
                    return false;
                }
                
                // Vérifier la correspondance avec les indices
                if (hintParts.length > 0) {
                    const wordParts = word.split(' ');
                    const hintWords = hintParts.split(' ');
                    
                    if (wordParts.length !== hintWords.length) return false;
                    
                    for (let i = 0; i < wordParts.length; i++) {
                        if (wordParts[i].length !== hintWords[i].length) return false;
                        
                        const hintRegex = new RegExp(`^${hintWords[i].replace(/\./g, '.')}$`, 'i');
                        if (!hintRegex.test(wordParts[i])) return false;
                    }
                }
                
                // Vérifier la correspondance avec la saisie
                if (inputText) {
                    const inputRegex = new RegExp(`^${inputText}`, 'i');
                    if (!inputRegex.test(word)) return false;
                }
                
                return true;
            }).slice(0, 20); // Limite à 20 suggestions

            this.renderGuesses();
        }

        renderGuesses() {
            this.guessElement.innerHTML = '';
            
            if (this.possibleWords.length === 0) {
                this.guessElement.innerHTML = 'Aucun mot trouvé';
                return;
            }

            this.possibleWords.forEach((word, index) => {
                const wordElem = document.createElement('div');
                wordElem.textContent = word;
                wordElem.style.cssText = `
                    display: inline-block;
                    padding: 4px 8px;
                    margin: 2px;
                    background: linear-gradient(135deg, #4f46e5, #7c3aed);
                    color: white;
                    border-radius: 6px;
                    cursor: pointer;
                    font-weight: 500;
                    transition: all 0.2s ease;
                    border: 1px solid rgba(255,255,255,0.1);
                `;

                // Effets de survol
                wordElem.addEventListener('mouseenter', function() {
                    this.style.background = 'linear-gradient(135deg, #6366f1, #8b5cf6)';
                    this.style.transform = 'translateY(-1px)';
                    this.style.boxShadow = '0 4px 12px rgba(79, 70, 229, 0.4)';
                });

                wordElem.addEventListener('mouseleave', function() {
                    this.style.background = 'linear-gradient(135deg, #4f46e5, #7c3aed)';
                    this.style.transform = 'translateY(0)';
                    this.style.boxShadow = 'none';
                });

                // Clic pour saisir le mot
                wordElem.addEventListener('click', () => {
                    const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
                    const formElem = document.querySelector('#game-chat form');
                    if (inputElem && formElem) {
                        inputElem.value = word;
                        formElem.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
                    }
                });

                this.guessElement.appendChild(wordElem);
            });
        }
    }

    // Initialisation du script
    new OptimizedWordSleuth();
})();