NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name CCO Gang Exp Ranking // @namespace Jefreesujit // @version 1.0.1 // @description A simple script for CCO gangs to rank players by EXP. // @author Jefreesujit // @match https://cybercodeonline.com/clan-players* // @icon https://www.google.com/s2/favicons?sz=64&domain=cybercodeonline.com // @grant none // @license MIT // ==/UserScript== /*jshint esversion: 8 */ (function() { 'use strict'; /* <--------------------------------- DOM ------------------------------> */ const renderPlayerRankings = (rankingData) => { const startDate = localStorage.getItem('rankStartDate'); const rankStatusText = `<span class="p-2"><b>Start Date:</b> ${startDate}</span><span class="p-2"> <b>Ranking Status:</b> In progress</span>`; console.table(rankingData); let rankingDomEl = `<div class="relative ion-activatable cursor-pointer w-full mb-2 border border-primary-dark pl-4 pr-4 p-1 items-center"> <div class="rank-item flex-row whitespace-nowrap flex-wrap text-primary-dark justify-between" style="width: 100%; color: rgb(204, 197, 65)"> <span style="width: 3em" class="flex-1 mr-4"> Rank </span> <span class="inline-flex flex-col justify-center rounded pl-2 pr-2 mr-4" style="width: 14em; cursor: pointer; height: 24px;"> <span class="flex flex-row flex-nowrap">Player Name </span> </span> <span style="width: 6em" class="flex-1 mr-4"> Start Exp </span> <span style="width: 6em" class="flex-1 mr-4"> Current Exp </span> <span style="width: 6em" class="flex-1 mr-4"> Exp Gain </span> </div> </div>`; rankingData.forEach((player, index) => { rankingDomEl += `<div class="relative ion-activatable cursor-pointer w-full mb-2 border border-primary-dark pl-4 pr-4 p-1 items-center"> <div class="rank-item flex-row whitespace-nowrap flex-wrap text-primary-dark justify-between" style="width: 100%"> <span style="width: 3em" class="flex-1 mr-4">${index + 1}</span> <span class="inline-flex flex-col justify-center rounded pl-2 pr-2 hover:bg-black-normal mr-4 " style="width: 14em; cursor: pointer; height: 24px; color: rgb(192, 228, 220);"> <span class="flex flex-row flex-nowrap">${player.PlayerName}</span> </span> <span style="width: 6em" class="flex-1 mr-4">${player.StartExp}</span> <span style="width: 6em" class="flex-1 mr-4">${player.CurrentExp}</span> <span style="width: 6em" class="flex-1 mr-4">${player.ExpGain}</span> </div> </div>`; }); document.querySelector('#rankingTableEl').insertAdjacentHTML('beforeend', rankingDomEl); document.querySelector('#rankingStatus').insertAdjacentHTML('beforeend', rankStatusText); } const renderContainerElement = (isRankActive) => { const gangName = document.querySelector('.absolute .neon-text').innerText; const gangTag = gangName.substring(gangName.indexOf('[')+1, gangName.indexOf(']')); const btnText = isRankActive ? 'End Ranking' : 'Start Ranking'; let ContentEl = ` <div id="contentEl" class="border items-center mt-2 mb-2"> <div id="contentTitle" style="font-weight: bold; font-size: 24px;" class="mb-2 mt-2"> ${gangTag} Gank Rankings </div> <div id="rankingStatus" style="display:block"></div> <div id="actionBtn" style="border: 1px solid rgb(204, 197, 65);padding: 5px 15px;color: rgb(204, 197, 65);" class="p-1 mt-4 mb-4 cursor-pointer">${btnText}</div> <div id="rankingTableEl" class="mb-2"></div> </div> `; document.querySelector('.p-4.min-h-full').childNodes[1].insertAdjacentHTML('beforeend', ContentEl); document.querySelector('#actionBtn').addEventListener("click", () => { handleActionClick(isRankActive, gangTag) }); } /* <--------------------------------- Utils ---------------------------------> */ const downloadTextFile = (text, name) => { const a = document.createElement('a'); const type = name.split(".").pop(); a.href = URL.createObjectURL(new Blob([text], { type: `text/${type === "txt" ? "plain" : type}` })); a.download = name; a.click(); }; // credits: https://stackoverflow.com/questions/8847766/how-to-convert-json-to-csv-format-and-store-in-a-variable const convertToCsv = (json) => { const replacer = function(key, value) { return value === null ? '' : value }; const fields = Object.keys(json[0]); let csv = json.map(function(row) { return fields.map(function(fieldName){ return JSON.stringify(row[fieldName], replacer) }).join(',') }) csv.unshift(fields.join(',')) // add header column csv = csv.join('\r\n'); return csv; } /* <--------------------------------- Ranking Script ----------------------------------> */ const calculatePlayerRankings = (startExpMap, currentExpMap) => { console.log('startExpMap, currentExpMap', startExpMap, currentExpMap); let mappedData = Object.keys(currentExpMap).map((player) => { const curExp = currentExpMap[player]; const stExp = startExpMap[player] || 0; const xpGain = curExp - stExp; return { PlayerName: player, StartExp: stExp, CurrentExp: curExp, ExpGain: xpGain }; }); const rankedData = mappedData.sort((a, b) => b.ExpGain - a.ExpGain); console.log('rankedData', rankedData); return rankedData; }; const getPlayerExpDetails = () => { const playerExpMap = {}; document.querySelectorAll('.mt-2 .relative.ion-activatable.items-center').forEach(subEl => { const nameEl = subEl.querySelector('.w-full .flex-nowrap'); const expEl = subEl.querySelectorAll('.flex-row.whitespace-nowrap .flex-1.mr-4')[3]; const nameValue = nameEl.innerText; const expValue = parseInt(expEl.innerText.replace(/[^0-9]/g,''), 10); playerExpMap[nameValue] = expValue; }); return playerExpMap; }; const handleStopTask = (gangTag) => { const playerExp = getPlayerExpDetails(); const date = new Date().toDateString(); const rankData = calculatePlayerRankings(playerExp, playerExp); renderPlayerRankings(rankData); const startDate = localStorage.getItem('rankStartDate'); const fileName = `${gangTag}-gang-contest-${startDate}-${date}.csv`; const csvData = convertToCsv(rankData); downloadTextFile(csvData, fileName); localStorage.setItem('isRankingActive', false); localStorage.setItem('playerStartExp', {}); document.querySelector('#actionBtn').innerText = 'Start Ranking'; document.querySelector('#rankingStatus').innerHTML = ''; document.querySelector('#rankingTableEl').innerHTML = ''; }; const handleStartTask = () => { const playerExp = getPlayerExpDetails(); const date = new Date().toDateString(); const rankData = calculatePlayerRankings(playerExp, playerExp); localStorage.setItem('isRankingActive', true); localStorage.setItem('rankStartDate', date); localStorage.setItem('playerStartExp', JSON.stringify(playerExp)); renderPlayerRankings(rankData); document.querySelector('#actionBtn').innerText = 'End Ranking'; }; const handleActionClick = (isActive, gangTag) => { const rankAttr = localStorage.getItem('isRankingActive'); const isRankActive = rankAttr && JSON.parse(rankAttr); if (!isRankActive) { console.log('Starting tracking gang progress'); handleStartTask(gangTag); } else { console.log('Ending tracking gang progress'); handleStopTask(gangTag); } } const checkIfHostGang = () => { const store = JSON.parse(localStorage.getItem('store')); const searchParams = new URLSearchParams(location.search); const currentGangId = searchParams.get('clanId'); const playerGangId = store.player.cid; console.log('gangId', currentGangId, playerGangId); return currentGangId === playerGangId; } // Initialise Script after 10 seconds of page load (wait until page loads fully) setTimeout(() => { window.console.clear = () => {}; window.console.log = (...args) => console.info.call(window, ...args); console.log('CCO Gang exp ranking script Initialized'); // check if the host belongs to the gang, if so start the script. if (checkIfHostGang()) { const rankAttr = localStorage.getItem('isRankingActive'); const isRankActive = rankAttr && JSON.parse(rankAttr); console.log('Ranking Status', isRankActive); renderContainerElement(isRankActive); if (isRankActive) { const startExp = JSON.parse(localStorage.getItem('playerStartExp')); const currentExp = getPlayerExpDetails(); const rankData = calculatePlayerRankings(startExp, currentExp); renderPlayerRankings(rankData); } } }, 10000); })();