Spoofs player information
// ==UserScript==
// @name Chess.com Player Editor
// @namespace http://tampermonkey.net/
// @version 2.0
// @match https://www.chess.com/*
// @grant none
// @run-at document-start
// @license MIT
// @description Spoofs player information
// ==/UserScript==
(function(){
'use strict';
const FAIRPLAY_ICON='https://images.chesscomfiles.com/chess-flair/staff_mod_account/blocked.svg';
const ALL_FLAIRS=[
'https://images.chesscomfiles.com/chess-flair/membership_icons/diamond_traditional.svg',
'https://images.chesscomfiles.com/chess-flair/membership_icons/diamond_blue.svg',
'https://images.chesscomfiles.com/chess-flair/membership_icons/crown_gold.svg',
'https://images.chesscomfiles.com/chess-flair/membership_icons/star_rainbow.svg',
'https://images.chesscomfiles.com/chess-flair/emoji/troll.svg',
'https://images.chesscomfiles.com/chess-flair/emoji/skull.svg',
'https://images.chesscomfiles.com/chess-flair/chess/brilliant.svg',
'https://images.chesscomfiles.com/chess-flair/chess/blunder.svg',
'https://images.chesscomfiles.com/chess-flair/chess/gg.svg',
'https://images.chesscomfiles.com/chess-flair/hosts/hikaru.svg',
'https://images.chesscomfiles.com/chess-flair/computer_chess/stockfish.svg'
];
const state={
enabled:true,
white:{name:'',rating:'',title:'',flairs:[],fairplay:false},
black:{name:'',rating:'',title:'',flairs:[],fairplay:false}
};
const original=new WeakMap();
let gui,visible=true;
const qs=(b,s)=>b.querySelector(s);
function getColor(b){
const p=b.closest('.player-component');
if(!p)return null;
if(p.querySelector('.clock-white'))return'white';
if(p.querySelector('.clock-black'))return'black';
}
function snapshot(b){
if(original.has(b))return;
original.set(b,{
name:qs(b,'.cc-user-username-component')?.textContent||'',
rating:qs(b,'[data-cy="user-tagline-rating"]')?.textContent||'',
avatar:b.closest('.player-component')?.querySelector('.cc-avatar-img')?.src||'',
country:[...(qs(b,'.country-flags-component')?.classList||[])]
});
}
function restoreAll(){
state.enabled=false;
document.querySelectorAll('.cc-user-block-component').forEach(b=>{
const o=original.get(b);
if(!o)return;
qs(b,'.cc-user-username-component').textContent=o.name;
qs(b,'[data-cy="user-tagline-rating"]').textContent=o.rating;
const av=b.closest('.player-component')?.querySelector('.cc-avatar-img');
if(av)av.src=o.avatar;
const c=qs(b,'.country-flags-component');
if(c)c.className=o.country.join(' ');
b.querySelectorAll('.custom-flair,.fair-play,.cc-user-title-component').forEach(e=>e.remove());
});
}
function ensureTitle(b,v){
let el=qs(b,'.cc-user-title-component');
if(!v){if(el)el.remove();return;}
if(!el){
el=document.createElement('div');
el.className='cc-user-title-component cc-text-x-small-bold';
el.style.color='white';
b.insertBefore(el,b.firstChild);
}
el.textContent=v;
}
function ensureFlairs(b,a){
b.querySelectorAll('.custom-flair').forEach(e=>e.remove());
a.forEach(src=>{
const i=document.createElement('img');
i.className='flair-rpc-component flair-rpc-small custom-flair';
i.width=16;i.height=16;i.src=src;
b.appendChild(i);
});
}
function ensureFairPlay(b,on){
let el=qs(b,'.fair-play');
if(on&&!el){
el=document.createElement('img');
el.className='flair-rpc-component flair-rpc-small fair-play';
el.src=FAIRPLAY_ICON;
el.width=16;el.height=16;
b.appendChild(el);
}
if(!on&&el)el.remove();
}
function apply(){
document.querySelectorAll('.cc-user-block-component').forEach(b=>{
const c=getColor(b);
if(!c)return;
snapshot(b);
if(!state.enabled)return;
const s=state[c];
ensureTitle(b,s.title);
ensureFlairs(b,s.flairs);
ensureFairPlay(b,s.fairplay);
if(s.name)qs(b,'.cc-user-username-component').textContent=s.name;
if(s.rating)qs(b,'[data-cy="user-tagline-rating"]').textContent=`(${s.rating})`;
});
}
new MutationObserver(()=>requestAnimationFrame(apply))
.observe(document.documentElement,{childList:true,subtree:true});
function drag(p,h){
let x=0,y=0,px=0,py=0;
h.onmousedown=e=>{
px=e.clientX;py=e.clientY;
document.onmousemove=ev=>{
x+=ev.clientX-px;y+=ev.clientY-py;
px=ev.clientX;py=ev.clientY;
p.style.transform=`translate(${x}px,${y}px)`;
};
document.onmouseup=()=>document.onmousemove=null;
};
}
function showTab(id){
gui.querySelectorAll('.tab').forEach(t=>t.style.display='none');
gui.querySelector('#'+id).style.display='block';
}
function renderFlairs(box,color){
box.innerHTML='';
box.style.display='grid';
box.style.gridTemplateColumns='repeat(6,1fr)';
box.style.gap='4px';
ALL_FLAIRS.forEach(src=>{
const b=document.createElement('button');
b.style='border:none;background:transparent;border-radius:4px';
const i=document.createElement('img');
i.src=src;i.width=24;i.height=24;
b.appendChild(i);
const u=()=>b.style.background=
state[color].flairs.includes(src)?'rgba(0,255,0,.35)':'transparent';
b.onclick=()=>{
const a=state[color].flairs;
const k=a.indexOf(src);
k>=0?a.splice(k,1):a.push(src);
state.enabled=true;apply();u();
};
u();box.appendChild(b);
});
}
function buildGUI(){
gui=document.createElement('div');
gui.style='position:fixed;top:80px;right:20px;background:#111;color:#fff;padding:10px;border-radius:8px;z-index:999999;width:260px;font-size:12px';
gui.innerHTML=`
<div id="hdr" style="cursor:move;font-weight:bold">Player Editor</div>
<div>
<button data-tab="names">Names</button>
<button data-tab="ratings">Ratings</button>
<button data-tab="titles">Titles</button>
<button data-tab="flairs">Flairs</button>
<button data-tab="fair">Fair Play</button>
</div>
<hr>
<div id="names" class="tab">
White name <input id="wn"><br>
Black name <input id="bn">
</div>
<div id="ratings" class="tab" style="display:none">
White rating <input id="wr"><br>
Black rating <input id="br">
</div>
<div id="titles" class="tab" style="display:none">
White title <input id="wt"><br>
Black title <input id="bt">
</div>
<div id="flairs" class="tab" style="display:none">
<b>White</b><div id="wf"></div><br>
<b>Black</b><div id="bf"></div>
</div>
<div id="fair" class="tab" style="display:none">
<label><input type="checkbox" id="wfp"> White</label>
<label><input type="checkbox" id="bfp"> Black</label>
</div>
<hr>
<button id="rst" style="width:100%;background:#400;color:#fff">Restore Original</button>
`;
document.body.appendChild(gui);
drag(gui,gui.querySelector('#hdr'));
gui.querySelectorAll('button[data-tab]').forEach(b=>{
b.onclick=()=>showTab(b.dataset.tab);
});
wn.oninput=e=>{state.white.name=e.target.value;state.enabled=true;apply();}
bn.oninput=e=>{state.black.name=e.target.value;state.enabled=true;apply();}
wr.oninput=e=>{state.white.rating=e.target.value;state.enabled=true;apply();}
br.oninput=e=>{state.black.rating=e.target.value;state.enabled=true;apply();}
wt.oninput=e=>{state.white.title=e.target.value;state.enabled=true;apply();}
bt.oninput=e=>{state.black.title=e.target.value;state.enabled=true;apply();}
wfp.onchange=e=>{state.white.fairplay=e.target.checked;state.enabled=true;apply();}
bfp.onchange=e=>{state.black.fairplay=e.target.checked;state.enabled=true;apply();}
rst.onclick=restoreAll;
renderFlairs(wf,'white');
renderFlairs(bf,'black');
}
window.addEventListener('load',buildGUI);
window.addEventListener('keydown',e=>{
if(e.key.toLowerCase()==='k'){
visible=!visible;
if(gui)gui.style.display=visible?'block':'none';
}
});
})();