您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A refined skribbl.io helper avec liste de mots fréquents/francisés
// ==UserScript== // @name Infornia FR 2.0 // @namespace https://greasyfork.org/en/users/1084087-fermion // @version 0.2.0 // @description A refined skribbl.io helper avec liste de mots fréquents/francisés // @author fermion // @match https://skribbl.io/* // @match https://www.skribbl.io/* // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== (function(){ 'use strict'; /*** MODULE 1: WordListManager ***/ class WordListManager { constructor(key){ this.key = key; this.correct = new Set(GM_getValue(key, [])); this.freq = []; this.big = new Set(); } async init(){ const OS_URL = 'https://static.opensubtitles.org/fr/frequency_top5000.txt'; const FR_URL = 'https://raw.githubusercontent.com/frodonh/french-words/main/french.txt'; const [freq, big] = await Promise.all([ this.fetch(OS_URL), this.fetch(FR_URL) ]); this.freq = freq; freq.forEach(w => this.big.add(w)); big.forEach(w => this.big.add(w)); this.correct.forEach(w => this.big.add(w)); this.save(); } async fetch(url){ try { const r = await fetch(url); if (!r.ok) return []; return r.text() .then(t => t.split('\n').map(w=>w.trim().toLowerCase()).filter(Boolean)); } catch(e) { console.error(e); return []; } } save(){ GM_setValue(this.key, [...this.correct]); } add(w){ if(!this.correct.has(w)){ this.correct.add(w); this.save(); } } rank(w){ const i = this.freq.indexOf(w); return i>=0 ? i : this.freq.length; } filter(cands, pattern, parts, close, guessed, prefix){ return cands.filter(w=>{ if(guessed.has(w)) return false; if(close && levenshtein(w, close)>1) return false; if(w.split(' ').length !== parts.length) return false; if(!new RegExp(`^${pattern}$`,'i').test(w)) return false; if(prefix && !w.startsWith(prefix)) return false; return true; }).map(w=>({w,score:this.rank(w)})) .sort((a,b)=>a.score-b.score) .map(o=>o.w); } } /*** MODULE 2: GuessEngine ***/ class GuessEngine { constructor(wm){ this.wm = wm; this.guessed = new Set(); this.close = ''; } reset(){ this.guessed.clear(); this.close = ''; } noteClose(w){ this.close = w; } onReveal(w){ this.wm.add(w); } generate(hints, input){ const patt = hints.map(ch=>ch==='_'?'.':ch).join(''); const parts = patt.split(' '); return this.wm.filter([...this.wm.big], patt, parts, this.close, this.guessed, input); } remember(w){ this.guessed.add(w); } } /*** MODULE 3: GuiManager ***/ class GuiManager { constructor(onClick, onExport){ this.createUI(); this.exportBtn.onclick = onExport; document.addEventListener('keydown', e=>{ if(e.key==='F2'){ const vis = this.panel.style.display !== 'none'; this.panel.style.display = vis ? 'none':'block'; GM_setValue('visible', !vis); } }); } createUI(){ this.panel = document.createElement('div'); Object.assign(this.panel.style,{position:'fixed',bottom:'0',right:'0',width:'100%',background:'#fff',zIndex:9999}); document.body.appendChild(this.panel); this.exportBtn = document.createElement('button'); this.exportBtn.textContent = 'Export'; Object.assign(this.exportBtn.style,{position:'absolute',bottom:'100%',right:'0',margin:'5px'}); this.panel.appendChild(this.exportBtn); this.container = document.createElement('div'); Object.assign(this.container.style,{display:'flex',flexWrap:'wrap',gap:'6px',maxHeight:'200px',overflowY:'auto',padding:'10px'}); this.panel.appendChild(this.container); const vis = GM_getValue('visible', true); this.panel.style.display = vis ? 'block' : 'none'; } render(list, inputEl, engine){ this.container.innerHTML = ''; list.slice(0,100).forEach((w,i)=>{ const div = document.createElement('div'); div.textContent = w; Object.assign(div.style,{fontWeight:'bold',padding:'5px',background:`hsl(${360*i/(list.length||1)},100%,50%)`,color:'#fff',cursor:'pointer'}); div.onmouseenter = ()=>div.style.background='lightgray'; div.onmouseleave = ()=>div.style.background=''; div.onmousedown = ()=>div.style.background='gray'; div.onclick = ()=>{ inputEl.value = w; engine.remember(w); const form = document.querySelector('#game-chat form'); form.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true})); }; this.container.appendChild(div); }); } } /*** UTILS ***/ function levenshtein(a,b){ const m=a.length,n=b.length; const M=Array.from({length:n+1},(_,i)=>[i]); for(let j=0;j<=m;j++)M[0][j]=j; for(let i=1;i<=n;i++)for(let j=1;j<=m;j++) M[i][j]=(b[i-1]===a[j-1]?M[i-1][j-1]:Math.min(M[i-1][j-1]+1,M[i][j-1]+1,M[i-1][j]+1)); return M[n][m]; } /*** MAIN ***/ async function main(){ const wm = new WordListManager('correctAnswers'); await wm.init(); const engine = new GuessEngine(wm); const gui = new GuiManager(null, ()=>exportNew()); gui.onGuess = w=>engine.remember(w); const inputEl = document.querySelector('#game-chat input'); function update() { const hints = [...document.querySelectorAll('.hints .hint')].map(e=>e.textContent); const list = engine.generate(hints, inputEl.value.trim()); gui.render(list, inputEl, engine); } function exportNew(){ const a = document.createElement('a'); const blob = new Blob([[...wm.correct].join('\n')], {type:'text/plain;charset=utf-8'}); a.href = URL.createObjectURL(blob); a.download = 'words.txt'; a.click(); } ['input','keydown'].forEach(ev=>inputEl.addEventListener(ev, update)); // Observers const hintObs = new MutationObserver(m=>update()); document.querySelectorAll('.hints .container, .words, #game-word') .forEach(el=>el && hintObs.observe(el,{childList:true,subtree:true})); const chatObs = new MutationObserver(muts=>{ muts.forEach(mut=>mut.addedNodes.forEach(node=>{ const msg = node.textContent || ''; const col = getComputedStyle(node).color; if(col==='rgb(226, 203, 0)' && msg.includes('is close!')) engine.noteClose(msg.split(' ')[0]); if(col==='rgb(57, 117, 206)') engine.reset(); if([...document.querySelectorAll('.hints .hint')].every(e=>e.classList.contains('uncover'))){ const correct = [...document.querySelectorAll('.hints .hint')].map(e=>e.textContent).join('').toLowerCase(); engine.onReveal(correct); } })); }); const chatC = document.querySelector('.chat-content'); if(chatC) chatObs.observe(chatC,{childList:true}); // Initial pump update(); } main(); })();