- // ==UserScript==
- // @name TORN: Display Crime Chain
- // @namespace http://torn.city.com.dot.com.com
- // @version 1.0.0
- // @description Calculates and displays your current crime chain
- // @author Ironhydedragon[2428902]
- // @match https://www.torn.com/loader.php?sid=crimes*
- // @license MIT
- // @run-at document-end
- // ==/UserScript==
-
- let crimeChain = 0;
-
- const redFlame = '#e64d1a';
-
- const PDA_API_KEY = '###PDA-APIKEY###';
- function isPDA() {
- const PDATestRegex = !/^(###).+(###)$/.test(PDA_API_KEY);
- console.log('REGEX', PDATestRegex); // TEST
- return PDATestRegex;
- }
-
- function setApiKey(apiKey) {
- localStorage.setItem('ihdScriptApiKey', apiKey);
- }
- function getApiKey() {
- return localStorage.getItem('ihdScriptApiKey');
- }
-
- const stylesheet = `
- <style>
- #crime-chain {
- cursor: unset;
- }
-
- #api-form.header-wrapper-top {
- display: flex;
- }
- #api-form.header-wrapper-top .container {
- display: flex;
- justify-content: start;
- align-items: center;
- padding-left: 20px;
- }
-
- #api-form.header-wrapper-top h2 {
- display: block;
- text-align: center;
- margin: 0;
- width: 172px;
- }
-
- #api-form.header-wrapper-top input {
- background: linear-gradient(0deg, #111, #000);
- border-radius: 5px;
- box-shadow: 0 1px 0 hsla(0, 0%, 100%, 0.102);
- box-sizing: border-box;
- color: #9f9f9f;
- display: inline;
- font-weight: 400;
- height: 24px;
- width: clamp(170px, 50%, 250px);
- margin: 0 0 0 21px;
- outline: none;
- padding: 0 10px 0 10px;
-
- font-size: 12px;
- font-style: italic;
- vertical-align: middle;
- border: 0;
- text-shadow: none;
- z-index: 100;
- }
- #api-form.header-wrapper-top a {
- margin: 0 8px;
- }
-
- @media screen and (max-width: 1000px) {
- #api-form.header-wrapper-top h2 {
- width: 148px;
- }
- #api-form.header-wrapper-top input {
- margin-left: 10px;
- }
- }
- @media screen and (max-width: 784px) {
- #api-form.header-wrapper-top h2 {
- font-size: 16px;
- width: 80px;
- }
-
- #crime-chain .linkTitle____NPyM {
- display: block;
- }
- #body.r .linksContainer___LiOTN {
- margin-left: 8px;
- }
- }
-
- </style>`;
- function renderStylesheet() {
- document.head.insertAdjacentHTML('beforeend', stylesheet);
- }
-
- function renderApiForm() {
- const topHeaderBannerEl = document.querySelector('#topHeaderBanner');
- const apiFormHTML = `
- <div id="api-form" class="header-wrapper-top">
- <div class="container clear-fix">
- <h2>API Key</h2>
- <input
- id="api-form__input"
- type="text"
- placeholder="Enter a full-acces API key..."
- />
- <a href="#" id="api-form__submit" type="btn" disabled><span class="link-text">Submit</span</button>
- </div>
- </div>`;
-
- topHeaderBannerEl.insertAdjacentHTML('afterbegin', apiFormHTML);
- }
- function dismountApiForm() {
- document.querySelector('#api-form').remove();
- }
-
- function renderCrimeChainHTML() {
- console.log('🖼️ RENDERING CHAIN HTML'); // TEST
- const crimeChainHTML = `
- <div class="linksContainer___LiOTN">
- <span aria-labelledby="crime-chain" class="linkContainer___X16y4 inRow___VfDnd greyLineV___up8VP link-container-CrimesHub" target="_self" id="crime-chain"
- ><span class="iconContainer___D5z6F linkIconContainer___Ep0LO"
- ><svg fill="#777777" height="17px" width="16px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 31.891 31.891" xml:space="preserve">
- <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
- <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
- <g id="SVGRepo_iconCarrier">
- <g>
- <path
- d="M30.543,5.74l-4.078-4.035c-1.805-1.777-4.736-1.789-6.545-0.02l-4.525,4.414c-1.812,1.768-1.82,4.648-0.02,6.424 l2.586-2.484c-0.262-0.791,0.061-1.697,0.701-2.324l2.879-2.807c0.912-0.885,2.375-0.881,3.275,0.01l2.449,2.42 c0.9,0.891,0.896,2.326-0.01,3.213l-2.879,2.809c-0.609,0.594-1.609,0.92-2.385,0.711l-2.533,2.486 c1.803,1.781,4.732,1.789,6.545,0.02l4.52-4.41C32.34,10.396,32.346,7.519,30.543,5.74z"
- ></path>
- <path
- d="M13.975,21.894c0.215,0.773-0.129,1.773-0.752,2.381l-2.689,2.627c-0.922,0.9-2.414,0.895-3.332-0.012l-2.498-2.461 c-0.916-0.906-0.91-2.379,0.012-3.275l2.691-2.627c0.656-0.637,1.598-0.961,2.42-0.689l2.594-2.57 c-1.836-1.811-4.824-1.82-6.668-0.02l-4.363,4.26c-1.846,1.803-1.855,4.734-0.02,6.549l4.154,4.107 c1.834,1.809,4.82,1.818,6.668,0.018l4.363-4.26c1.844-1.805,1.852-4.734,0.02-6.547L13.975,21.894z"
- ></path>
- <path d="M11.139,20.722c0.611,0.617,1.611,0.623,2.234,0.008l7.455-7.416c0.621-0.617,0.625-1.615,0.008-2.234 c-0.613-0.615-1.611-0.619-2.23-0.006l-7.457,7.414C10.529,19.103,10.525,20.101,11.139,20.722z"></path>
- </g>
- </g></svg></span
- ><span class="linkTitle____NPyM"><span id="crime-chain__current">###</span></span></span
- >
- </div>
- `;
- const titleContainerEl = document.querySelector('.crimes-app .titleContainer___QrlWP');
- titleContainerEl.insertAdjacentHTML('afterend', crimeChainHTML);
- }
-
- function renderCrimeChainCurrent() {
- console.log('⛓️', crimeChain); // TEST
- document.querySelector('#crime-chain__current').textContent = Math.floor(crimeChain);
- }
-
- async function fetchCrimes(toTimestamp) {
- const response = await fetch(`https://api.torn.com/user/?selections=log&cat=136${toTimestamp ? '&to=' + toTimestamp : ''}&key=${getApiKey()}`);
- const data = await response.json();
- return data;
- }
-
- async function calcCrimeChain() {
- try {
- let dataCollector = [];
-
- const initialData = await fetchCrimes();
- function dataCollectorUnshifter(fetchData) {
- for (const log in fetchData.log) {
- if (fetchData.log[log].title.match(/Crime (success|fail|critical fail)/gi)) {
- dataCollector.unshift(fetchData.log[log]);
- }
- }
- }
- dataCollectorUnshifter(initialData);
- while (dataCollector.filter((log) => log.title.match(/Crime critical fail/i)).length < 1) {
- const data = await fetchCrimes(dataCollector[0].timestamp - 1);
- dataCollectorUnshifter(data);
- }
-
- for (const d of dataCollector) {
- if (d.title.match(/Crime success/i)) {
- crimeChain++;
- }
- if (d.title.match(/Crime fail/i)) {
- crimeChain = crimeChain ? crimeChain / 2 : 0;
- }
- if (d.title.match(/Crime critical fail/i)) {
- crimeChain = 0;
- }
- }
- } catch (error) {
- console.error(error); // TEST
- }
- }
-
- //// Callbacks
- function submitFormCallback() {
- const inputEl = document.querySelector('#api-form__input');
- const submitBtnEl = document.querySelector('#api-form__submit');
-
- const apiKey = inputEl.value;
- if (apiKey.length !== 16) {
- inputEl.style.border = `2px solid ${redFlame}`;
- submitBtnEl.disabled = true;
- return;
- }
- setApiKey(apiKey);
- dismountApiForm();
- window.location.reload();
- }
-
- function inputValidatorCallback(event) {
- const inputEl = document.querySelector('#api-form__input');
- const submitBtnEl = document.querySelector('#api-form__submit');
- if (event.target.value.length === 16) {
- submitBtnEl.disabled = false;
- inputEl.style.border = '1px solid #444';
- }
- if (event.target.value.length !== 16) {
- submitBtnEl.disabled = true;
- }
- }
-
- function updateCrimeCallback(mutationList) {
- for (const mutation of mutationList) {
- if (mutation.addedNodes.length > 0 && mutation.addedNodes[0].classList && [...mutation.addedNodes[0].classList].join(' ').match(/crimes-outcome-/)) {
- const outcome = [...mutation.addedNodes[0].classList].join(' ').match(/(?<=crimes-outcome-)\w+/gi)[0];
- console.log('👀', outcome); // TEST
-
- if (outcome === 'success') {
- crimeChain++;
- }
- if (outcome === 'failure') {
- crimeChain = crimeChain / 2;
- }
- if (outcome === 'criticalFailure') {
- crimeChain = crimeChain / 2;
- }
-
- renderCrimeChainCurrent();
- }
- }
- }
-
- //////// CONTROLLERS ////////
- function apiKeyFormController() {
- renderApiForm();
-
- // set event liseners
- //// Event listeners
- document.querySelector('#api-form__submit').addEventListener('click', submitFormCallback);
- document.querySelector('#api-form__input').addEventListener('input', inputValidatorCallback);
- document.querySelector('#api-form__input').addEventListener('keyup', (event) => {
- if (event.key === 'Enter' || event.keyCode === 13) {
- submitFormCallback();
- }
- });
- return;
- }
-
- function initController() {
- renderStylesheet();
-
- if (isPDA()) {
- console.log('🌟 IS PDA!!!!!', PDA_API_KEY); // TEST
- setApiKey(PDA_API_KEY);
- }
-
- if (!getApiKey()) {
- console.log('noAPIKey found', getApiKey()); // TEST
- apiKeyFormController();
- return;
- }
-
- renderCrimeChainHTML();
- }
-
- async function loadController() {
- await calcCrimeChain();
- renderCrimeChainCurrent();
- }
-
- function updateCrimeChainController() {
- const updateCrimeObserver = new MutationObserver(updateCrimeCallback);
- updateCrimeObserver.observe(document, { attributes: false, childList: true, subtree: true });
- }
-
- //// Promise race conditions
- // necessary as PDA scripts are inject after window.onload
- const PDAPromise = new Promise((res, rej) => {
- if (document.readyState === 'complete') res();
- });
-
- const browserPromise = new Promise((res, rej) => {
- window.addEventListener('load', () => res());
- });
-
- (async () => {
- try {
- console.log('⛓️ Crime chain script ON!'); // TEST
- await Promise.race([PDAPromise, browserPromise]);
- initController();
- if (getApiKey()) {
- await loadController();
- updateCrimeChainController();
- }
- } catch (error) {
- console.error(error); // TEST
- }
- })();