// ==UserScript==
// @name CS.RIN.RU Enhanced (External)
// @name:fr CS.RIN.RU Amélioré (Externe)
// @name:pt CS.RIN.RU Melhorado (Externo)
// @name:tr Genişletilmiş CS.RIN.RU (Ek)
// @namespace https://github.com/Altansar69/CS.RIN.RU-Enhanced-external
// @version 1.1.7
// @description Everything that concerns CS.RIN.RU - Steam Underground Community but does not act on the site.
// @description:fr Tout ce qui concerne CS.RIN.RU - Steam Underground Community mais qui n'agit pas sur le site.
// @description:pt W.I.P.
// @description:tr CS.RIN.RU sitesini ilgilendiren her şey - Steam Underground Topluluğu ancak sitede faaliyet göstermemektedir.
// @author CS.RIN.RU community
// @match *://store.steampowered.com/app/*
// @match *://steamdb.info/app/*
// @match *://www.pcgamingwiki.com/wiki/*
// @icon https://i.ibb.co/p1k6cq6/image.png
// @grant GM_xmlhttpRequest
// @homepageURL https://github.com/Altansar69/CS.RIN.RU-Enhanced-external
// @supportURL https://cs.rin.ru/forum/viewtopic.php?f=14&t=75717
// @updateURL https://raw.githubusercontent.com/Altansar69/CS.RIN.RU-Enhanced-external/master/CS-RIN-RU-ENHANCED-external.user.js
// @downloadURL https://raw.githubusercontent.com/Altansar69/CS.RIN.RU-Enhanced-external/master/CS-RIN-RU-ENHANCED-external.user.js
// ==/UserScript==
let defaultTag;
//defaultTag = "Cracked (by default)"
function addRinLinkToSteam() {
if (!document.location.origin.match("store.steampowered.com")) return;
const page = "steam"
const rinButton = addRinButton(page);
const dlcPage = document.querySelector("div.game_area_bubble.game_area_dlc_bubble");
const pageUrl = dlcPage?.querySelector("div > p > a")?.href ?? document.location.pathname;
const appName = document.querySelector("#appHubAppName").textContent;
const regex = /\/app\/(\d+)\//;
const appId = pageUrl.match(regex)[1];
const developer = encodeURIComponent(document.querySelector("#developers_list").firstElementChild.textContent);
updatePage(appId, appName, developer, rinButton, page);
}
addRinLinkToSteam();
function addRinLinkToSteamDB() {
if (!document.location.origin.match("steamdb.info")) return;
const page = "steamdb"
const rinButton = addRinButton(page);
const appName = document.querySelector("h1").textContent;
const firstEntry = document.querySelector('.span3');
const appId = firstEntry.nextElementSibling.textContent;
const developer = encodeURIComponent(firstEntry.parentElement
.nextElementSibling.nextElementSibling
.firstElementChild.nextElementSibling.textContent.replace(/\n/g, ""))
updatePage(appId, appName, developer, rinButton, page)
}
addRinLinkToSteamDB();
function addRinLinkToPCGW() {
if (!document.location.origin.match("pcgamingwiki.com")) return;
const page = "PCGW"
const rinButton = addRinButton(page);
const pageUrl = document.querySelector('.infobox-steamdb > a').getAttribute("href");
const appName = document.querySelector("h1").textContent;
const regex = /\/app\/(\d+)\//;
const appId = pageUrl.match(regex)[1];
const developer = encodeURIComponent(document.querySelector(".template-infobox-info").textContent);
updatePage(appId, appName, developer, rinButton, page);
}
addRinLinkToPCGW();
function addRinButton(page) {
const rinImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAEZ0FNQQAAsY58+1GTAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAZhSURBVHjaxJdfSFzZHcc/984d586YzNXR0VQTrQ0aRZQ0bjeNpFnShV1oSpsFCdsUoiklfWgL2YcstPjQh20gVQMl7UNokM0+1LZCStfVTaDbppsatFJ0SGLSEHWk8V+MjDPeGe/M/deH3JnO6MTautADA+d3/sz58P39OecKtm3z/2xShyDQdvFiycfd3b/WNa1JFEXT3gaVIAiCaZqSx+v96/mPPjrzXmvrevb8+7Yt/qC0tCuVSLTZtu2WPJ6/f6Oz8+3fXrgQz/mfdmBPXd1FT2Hhj1reegvTMLZN75Zlhj/4AEEUz81PTv4qe+4Lr75as/Do0fTRjg6UPXv445Ur+IqKvjX/8OFvchQA0DWtuaKxkTffffe/lvDRp5/yfGambuO4KEmK5PHwxoUL+INB/tbfTyoer9nkgj9XVdEmCIJpGMRVFdHlyllgWRa3b99menqaU6dO4ff7c+ZNw0B0ucR8cLZtsxaJ4N61C8swECVp0zop3TFMk3g8vgng6tWrXL9+HYCJiQkuXbqUC2CaiC6X/jKARCKBJx7HtCyEfEGYAXAUELIADMPg5s2bGfvOnTuEQiH279+fGdPicZ6Hw98r8Pm+m44r2zTNyNzc77Bt1hMJ3KqKZVm4BGFrgDVVRRD/rdLCwgLRaDRjl5aWIssyw8PDLC8vc+zYMVrfeYepUKioRFGwHZelYjHuXrnyfamgAFVVEdbWXgBsUDcXQNeJxWIZgKWlJbq7u9E0LUeRrq4uJicnEQSBvr4+vF4vhmFQWVmJ2+2moKCAN157DUEUsSwLVVWxYzFM08S9FUBK14lGowiiSCKRoKenh5WVlZzFq6urrK6uZvwbiUSIRCIArK+vc/ToUUpKSkioKrZlARCLxTBkGUPXKSwrq/tcff1lQLBM0/D4fBM5CqQB7t27t+nwrVpNTQ3Hjx+nurr6ReakUuyqrsYGNNtGj0bZXVvL0szMGcHlAkEAQUBdWXEUsG1bNwyisViG+j+WUElCURR8Ph8NDQ0oipJRB6D63DkA1jQNNI3giRMEc1OEhcHBFwCSx3N/7dmzE7MDA9imyXxW4OVroijS0tJCY2MjyWSSu3fvUlpaiiRJeddblkVhYSGyLPP06VO8Xi8ikEwmXwB8qa2tZ7Svr3htbOx1l9ttxDyeMtzu4pyy63Zz8OBBCgoK8Pv9BINBXC4X0WiUvXv3sry8jNfrzQtQXl7OgQMHcLvdzM/PMzc3x+7CQjRNQ6iqqsq3Zwx4JXtAlmWOHDmCIAgoikJrayv19fXcunWL+/fvI4riS1118uRJFEVBkiQWFxcJh8OExsdhfJx8mlUBTRsHA4FAJjZs26a4uJjm5maam5sJh8P09vby4MEDNl6ktbW1BAIBkskkoihi2zapVIpoNIonmcyrQBXwCPBuBAgEAhmAmpoaioqKqKys5OzZsxQXF6PrOjdu3GBwcJD5+XlKSkro7e1FlmVGR0dZW1tjYGCAx48fY+o6RTMzeQFkYBLYdHP5/X5kWd4kmSzLHDp0iNOnT3P48GEAZmdnefLkCeXl5TQ0NHD+/HnGxsYwTdOp1zZlc3N5AXyOAvvy+XR/NEppVnUULYvFQIDFoiJEUaSsrIyOjg7a29tJJpNcu3aN/v5+lpaWch80wL7l5bwx8NUNh5tApoaGFUVtrKi4qIhi3LIsW1PVs5GVlS+qTgrGYjE6OzsJhUI0NTUxNjbG1NTU5lQGdF3PC/D1rP5z4CTwc6DFoVn4MB6/DCQBvplKtaUMg3g856XF0NAQi4uLjIyMbJpLK5DKAyAAx7PsG8Aw8G1gFFCAWuCHQDeAS5Ie+wzjWFBVyUlEVWV8ehoPUJ6vmAE+y9oE0Aikn1cW8Aun/w/gDPAHx+5yAnUosG/f5RpNq6zS9TpRknS2+cq2bdtyy/LERoCvZPX/AtzLsj8E3gM6HfunwNDUyMhD4Gv/67M8W7V9QHuW/XGe9T1A+poMOim7o5ZOw1eAPwG7nfEE0AxM5dnziZMpSaB+dnY2vBMAEagA3s86HGDgJYenXQPgAQ7sVAEReNsJPoAZx++/3GLP77P6wc8C4BNAy/JxM3Bniz1TwILT/0wUCAE/Af4JPNvGHh1Ifwd+p7q62r9TAICfAZ8H+rcJ8GNHtQrgyzv6Ok7XBee33dbv7H0dWN4JwL8GAKNYpgagYXMZAAAAAElFTkSuQmCC";
const rinButton = document.createElement("a");
rinButton.className = "btnv6_blue_hoverfade btn_medium";
rinButton.style.marginLeft = "0.280em";
const spanElement = document.createElement("span");
spanElement.dataset.tooltipText = "View on CS.RIN.RU";
const imgElement = document.createElement("img");
imgElement.className = "ico16";
imgElement.setAttribute("src", rinImage);
spanElement.appendChild(imgElement);
// Add text for RIN button on SteamDB
if (page === "steamdb") {
imgElement.style.height = "16px";
imgElement.style.width = "16px";
spanElement.append(" CS.RIN.RU");
}
// Make sure the button has the same size as the other buttons
if (page === "PCGW") {
rinButton.className = "svg-icon template-infobox-icon";
}
rinButton.append(spanElement);
const siteSelectors = {
"steam": () => document.querySelector('.apphub_OtherSiteInfo'),
"steamdb": () => document.querySelectorAll('.app-links')[1],
"PCGW": () => document.querySelectorAll('.template-infobox-icons')[0]
};
const otherSiteInfo = (siteSelectors[page] || (() => null))();
otherSiteInfo.insertBefore(rinButton, otherSiteInfo.firstChild);
return rinButton;
}
function updatePage(appId, appName, developer, rinButton, page) {
getRinTopic(appId, appName, developer, function (url, tags) {
// Adds the cs.rin topic "href" attribute to the button
addRinUrl(rinButton, url);
addRinTags(tags, page);
});
}
function getRinTopic(appId, appName, developer, callback) {
const rinSearchUrl = `https://cs.rin.ru/forum/search.php?keywords=${appId}&fid%5B%5D=10&sr=topics&sf=firstpost`;
console.log(rinSearchUrl);
GM_xmlhttpRequest({
method: "GET", url: rinSearchUrl, onload: function (response) {
const doc = new DOMParser().parseFromString(response.responseText, "text/html");
const topicSelectors = doc.querySelectorAll(".titles:not(:first-child), .topictitle");
if (topicSelectors.length > 1) {
getRinTopicAdvanced(appId, appName, developer, callback);
} else {
processResponse(appName, response.responseText, callback, function () {
getRinTopic(appId, "", developer, callback); // Retry getRinTopic if search fails
});
}
}
});
}
function getRinTopicAdvanced(appId, appName, developer, callback) {
const rinSearchUrl = `https://cs.rin.ru/forum/search.php?keywords=${appId}+${developer}&fid%5B%5D=10&sr=topics&sf=firstpost`;
console.log(rinSearchUrl);
GM_xmlhttpRequest({
method: "GET", url: rinSearchUrl, onload: function (response) {
processResponse(appName, response.responseText, callback, function () {
getRinTopicAdvanced(appId, appName, developer, callback); // Retry getRinTopicAdvanced if search fails
});
}
});
}
let retryScheduled = false; // Flag to track if a retry is scheduled
function processResponse(appName, responseText, callback, retryFunction) {
if (retryScheduled) return; // If a retry is scheduled, don't do anything
const doc = new DOMParser().parseFromString(responseText, "text/html");
// Check if search was successful
const checkElement = doc.querySelector("#wrapcentre > form > table.tablebg > tbody > tr:nth-child(1) > th:nth-child(1)");
if (!checkElement) {
if (retryFunction) {
retryScheduled = true; // Set the flag to true to block further retries
setTimeout(() => {
retryScheduled = false; // Reset the flag when the retry function is called
retryFunction(); // Call the retryFunction, whichever function called this function
}, 200);
}
return;
}
// Get all topics
const topics = doc.querySelectorAll(".titles:not(:first-child), .topictitle");
let topicSelector = null;
for (let potentialTopic of topics) {
if (potentialTopic.textContent.includes(appName)) {
topicSelector = potentialTopic;
break;
}
}
// Default to first topic
if (!topicSelector) {
topicSelector = doc.querySelector(".titles:not(:first-child), .topictitle");
}
const rinURL = topicSelector ? topicSelector.getAttribute("href") : "posting.php?mode=post&f=10";
const redirectUrl = "https://cs.rin.ru/forum/" + rinURL.split("&hilit")[0];
const tags = topicSelector ? topicSelector.text.match(/(? document.getElementById("appHubAppName"),
"steamdb": () => document.querySelector('[itemprop="name"]'),
"PCGW": () => document.getElementsByClassName("article-title")[0]
};
// const titleElem = (tagFunctions[page] || (() => null))(tags);
const titleLocation = (titleLocations[page] || (() => null))();
const titleElem = appendRinTags(tags, titleLocation);
// Add colours to the tags
const bracketRegex = /[\[\]]/g;
let newContent = titleElem.textContent;
tags.forEach(tag => {
const color = colorize(tag, titleElem);
const tagSpan = `[${tag.replace(bracketRegex, "")}]`;
newContent = newContent.replace(tag, tagSpan);
});
titleElem.innerHTML = newContent;
}
function hexToRgb(hex) {
return [parseInt(hex.substring(0, 2), 16), parseInt(hex.substring(2, 4), 16), parseInt(hex.substring(4, 6), 16)];
}
function colorize(str, parentElem) {
let lstr = str.toLowerCase();
let hash = 0;
for (let i = 0; i < lstr.length; i++) {
hash = lstr.charCodeAt(i) + ((hash << 5) - hash);
}
let color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16);
let rgb = hexToRgb(color);
while (!getComputedStyle(parentElem).getPropertyValue("background-color").match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)) {
parentElem = parentElem.parentElement;
}
let bgColour = getComputedStyle(parentElem).getPropertyValue("background-color");
let matches = bgColour.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
const bgRgb = [parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3])];
while (Math.abs(rgb[0] + rgb[1] + rgb[2] - (bgRgb[0] + bgRgb[1] + bgRgb[2])) < 300) {
hash = (hash << 5) - hash;
color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16);
rgb = hexToRgb(color);
}
return '#' + color.padStart(6, '0');
}