// ==UserScript==
// @name Sore Foot Club - RR Mugger Detection
// @namespace dekleinekobini.rr-mugger-detect
// @version 1.0.4
// @author DeKleineKobini [2114440]
// @description Detect whether or not your RR opponent is a known mugger.
// @icon https://i.ibb.co/6vFksXW/sfc-logo.jpg
// @match https://www.torn.com/page.php?sid=russianRoulette*
// @connect api.no1irishstig.co.uk
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
(e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" .mugger-detect-message {color:var(--detect-color);background-color:var(--detect-background-color);text-align:center;padding:2px;font-size:16px;line-height:20px}@media only screen and (min-width: 768px) {.mugger-detect-message {color:var(--detect-color);background-color:var(--detect-background-color);text-align:center;padding:12px;font-size:18px;line-height:24px}.mugger-detect-message .title{line-height:30px}}.mugger-detect-message .title{line-height:25px}.mugger-detect-message .message{line-height:20px}.mugger-detect-message a{color:var(--detect-link-color, #fff);text-decoration:none}[class*=headerWrap___]{height:unset!important} ");
(function () {
'use strict';
async function findDelayed(node, findElement, timeout) {
return new Promise((resolve, reject) => {
const initialElement = findElement();
if (initialElement) {
resolve(initialElement);
return;
}
const observer = new MutationObserver(() => {
const element = findElement();
element && (clearTimeout(timeoutId), observer.disconnect(), resolve(element));
}), timeoutId = setTimeout(() => {
observer.disconnect(), reject("Failed to find the element within the acceptable timeout.");
}, timeout);
observer.observe(node, { childList: true, subtree: true });
});
}
async function findBySelectorDelayed(node, selector, timeout = 5e3) {
return findDelayed(node, () => node.querySelector(selector), timeout);
}
let currentPlayerId;
function getCurrentPlayerId() {
if (currentPlayerId)
return currentPlayerId;
const websocketElement = document.getElementById("websocketConnectionData");
if (websocketElement) {
const data = JSON.parse(websocketElement.textContent);
return currentPlayerId = parseInt(data.userID, 10), parseInt(data.userID, 10);
}
const userInputElement = document.getElementById("torn-user");
if (userInputElement) {
const data = JSON.parse(userInputElement.getAttribute("value"));
return currentPlayerId = data.id, data.id;
}
return console.warn("[Playground] Failed to get the current player's id."), null;
}
var MessageType = /* @__PURE__ */ ((MessageType2) => {
MessageType2["WARNING"] = "WARNING";
MessageType2["SAFE"] = "SAFE";
MessageType2["DISABLED"] = "DISABLED";
MessageType2["ERROR"] = "ERROR";
return MessageType2;
})(MessageType || {});
const SETTINGS = {
[
"WARNING"
/* WARNING */
]: {
title: "- WARNING -",
message: (_, opponent) => `<a href="https://tcy.sh/p/${opponent.id}" target="_blank">${opponent.name}</a> is a known mugger.`,
color: "#520a04",
backgroundColor: "#f77a71",
linkColor: "#fff"
},
[
"SAFE"
/* SAFE */
]: {
title: "- Probably Safe -",
message: (_, opponent) => `<a href="https://tcy.sh/p/${opponent.id}" target="_blank">${opponent.name}</a> is not a known mugger.`,
color: "#132c15",
backgroundColor: "#54b358",
linkColor: "#fff"
},
[
"DISABLED"
/* DISABLED */
]: {
title: "- Script Disabled -",
message: `You are not a <a href="https://discord.gg/58HgDHbxhd" target="_blank">Sore Foot Club</a> member.`,
color: "#3a006d",
backgroundColor: "#c582ff",
linkColor: "#fff"
},
[
"ERROR"
/* ERROR */
]: {
title: "- Error -",
message: "Something went wrong. Unable to fetch the mugger status.",
color: "#322500",
backgroundColor: "#cb9800"
}
};
var RussianRouletteSubpage = /* @__PURE__ */ ((RussianRouletteSubpage2) => {
RussianRouletteSubpage2["GAME"] = "GAME";
RussianRouletteSubpage2["OVERVIEW"] = "OVERVIEW";
RussianRouletteSubpage2["UNKNOWN"] = "UNKNOWN";
return RussianRouletteSubpage2;
})(RussianRouletteSubpage || {});
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
let currentPage = RussianRouletteSubpage.UNKNOWN;
const SELECTOR_APP_CONTAINER = "[class*='appContainer___']";
const SELECTOR_PLAYER_NAME = "[class*='titleWrap___'] [class*='title___']";
void onPageLoad();
function getSubpage() {
const { hash } = window.location;
switch (hash) {
case "#/game":
return RussianRouletteSubpage.GAME;
case "#/":
return RussianRouletteSubpage.OVERVIEW;
default:
return RussianRouletteSubpage.UNKNOWN;
}
}
async function onPageLoad() {
const rootElement = await findBySelectorDelayed(document, SELECTOR_APP_CONTAINER);
new MutationObserver(() => {
const subpage = getSubpage();
onSubpage(subpage);
}).observe(rootElement, { childList: true });
onSubpage(currentPage);
}
function onSubpage(subpage) {
if (currentPage === subpage) return;
currentPage = subpage;
onSubpageChange(subpage);
}
function onSubpageChange(subpage) {
if (subpage === RussianRouletteSubpage.GAME) {
findBySelectorDelayed(document, SELECTOR_PLAYER_NAME).then(handleGameLoading).catch((reason) => {
console.error("[RR Mugger Detect] Couldn't properly load the script.", reason);
});
}
}
function handleGameLoading() {
const waitingTitleElement = document.querySelector(`${SELECTOR_PLAYER_NAME}[href='/#']`);
if (!waitingTitleElement) {
handleGameLoaded();
return;
}
new MutationObserver((_, observer) => {
observer.disconnect();
handleGameLoaded();
}).observe(waitingTitleElement, { attributes: true, attributeFilter: ["href"] });
}
function handleGameLoaded() {
if (document.querySelector(".mugger-detect-message")) {
return;
}
const playerElements = Array.from(document.querySelectorAll(SELECTOR_PLAYER_NAME));
const playerIds = playerElements.map((element) => {
const idMatch = element.href.match(/XID=(\d+)/);
if (!idMatch) return null;
return {
id: parseInt(idMatch[1], 10),
name: element.textContent.trim()
};
}).filter((result) => !!result);
if (playerIds.length !== 2) return;
const userId = getCurrentPlayerId();
const user = playerIds.find(({ id }) => id === userId);
const opponent = playerIds.find(({ id }) => id !== userId);
if (!user || !opponent) return;
fetchMuggerDetection(user.id, opponent.id).then((detection) => showDetection(user, opponent, detection)).catch((reason) => {
showDetection(user, opponent, null);
console.error("[RR Mugger Detect] Failed to fetch the mugger status.", reason);
});
}
function removeDetectionMessages() {
Array.from(document.querySelectorAll(".mugger-detect-message")).forEach((message) => message.remove());
}
function showDetection(user, opponent, detection) {
removeDetectionMessages();
const wrapperElement = document.createElement("div");
wrapperElement.classList.add("mugger-detect-message");
let type;
if (!detection) type = MessageType.ERROR;
else if (!detection.is_member) type = MessageType.DISABLED;
else if (!detection.is_mugger) type = MessageType.SAFE;
else type = MessageType.WARNING;
const settings = SETTINGS[type];
wrapperElement.style.setProperty("--detect-color", settings.color);
wrapperElement.style.setProperty("--detect-background-color", settings.backgroundColor);
if (settings.linkColor) {
wrapperElement.style.setProperty("--detect-link-color", settings.linkColor);
}
const titleElement = document.createElement("span");
titleElement.classList.add("title");
titleElement.textContent = settings.title;
wrapperElement.appendChild(titleElement);
wrapperElement.appendChild(document.createElement("br"));
let message;
if (typeof settings.message === "function") {
message = settings.message(user, opponent);
} else {
message = settings.message;
}
const messageElement = document.createElement("span");
messageElement.classList.add("message");
messageElement.innerHTML = message;
wrapperElement.appendChild(messageElement);
const bodyElement = document.querySelector("[class*='headerWrap___'] [class*='bottomSection___']");
bodyElement.insertAdjacentElement("beforebegin", wrapperElement);
}
async function fetchMuggerDetection(userId, opponentId) {
return new Promise((resolve, reject) => {
_GM_xmlhttpRequest({
url: `https://api.no1irishstig.co.uk/sfc?player_id=${userId}&opponent_id=${opponentId}`,
onload: (response) => {
if (response.status === 200) {
resolve(JSON.parse(response.responseText));
} else {
reject(new Error(`Request failed with status: ${response.status} - ${response.statusText}`));
}
},
onerror: (response) => reject(new Error(`Request failed with status: ${response.status} - ${response.statusText} or error: ${response.error}`)),
ontimeout: () => reject(new Error("Request timed out")),
onabort: () => reject(new Error("Request aborted"))
});
});
}
})();