Battlemetrics Toolkit - Desktop

Modifies the rcon panel for battlemetrics to help color code important events and details about players.

// ==UserScript==
// @name Battlemetrics Toolkit - Desktop
// @namespace https://www.battlemetrics.com/
// @version 9.8
// @description Modifies the rcon panel for battlemetrics to help color code important events and details about players.
// @author Cyslez
// @match https://www.battlemetrics.com/*
// @match https://www.battlemetrics.com
// @icon https://www.google.com/s2/favicons?sz=64&domain=battlemetrics.com
// @connect communitybanlist.com
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==

const version="v9.8",updateRate="150",colors={cTeamBluefor:"#e7a600",cTeamPac:"#34804d",cTeamOpfor:"#d95627",cTeamIndep:"#eaff00",cAdminName:"#00fff7",cModAction:"#ff3333",cAdminAction:"#37ff00",cTeamKilled:"#ffcc00",cLeftServer:"#ff33cc",cJoined:"#919191",cTracked:"#919191"},styles={zShift:".css-ym7lu8 {z-index: 2;}",zShiftTime:".css-z1s6qn {z-index: 3;}",zShiftTimeDate:".css-1jtoyp {z-index: 3;}",nobranding:"#poweredbyovh {width: 100vw; max-width: 100%}"},sets={teamKilled:new Set(["team killed"]),trackedTriggers:new Set(["Welcome to gMg!","We offer FREE WHITELIST","Auto-Warn | Squads containing MBTs","Auto-Warn | Vehicles requiring crewman","Seeding progress degrades","Auto-Warn | Piloting a Heli?","Auto-Warn | New pilots are NOT permitted","Auto Kick - Username must contain","Welcome to gMg! Be sure to join","You are earning FREE","There are over 88","Auto Kick - Your Steam Community","Streaming? Consider a minimum","Cats have 3 eyelids.","Pikachu directly","Sudowoodo is actually","Over 30 billion","If you feel a chill","Depending on who you ask, the","Gold and Silver were meant","The original games were","Ash's Japanese","The first pokemon games","The Kanto Region","Join a squad, you are unassigned and will be kicked","No more facts for you. Touch grass.","The normal body temperature of a cat","Cats sleep 70% of","A cat ran for mayor","A cat’s nose is","A cat called Stubbs","Cats only use their","There are 44","A cat’s sense of","Cats only sweat through","Seeding Reward:","Studies show that owning","Cats make around 100","During the Middle Ages","The oldest cat video","Alongside milk, it","Cats have a longer-term","The largest pedigree","The smallest pedigreed","Cats have","A cat usually has around","Cats have 1,000 times","Cats use their whiskers","The first cat in space","Cats can move their","Cats lick themselves","In the original Italian"," A cat’s jaw can’t move","Cat owners are 17%","Cat people are 11%","The largest litter of","Eighty-eight percent of","Only 24% of cats","Cats have about 130,000","Approximately 1/3 of cat","The first cartoon cat","A cat has 230 bones","Cats spend nearly 1/3 of","Rome has more homeless cats","A cat will always sit","A coughed up furball will","Owning a cat can reduce","Meow"," Evidence suggests domesticated","Auto-Kick | Steam Account must be at least 14 days"]),leftServer:new Set(["left the server"]),joinedServer:new Set(["joined the server"]),actionList:new Set(["was warned","was kicked","was banned","edited BattleMetrics Ban","added BattleMetrics Ban","deleted BattleMetrics Ban"]),adminList:new Set,teamBluefor:new Set(["Australian Defence Force","British Armed Forces","Canadian Armed Forces","United States Army","United States Marine Corps"]),teamPac:new Set(["People's Liberation Army","PLA Amphibious Ground Forces","PLA Navy Marine Corps"]),teamOpfor:new Set(["Russian Airborne Forces","Russian Ground Forces"]),teamIndep:new Set(["Western Private Military Contractors","Middle Eastern Alliance","Turkish Land Forces","Middle Eastern Insurgents","Irregular Militia Forces","Western Private Military Contractors"]),adminTerms:new Set(["admin","Admin","ADMIN","aDMIN","to the other team.",") was disbanded b","requested a list of squads.","set the next map to","changed the map to","requested the next map.",") forced","AdminRenameSquad","(Global)","executed Player Action Action","requested the current map.","restarted the match.","Squad disband - SL","was removed from their squad by Trigger.","requested layer list.","was removed from their squad by","added flag","removed flag"])};function isAdmin(e){return sets.adminList.has(e)}async function fetchAdminList(){try{const e=await fetch("https://raw.githubusercontent.com/Eddie0343/bm-userscript/refs/heads/main/adminList.json");if(!e.ok)throw new Error("Failed to fetch admin list");const t=await e.json();sets.adminList=new Set(t.admins),console.log("Admin list updated:",sets.adminList)}catch(e){console.error("Error fetching admin list:",e)}}async function runCode(){function e(e,t){let o=parseInt(e.slice(1),16),n=t<0?0:255,a=t<0?-1*t:t,r=o>>16,i=o>>8&255,s=255&o;return"#"+(16777216+65536*(Math.round((n-r)*a)+r)+256*(Math.round((n-i)*a)+i)+(Math.round((n-s)*a)+s)).toString(16).slice(1).toUpperCase()}console.log("Fetching admin list"),await fetchAdminList(),console.log("Running initial one-time code..."),Object.values({zShift:".css-ym7lu8 {z-index: 2;}",zShiftTime:".css-z1s6qn {z-index: 3;}",zShiftTimeDate:".css-1jtoyp {z-index: 3;}",nobranding:"#RCONLayout > nav > ul > li.css-1nxi32t > a {width: 100vw; max-width: 100%}"}).forEach((e=>GM_addStyle(e))),function(){const t=[{id:"NPFbutton",label:"N",url:"https://www.battlemetrics.com/rcon/servers/16084215",backgroundColor:"green"},{id:"Eventbutton",label:"E",url:"https://www.battlemetrics.com/rcon/servers/33871689",backgroundColor:"green"},{id:"SquadMaps",label:"M",url:"https://squadmaps.com/",backgroundColor:"orange"},{id:"version",label:version,url:"https://greasyfork.org/en/scripts/501133-battlemetrics-toolkit-desktop-auto-update?locale_override=1",backgroundColor:"black",fontSize:"6pt"}],o=Object.assign(document.createElement("div"),{style:"position: absolute; top: 10px; right: 5%; z-index: 99999;"});document.body.appendChild(o),t.forEach((({id:t,label:n,url:a,backgroundColor:r})=>{const i=Object.assign(document.createElement("input"),{type:"button",id:t,value:n,style:`width: 60px; height: 30px; background: ${r}; color: white; border: none; border-radius: 5px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); font-size: 12px; font-weight: bold; cursor: pointer; margin-right: 5px; padding: 2px; transition: background 0.3s, box-shadow 0.3s;`,onclick:()=>window.open(a,"_blank")});i.onmouseover=()=>{i.style.background=e(r,.1),i.style.boxShadow="0 6px 8px rgba(0, 0, 0, 0.15)"},i.onmouseout=()=>{i.style.background=r,i.style.boxShadow="0 4px 6px rgba(0, 0, 0, 0.1)"},o.appendChild(i)}))}();let t=!1;async function o(){if(await new Promise((e=>setTimeout(e,updateRate))),document.querySelector(".ReactVirtualized__Grid__innerScrollContainerddd")||document.querySelector(".css-b7r34x")){function o(){let t=document.createElement("div");t.id="note-menu-cont",t.style="position: absolute; top: 140px; left: 0; z-index: 99999;";let o=document.createElement("button");o.innerHTML="Note Menu",o.id="drop-button",o.style="width: 140px; height: 40px;background: #222222; color: white; border: none; border-radius: 5px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); font-size: 14px; font-weight: bold; cursor: pointer; padding: 2px; position: relative; transition: background 0.3s, box-shadow 0.3s;",o.onmouseover=()=>{o.style.background=e("#222222",.1),o.style.boxShadow="0 6px 8px rgba(0, 0, 0, 0.15)"},o.onmouseout=()=>{o.style.background="#222222",o.style.boxShadow="0 4px 6px rgba(0, 0, 0, 0.1)"};let n=document.createElement("div");n.id="first-dropdown-menu",n.style="display: none; position: absolute; top: 100%; left: 0; background: #222222; border: none; border-radius: 5px; z-index: 1000;";["Warn","Kick"].forEach((t=>{let o=document.createElement("button");o.innerHTML=t,o.style="width: 100%; background: #222222; color: white; border: none; border-radius: 5px; padding: 10px; text-align: left; cursor: pointer; transition: background 0.3s;",o.onmouseover=()=>{o.style.background=e("#222222",.1)},o.onmouseout=()=>{o.style.background="#222222"},o.addEventListener("click",(()=>{!function(t){a.innerHTML="";const o="Kick"===t?{Trolling:"Player was Kicked for Trolling",Teamkilling:"Player was Kicked for Teamkilling","Asset Wasting":"Player was Kicked for Asset Wasting","Squad Edging":"Player was Kicked for Squad Seeding","Armor Rules":"Player was Kicked for Breaking Armor Rules, leaving Main without a second Crewman","MBT Rules":"Player was Kicked for Breaking MBT Rules, having INF in a MBT squad","Heli Rules":"Player was Kicked for Breaking Heli Rules, Flying a Heli with a Squad with more than 4 people in the Squad",Maincamping:"Player was Kicked for Maincamping","Practice flying":"Player was Kicked for Not being able to Fly the Heli without Crashing","Seeding Rules":"Player was Kicked for Breaking Seeding Rules","Spawn Camping":"Player was Kicked for Spawn Camping on Jensens","No Mic SL":"Player was Kicked for having No Mic as SL"}:{Trolling:"Player was Warned for Trolling",Teamkilling:"Player was Warned for Teamkilling","Asset Wasting":"Player was Warned for Asset Wasting","Squad Edging":"Player was Warned for Squad Seeding","Armor Rules":"Player was Warned for Breaking Armor Rules, leaving Main without a second Crewman","MBT Rules":"Player was Warned for Breaking MBT Rules, having INF in a MBT squad","Heli Rules":"Player was Warned for Breaking Heli Rules, Flying a Heli with a Squad with more than 4 people in the Squad",Maincamping:"Player was Warned for Maincamping","Practice flying":"Player was Warned for Not being able to Fly the Heli without Crashing","Seeding Rules":"Player was Warned for Breaking Seeding Rules","Spawn Camping":"Player was Warned for Spawn Camping on Jensens","No Mic SL":"Player was Warned for having No Mic as SL"};Object.keys(o).forEach((t=>{let n=document.createElement("button");n.innerHTML=t,n.style="width: 100%; background: #222222; color: white; border: none; border-radius: 5px; padding: 10px; text-align: left; cursor: pointer; transition: background 0.3s;",n.onmouseover=()=>{n.style.background=e("#222222",.1)},n.onmouseout=()=>{n.style.background="#222222"},n.addEventListener("click",(()=>{!function(e){const t=document.querySelector('.tiptap.ProseMirror[contenteditable="true"]');t&&(t.innerHTML=`<p>${e}</p>`,t.focus(),window.getSelection().collapse(t,1))}(o[t]),a.style.display="none"})),a.appendChild(n)})),a.style.display="block"}(t),n.style.display="none"})),n.appendChild(o)}));let a=document.createElement("div");a.id="snd-drop-menu",a.style="display: none; position: absolute; top: 100%; left: 71%; background: #222222; border: none; border-radius: 5px; z-index: 1000",o.addEventListener("click",(()=>{let e="block"===n.style.display;n.style.display=e?"none":"block",a.style.display="none"})),document.addEventListener("click",(e=>{t.contains(e.target)||(n.style.display="none",a.style.display="none")})),t.appendChild(o),t.appendChild(n),t.appendChild(a),document.body.appendChild(t)}document.querySelectorAll(".css-z1s6qn").forEach((e=>{let t=e.getAttribute("datetime");if(t){let o=new Date(t);isNaN(o.getTime())||e.setAttribute("title",o.toLocaleString(void 0,{timeZoneName:"short"}))}})),function(){let e=document.querySelectorAll(".css-1ewh5td"),t=document.querySelectorAll(".css-fj458c"),o=document.querySelectorAll(".css-ym7lu8");function n(e,t,o){e.forEach((e=>{for(let n of t)if(e.textContent.includes(n)){e.style.color=o;break}}))}function a(e,t,o){e.forEach((function(e){t.forEach((function(t){new RegExp(`(\\b${t}\\b)`).test(e.textContent)&&(e.style.color=o)}))}))}n(o,sets.adminTerms,colors.cAdminAction),n(o,sets.joinedServer,colors.cJoined),n(o,sets.leftServer,colors.cLeftServer),n(o,sets.actionList,colors.cModAction),n(o,sets.teamBluefor,colors.cTeamBluefor),n(o,sets.teamPac,colors.cTeamPac),n(o,sets.teamOpfor,colors.cTeamOpfor),n(o,sets.teamIndep,colors.cTeamIndep),n(o,sets.teamKilled,colors.cTeamKilled),n(o,sets.trackedTriggers,colors.cTracked),a(t,sets.adminList,colors.cAdminName),a(e,sets.adminList,colors.cAdminName)}(),function(){function e(e,t){return document.querySelector(`[title*="${e}"]`)?.innerText||t}const n="https://communitybanlist.com/graphql";function a(e,t,o){const n=document.createElement("div");n.id="CBL-info",n.style="width: 140px; height: 105px; top: 190px; background: #222222; color: white; border: none; border-radius: 5px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); padding: 2px; position: absolute; text-align: center; z-index: 99998;";let a="white";e>=1&&e<=5?a="orange":e>5&&(a="red"),n.innerHTML=`<h4 style="font-size: 14px; font-weight: bold; color: ${a};">Reputation Points: ${e}</h4> <h4 style="font-size: 14px; font-weight: bold;">Active Bans: ${t}</h4> <h4 style="font-size: 14px; font-weight: bold;">Expired Bans: ${o}</h4>`,document.body.appendChild(n)}function r(e,t){document.getElementById(e)||t()}function i(e){const t=document.getElementById(e);t&&t.remove()}document.querySelector("#RCONPlayerPage")?(r("cpy-button",(function(){const t=document.createElement("button");t.id="cpy-button",t.textContent="Copy Player Info",t.classList.add("cpy-button-style"),document.body.appendChild(t),t.addEventListener("click",(()=>{const t=e("765","SteamID MISSING?"),o=e("0002",""),n=document.querySelector("#RCONPlayerPage > h1")?.innerText||"NAME MISSING?",a=document.querySelector(".collapse.in ul li a span")?.innerText||"";!function(e){const t=document.createElement("textarea");t.style.position="fixed",t.style.opacity="0",t.value=e,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t)}(`Name : ${n}\nSteam64 : ${t}\nEOS : ${o}\nCrime : ${a.split(" - ")[1]?.split(" | Expires")[0]?.trim()||""}\nTime : ${a.match(/\| Expires: ([^|]+)/)?.[1]?.trim()||""}\nEvidence/Note : `)})),function(){const e=document.createElement("style");e.innerHTML=".cpy-button-style {width: 140px;height: 40px;background: #222222;color: white;border: none;border-radius: 3px;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);font-size: 14px;font-weight: bold;cursor: pointer;padding: 2px;position: absolute;top: 90px;left: 0;z-index: 99999;transition: background 0.3s, box-shadow 0.3s;}.cpy-button-style:hover, .note-menu-style:hover {background: #383838;box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);}",document.head.appendChild(e)}()})),r("note-menu-cont",o),r("CBL-info",(async function(){if(t)return void console.log("Data fetching is already in progress. Skipping...");const o=e("765","SteamID MISSING?");if(o&&"SteamID MISSING?"!==o)try{t=!0,await async function(e){await new Promise((e=>setTimeout(e,500)));const t=1,o=3e3;let r=0,i=!1;for(;r<t&&!i;)try{r++,console.log(`Attempt ${r}: Fetching user data for SteamID ${e}`);const t=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:'query Search($id: String!) { steamUser(id: $id) { reputationPoints activeBans: bans(orderBy: "created", orderDirection: DESC, expired: false) { edges { node { id } } } expiredBans: bans(orderBy: "created", orderDirection: DESC, expired: true) { edges { node { id } } } } }',variables:{id:e}})});if(!t.ok)throw new Error(`HTTP error! Status: ${t.status} (${t.statusText})`);const o=await t.json();if(!o||!o.data||!o.data.steamUser)throw new Error("Invalid response format or user not found.");const s=o.data.steamUser,l=s.reputationPoints||"N/A";a(l,s.activeBans.edges.length||0,s.expiredBans.edges.length||0),console.log("Fetch successful!"),i=!0}catch(e){console.error(`Attempt ${r} failed: ${e.message}`,e),r<t?(console.log(`Retrying in ${o/1e3} seconds...`),await new Promise((e=>setTimeout(e,o)))):(console.error("Max retries reached. Fetch operation failed."),a("N/A","N/A",0),i=!0)}}(o)}catch(e){console.error("Error fetching Steam user data:",e)}finally{t=!1}else console.error("Invalid Steam ID")}))):(i("cpy-button"),i("note-menu-cont"),i("CBL-info")),document.querySelectorAll(".css-q39y9k").forEach((e=>{const t=e.title;if(t.startsWith("765")&&!e.getAttribute("data-processed")){const o=document.createElement("a");Array.from(e.attributes).forEach((e=>o.setAttribute(e.name,e.value))),o.href="https://communitybanlist.com/search/"+t,o.innerHTML=t,o.target="_blank",e.replaceWith(o),o.setAttribute("data-processed","true")}}))}(),function(){const e=[{phrase:"Change Layer",styles:{color:"red",fontStyle:"bold",textAlign:"center",fontSize:"100pt"}},{phrase:"Set Next Layer",styles:{color:"lime",fontStyle:"bold",textAlign:"center",fontSize:"50pt"}},{phrase:"Kick",styles:{color:"orange",fontStyle:"bold",textAlign:"center",fontSize:"48pt"}},{phrase:"Warn",styles:{color:"lime",fontStyle:"bold",textAlign:"center",fontSize:"24pt"}}],t=[{phrase:"Warn",styles:{color:"lime"}},{phrase:"Squad List",styles:{color:"gold"}},{phrase:"Kick",styles:{color:"orange"}},{phrase:"Ban",styles:{color:"red"}}],o=[{phrase:"Ban",styles:{color:"red"}},{phrase:"Next Layer",styles:{color:"lime",fontSize:"16pt"}},{phrase:"Change Layer",styles:{color:"red",fontStyle:"bold",fontSize:"8pt"}},{phrase:"Squad List",styles:{color:"gold",fontSize:"16pt"}}];function n(e,t){e.forEach((e=>{t.forEach((({phrase:t,styles:o})=>{e.textContent.includes(t)&&Object.assign(e.style,o)}))}))}setTimeout((()=>{n(document.querySelectorAll(".modal-title"),e),n(document.querySelectorAll(".css-f5o5h6 a, .css-f5o5h6 button"),t),n(document.querySelectorAll(".css-1ixz43s a, .css-1ixz43s button"),t),n(document.querySelectorAll(".css-yun63y a, .css-yun63y button"),o)}),300)}()}}setInterval((async()=>{await o()}),updateRate)}function observeDOMChanges(){new MutationObserver(((e,t)=>{for(const o of e)if("childList"===o.type||"attributes"===o.type){const e=document.querySelector(".ReactVirtualized__Grid__innerScrollContainer"),o=document.querySelector(".navbar-brand");if(e||o){console.log("Target element detected. Starting code..."),t.disconnect(),runCode();break}}})).observe(document.body,{childList:!0,subtree:!0,attributes:!0})}observeDOMChanges();